blob: bb12c492595641d393d49bdba7861729fb4e6ec9 [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include <Utils.hpp>
18#include <boost/container/flat_map.hpp>
James Feist8f2710a2018-05-09 17:18:55 -070019#include <boost/algorithm/string/predicate.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080020#include <ctime>
James Feistb49ffc32018-05-02 11:10:43 -070021#include <chrono>
22#include <thread>
James Feist9eb0b582018-04-27 12:15:46 -070023#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080025#include <fcntl.h>
26#include <fstream>
27#include <future>
James Feistb5320a72018-01-24 12:28:12 -080028#include <linux/i2c-dev-user.h>
James Feist3cb5fec2018-01-23 14:41:51 -080029#include <iostream>
30#include <sys/ioctl.h>
James Feist3f8a2782018-02-12 09:24:42 -080031#include <regex>
James Feist4131aea2018-03-09 09:47:30 -080032#include <sys/inotify.h>
James Feist2a9d6db2018-04-27 15:48:28 -070033#include <xyz/openbmc_project/Common/error.hpp>
James Feistb49ffc32018-05-02 11:10:43 -070034#include <errno.h>
James Feist3cb5fec2018-01-23 14:41:51 -080035
36namespace fs = std::experimental::filesystem;
37static constexpr bool DEBUG = false;
38static size_t UNKNOWN_BUS_OBJECT_COUNT = 0;
James Feistb49ffc32018-05-02 11:10:43 -070039constexpr size_t MAX_FRU_SIZE = 512;
40constexpr size_t MAX_EEPROM_PAGE_INDEX = 255;
James Feist26c27ad2018-07-25 15:09:40 -070041constexpr size_t busTimeoutSeconds = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080042
43const static constexpr char *BASEBOARD_FRU_LOCATION =
44 "/etc/fru/baseboard.fru.bin";
45
James Feist4131aea2018-03-09 09:47:30 -080046const static constexpr char *I2C_DEV_LOCATION = "/dev";
47
James Feist3cb5fec2018-01-23 14:41:51 -080048static constexpr std::array<const char *, 5> FRU_AREAS = {
49 "INTERNAL", "CHASSIS", "BOARD", "PRODUCT", "MULTIRECORD"};
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -070050const static std::regex NON_ASCII_REGEX("[^\x01-\x7f]");
James Feist3cb5fec2018-01-23 14:41:51 -080051using DeviceMap = boost::container::flat_map<int, std::vector<char>>;
52using BusMap = boost::container::flat_map<int, std::shared_ptr<DeviceMap>>;
53
James Feist6ebf9de2018-05-15 15:01:17 -070054struct FindDevicesWithCallback;
55
James Feistc95cb142018-02-26 10:41:42 -080056static bool isMuxBus(size_t bus)
57{
58 return is_symlink(std::experimental::filesystem::path(
59 "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device"));
60}
61
James Feist3cb5fec2018-01-23 14:41:51 -080062int get_bus_frus(int file, int first, int last, int bus,
63 std::shared_ptr<DeviceMap> devices)
64{
James Feist3cb5fec2018-01-23 14:41:51 -080065
James Feist26c27ad2018-07-25 15:09:40 -070066 std::future<int> future = std::async(std::launch::async, [&]() {
67 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> block_data;
68 for (int ii = first; ii <= last; ii++)
James Feist3cb5fec2018-01-23 14:41:51 -080069 {
James Feist3cb5fec2018-01-23 14:41:51 -080070
James Feist26c27ad2018-07-25 15:09:40 -070071 // Set slave address
72 if (ioctl(file, I2C_SLAVE_FORCE, ii) < 0)
James Feist3cb5fec2018-01-23 14:41:51 -080073 {
James Feist26c27ad2018-07-25 15:09:40 -070074 std::cerr << "device at bus " << bus << "register " << ii
75 << "busy\n";
76 continue;
77 }
78 // probe
79 else if (i2c_smbus_read_byte(file) < 0)
80 {
81 continue;
82 }
James Feist3cb5fec2018-01-23 14:41:51 -080083
James Feist26c27ad2018-07-25 15:09:40 -070084 if (DEBUG)
85 {
86 std::cout << "something at bus " << bus << "addr " << ii
87 << "\n";
88 }
89 if (i2c_smbus_read_i2c_block_data(file, 0x0, 0x8,
90 block_data.data()) < 0)
91 {
92 std::cerr << "failed to read bus " << bus << " address " << ii
93 << "\n";
94 continue;
95 }
96 size_t sum = 0;
97 for (int jj = 0; jj < 7; jj++)
98 {
99 sum += block_data[jj];
100 }
101 sum = (256 - sum) & 0xFF;
102
103 // check the header checksum
104 if (sum == block_data[7])
105 {
106 std::vector<char> device;
107 device.insert(device.end(), block_data.begin(),
108 block_data.begin() + 8);
109
110 for (int jj = 1; jj <= FRU_AREAS.size(); jj++)
111 {
112 auto area_offset = device[jj];
113 if (area_offset != 0)
James Feist3cb5fec2018-01-23 14:41:51 -0800114 {
James Feist26c27ad2018-07-25 15:09:40 -0700115 area_offset *= 8;
James Feist3cb5fec2018-01-23 14:41:51 -0800116 if (i2c_smbus_read_i2c_block_data(
James Feist26c27ad2018-07-25 15:09:40 -0700117 file, area_offset, 0x8, block_data.data()) < 0)
James Feist3cb5fec2018-01-23 14:41:51 -0800118 {
119 std::cerr << "failed to read bus " << bus
120 << " address " << ii << "\n";
121 return -1;
122 }
James Feist26c27ad2018-07-25 15:09:40 -0700123 int length = block_data[1] * 8;
James Feist3cb5fec2018-01-23 14:41:51 -0800124 device.insert(device.end(), block_data.begin(),
James Feist26c27ad2018-07-25 15:09:40 -0700125 block_data.begin() + 8);
126 length -= 8;
127 area_offset += 8;
128
129 while (length > 0)
130 {
131 auto to_get = std::min(0x20, length);
132 if (i2c_smbus_read_i2c_block_data(
133 file, area_offset, to_get,
134 block_data.data()) < 0)
135 {
136 std::cerr << "failed to read bus " << bus
137 << " address " << ii << "\n";
138 return -1;
139 }
140 device.insert(device.end(), block_data.begin(),
141 block_data.begin() + to_get);
142 area_offset += to_get;
143 length -= to_get;
144 }
James Feist3cb5fec2018-01-23 14:41:51 -0800145 }
146 }
James Feist26c27ad2018-07-25 15:09:40 -0700147 devices->emplace(ii, device);
James Feist3cb5fec2018-01-23 14:41:51 -0800148 }
James Feist3cb5fec2018-01-23 14:41:51 -0800149 }
James Feist26c27ad2018-07-25 15:09:40 -0700150 return 1;
151 });
152 std::future_status status =
153 future.wait_for(std::chrono::seconds(busTimeoutSeconds));
154 if (status == std::future_status::timeout)
155 {
156 std::cerr << "Error reading bus " << bus << "\n";
157 return -1;
James Feist3cb5fec2018-01-23 14:41:51 -0800158 }
159
James Feist26c27ad2018-07-25 15:09:40 -0700160 return future.get();
James Feist3cb5fec2018-01-23 14:41:51 -0800161}
162
James Feist6ebf9de2018-05-15 15:01:17 -0700163static void FindI2CDevices(const std::vector<fs::path> &i2cBuses,
164 std::shared_ptr<FindDevicesWithCallback> context,
165 boost::asio::io_service &io, BusMap &busMap)
James Feist3cb5fec2018-01-23 14:41:51 -0800166{
James Feist3cb5fec2018-01-23 14:41:51 -0800167 for (auto &i2cBus : i2cBuses)
168 {
169 auto busnum = i2cBus.string();
170 auto lastDash = busnum.rfind(std::string("-"));
171 // delete everything before dash inclusive
172 if (lastDash != std::string::npos)
173 {
174 busnum.erase(0, lastDash + 1);
175 }
176 auto bus = std::stoi(busnum);
177
178 auto file = open(i2cBus.c_str(), O_RDWR);
179 if (file < 0)
180 {
181 std::cerr << "unable to open i2c device " << i2cBus.string()
182 << "\n";
183 continue;
184 }
185 unsigned long funcs = 0;
186
187 if (ioctl(file, I2C_FUNCS, &funcs) < 0)
188 {
189 std::cerr
190 << "Error: Could not get the adapter functionality matrix bus"
191 << bus << "\n";
192 continue;
193 }
194 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE) ||
195 !(I2C_FUNC_SMBUS_READ_I2C_BLOCK))
196 {
197 std::cerr << "Error: Can't use SMBus Receive Byte command bus "
198 << bus << "\n";
199 continue;
200 }
201 auto &device = busMap[bus];
202 device = std::make_shared<DeviceMap>();
203
James Feistc95cb142018-02-26 10:41:42 -0800204 // don't scan muxed buses async as don't want to confuse the mux
205 if (isMuxBus(bus))
206 {
207 get_bus_frus(file, 0x03, 0x77, bus, device);
208 close(file);
209 }
210 else
211 {
James Feist6ebf9de2018-05-15 15:01:17 -0700212 io.post([file, device, bus, context] {
213 // i2cdetect by default uses the range 0x03 to 0x77, as
214 // this is
215 // what we
216 // have tested with, use this range. Could be changed in
217 // future.
218 get_bus_frus(file, 0x03, 0x77, bus, device);
219 close(file);
220 });
James Feistc95cb142018-02-26 10:41:42 -0800221 }
James Feist3cb5fec2018-01-23 14:41:51 -0800222 }
James Feist3cb5fec2018-01-23 14:41:51 -0800223}
224
James Feist6ebf9de2018-05-15 15:01:17 -0700225// this class allows an async response after all i2c devices are discovered
226struct FindDevicesWithCallback
227 : std::enable_shared_from_this<FindDevicesWithCallback>
228{
229 FindDevicesWithCallback(const std::vector<fs::path> &i2cBuses,
230 boost::asio::io_service &io, BusMap &busMap,
231 std::function<void(void)> &&callback) :
232 _i2cBuses(i2cBuses),
233 _io(io), _busMap(busMap), _callback(std::move(callback))
234 {
235 }
236 ~FindDevicesWithCallback()
237 {
238 _callback();
239 }
240 void run()
241 {
242 FindI2CDevices(_i2cBuses, shared_from_this(), _io, _busMap);
243 }
244
245 const std::vector<fs::path> &_i2cBuses;
246 boost::asio::io_service &_io;
247 BusMap &_busMap;
248 std::function<void(void)> _callback;
249};
250
James Feist3cb5fec2018-01-23 14:41:51 -0800251static const std::tm intelEpoch(void)
252{
253 std::tm val = {0};
254 val.tm_year = 1996 - 1900;
255 return val;
256}
257
258bool formatFru(const std::vector<char> &fruBytes,
259 boost::container::flat_map<std::string, std::string> &result)
260{
261 static const std::vector<const char *> CHASSIS_FRU_AREAS = {
262 "PART_NUMBER", "SERIAL_NUMBER", "CHASSIS_INFO_AM1", "CHASSIS_INFO_AM2"};
263
264 static const std::vector<const char *> BOARD_FRU_AREAS = {
265 "MANUFACTURER", "PRODUCT_NAME", "SERIAL_NUMBER", "PART_NUMBER",
266 "VERSION_ID"};
267
268 static const std::vector<const char *> PRODUCT_FRU_AREAS = {
269 "MANUFACTURER", "PRODUCT_NAME", "PART_NUMBER",
270 "PRODUCT_VERSION", "PRODUCT_SERIAL_NUMBER", "ASSET_TAG"};
271
272 size_t sum = 0;
273
274 if (fruBytes.size() < 8)
275 {
276 return false;
277 }
278 std::vector<char>::const_iterator fruAreaOffsetField = fruBytes.begin();
James Feist9eb0b582018-04-27 12:15:46 -0700279 result["Common_Format_Version"] =
James Feist3cb5fec2018-01-23 14:41:51 -0800280 std::to_string(static_cast<int>(*fruAreaOffsetField));
281
282 const std::vector<const char *> *fieldData;
283
284 for (auto &area : FRU_AREAS)
285 {
286 fruAreaOffsetField++;
287 if (fruAreaOffsetField >= fruBytes.end())
288 {
289 return false;
290 }
291 size_t offset = (*fruAreaOffsetField) * 8;
292
293 if (offset > 1)
294 {
295 // +2 to skip format and length
296 std::vector<char>::const_iterator fruBytesIter =
297 fruBytes.begin() + offset + 2;
298
299 if (fruBytesIter >= fruBytes.end())
300 {
301 return false;
302 }
303
304 if (area == "CHASSIS")
305 {
306 result["CHASSIS_TYPE"] =
307 std::to_string(static_cast<int>(*fruBytesIter));
308 fruBytesIter += 1;
309 fieldData = &CHASSIS_FRU_AREAS;
310 }
311 else if (area == "BOARD")
312 {
313 result["BOARD_LANGUAGE_CODE"] =
314 std::to_string(static_cast<int>(*fruBytesIter));
315 fruBytesIter += 1;
316 if (fruBytesIter >= fruBytes.end())
317 {
318 return false;
319 }
320
321 unsigned int minutes = *fruBytesIter |
322 *(fruBytesIter + 1) << 8 |
323 *(fruBytesIter + 2) << 16;
324 std::tm fruTime = intelEpoch();
325 time_t timeValue = mktime(&fruTime);
326 timeValue += minutes * 60;
327 fruTime = *gmtime(&timeValue);
328
329 result["BOARD_MANUFACTURE_DATE"] = asctime(&fruTime);
330 result["BOARD_MANUFACTURE_DATE"]
331 .pop_back(); // remove trailing null
332 fruBytesIter += 3;
333 fieldData = &BOARD_FRU_AREAS;
334 }
335 else if (area == "PRODUCT")
336 {
337 result["PRODUCT_LANGUAGE_CODE"] =
338 std::to_string(static_cast<int>(*fruBytesIter));
339 fruBytesIter += 1;
340 fieldData = &PRODUCT_FRU_AREAS;
341 }
342 else
343 {
344 continue;
345 }
346 for (auto &field : *fieldData)
347 {
348 if (fruBytesIter >= fruBytes.end())
349 {
350 return false;
351 }
352
353 size_t length = *fruBytesIter & 0x3f;
354 fruBytesIter += 1;
355
356 if (fruBytesIter >= fruBytes.end())
357 {
358 return false;
359 }
360
361 result[std::string(area) + "_" + field] =
362 std::string(fruBytesIter, fruBytesIter + length);
363 fruBytesIter += length;
364 if (fruBytesIter >= fruBytes.end())
365 {
366 std::cerr << "Warning Fru Length Mismatch:\n ";
367 for (auto &c : fruBytes)
368 {
369 std::cerr << c;
370 }
371 std::cerr << "\n";
372 if (DEBUG)
373 {
374 for (auto &keyPair : result)
375 {
376 std::cerr << keyPair.first << " : "
377 << keyPair.second << "\n";
378 }
379 }
380 return false;
381 }
382 }
383 }
384 }
385
386 return true;
387}
388
389void AddFruObjectToDbus(
James Feist9eb0b582018-04-27 12:15:46 -0700390 std::shared_ptr<sdbusplus::asio::connection> dbusConn,
391 std::vector<char> &device, sdbusplus::asio::object_server &objServer,
James Feist3cb5fec2018-01-23 14:41:51 -0800392 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700393 std::shared_ptr<sdbusplus::asio::dbus_interface>>
394 &dbusInterfaceMap,
James Feist13b86d62018-05-29 11:24:35 -0700395 uint32_t bus, uint32_t address)
James Feist3cb5fec2018-01-23 14:41:51 -0800396{
397 boost::container::flat_map<std::string, std::string> formattedFru;
398 if (!formatFru(device, formattedFru))
399 {
400 std::cerr << "failed to format fru for device at bus " << std::hex
401 << bus << "address " << address << "\n";
402 return;
403 }
404 auto productNameFind = formattedFru.find("BOARD_PRODUCT_NAME");
405 std::string productName;
406 if (productNameFind == formattedFru.end())
407 {
408 productNameFind = formattedFru.find("PRODUCT_PRODUCT_NAME");
409 }
410 if (productNameFind != formattedFru.end())
411 {
412 productName = productNameFind->second;
James Feist3f8a2782018-02-12 09:24:42 -0800413 std::regex illegalObject("[^A-Za-z0-9_]");
414 productName = std::regex_replace(productName, illegalObject, "_");
James Feist3cb5fec2018-01-23 14:41:51 -0800415 }
416 else
417 {
418 productName = "UNKNOWN" + std::to_string(UNKNOWN_BUS_OBJECT_COUNT);
419 UNKNOWN_BUS_OBJECT_COUNT++;
420 }
421
James Feist918e18c2018-02-13 15:51:07 -0800422 productName = "/xyz/openbmc_project/FruDevice/" + productName;
James Feist918e18c2018-02-13 15:51:07 -0800423 // avoid duplicates by checking to see if on a mux
James Feist79e9c0b2018-03-15 15:21:17 -0700424 if (bus > 0)
James Feist918e18c2018-02-13 15:51:07 -0800425 {
James Feist79e9c0b2018-03-15 15:21:17 -0700426 size_t index = 0;
James Feist9eb0b582018-04-27 12:15:46 -0700427 for (auto const &busIface : dbusInterfaceMap)
James Feist918e18c2018-02-13 15:51:07 -0800428 {
James Feist9eb0b582018-04-27 12:15:46 -0700429 if ((busIface.second->get_object_path() == productName))
James Feist918e18c2018-02-13 15:51:07 -0800430 {
James Feist9eb0b582018-04-27 12:15:46 -0700431 if (isMuxBus(bus) && address == busIface.first.second)
James Feist79e9c0b2018-03-15 15:21:17 -0700432 {
433 continue;
434 }
435 // add underscore _index for the same object path on dbus
436 std::string strIndex = std::to_string(index);
437 if (index > 0)
438 {
439 productName.substr(0, productName.size() - strIndex.size());
440 }
441 else
442 {
443 productName += "_";
444 }
445 productName += std::to_string(index++);
James Feist918e18c2018-02-13 15:51:07 -0800446 }
447 }
448 }
James Feist3cb5fec2018-01-23 14:41:51 -0800449
James Feist9eb0b582018-04-27 12:15:46 -0700450 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
451 objServer.add_interface(productName, "xyz.openbmc_project.FruDevice");
452 dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface;
453
James Feist3cb5fec2018-01-23 14:41:51 -0800454 for (auto &property : formattedFru)
455 {
James Feist9eb0b582018-04-27 12:15:46 -0700456
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700457 std::regex_replace(property.second.begin(), property.second.begin(),
458 property.second.end(), NON_ASCII_REGEX, "_");
James Feist9eb0b582018-04-27 12:15:46 -0700459 if (property.second.empty())
460 {
461 continue;
462 }
463 std::string key =
464 std::regex_replace(property.first, NON_ASCII_REGEX, "_");
465 if (!iface->register_property(key, property.second + '\0'))
466 {
467 std::cerr << "illegal key: " << key << "\n";
468 }
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700469 if (DEBUG)
470 {
471 std::cout << property.first << ": " << property.second << "\n";
472 }
James Feist3cb5fec2018-01-23 14:41:51 -0800473 }
James Feist2a9d6db2018-04-27 15:48:28 -0700474
475 // baseboard will be 0, 0
James Feist13b86d62018-05-29 11:24:35 -0700476 iface->register_property("BUS", bus);
477 iface->register_property("ADDRESS", address);
James Feist2a9d6db2018-04-27 15:48:28 -0700478
James Feist9eb0b582018-04-27 12:15:46 -0700479 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800480}
481
482static bool readBaseboardFru(std::vector<char> &baseboardFru)
483{
484 // try to read baseboard fru from file
485 std::ifstream baseboardFruFile(BASEBOARD_FRU_LOCATION, std::ios::binary);
486 if (baseboardFruFile.good())
487 {
488 baseboardFruFile.seekg(0, std::ios_base::end);
489 std::streampos fileSize = baseboardFruFile.tellg();
490 baseboardFru.resize(fileSize);
491 baseboardFruFile.seekg(0, std::ios_base::beg);
492 baseboardFruFile.read(baseboardFru.data(), fileSize);
493 }
494 else
495 {
496 return false;
497 }
498 return true;
499}
500
James Feistb49ffc32018-05-02 11:10:43 -0700501bool writeFru(uint8_t bus, uint8_t address, const std::vector<uint8_t> &fru)
502{
503 boost::container::flat_map<std::string, std::string> tmp;
504 if (fru.size() > MAX_FRU_SIZE)
505 {
506 std::cerr << "Invalid fru.size() during writeFru\n";
507 return false;
508 }
509 // verify legal fru by running it through fru parsing logic
510 if (!formatFru(reinterpret_cast<const std::vector<char> &>(fru), tmp))
511 {
512 std::cerr << "Invalid fru format during writeFru\n";
513 return false;
514 }
515 // baseboard fru
516 if (bus == 0 && address == 0)
517 {
518 std::ofstream file(BASEBOARD_FRU_LOCATION, std::ios_base::binary);
519 if (!file.good())
520 {
521 std::cerr << "Error opening file " << BASEBOARD_FRU_LOCATION
522 << "\n";
523 throw sdbusplus::xyz::openbmc_project::Common::Error::
524 InternalFailure();
525 return false;
526 }
527 file.write(reinterpret_cast<const char *>(fru.data()), fru.size());
528 return file.good();
529 }
530 else
531 {
532 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
533
534 int file = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
535 if (file < 0)
536 {
537 std::cerr << "unable to open i2c device " << i2cBus << "\n";
538 throw sdbusplus::xyz::openbmc_project::Common::Error::
539 InternalFailure();
540 return false;
541 }
542 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
543 {
544 std::cerr << "unable to set device address\n";
545 close(file);
546 throw sdbusplus::xyz::openbmc_project::Common::Error::
547 InternalFailure();
548 return false;
549 }
550
551 constexpr const size_t RETRY_MAX = 2;
552 uint16_t index = 0;
553 size_t retries = RETRY_MAX;
554 while (index < fru.size())
555 {
556 if ((index && ((index % (MAX_EEPROM_PAGE_INDEX + 1)) == 0)) &&
557 (retries == RETRY_MAX))
558 {
559 // The 4K EEPROM only uses the A2 and A1 device address bits
560 // with the third bit being a memory page address bit.
561 if (ioctl(file, I2C_SLAVE_FORCE, ++address) < 0)
562 {
563 std::cerr << "unable to set device address\n";
564 close(file);
565 throw sdbusplus::xyz::openbmc_project::Common::Error::
566 InternalFailure();
567 return false;
568 }
569 }
570
571 if (i2c_smbus_write_byte_data(file, index & 0xFF, fru[index]) < 0)
572 {
573 if (!retries--)
574 {
575 std::cerr << "error writing fru: " << strerror(errno)
576 << "\n";
577 close(file);
578 throw sdbusplus::xyz::openbmc_project::Common::Error::
579 InternalFailure();
580 return false;
581 }
582 }
583 else
584 {
585 retries = RETRY_MAX;
586 index++;
587 }
588 // most eeproms require 5-10ms between writes
589 std::this_thread::sleep_for(std::chrono::milliseconds(10));
590 }
591 close(file);
592 return true;
593 }
594}
595
James Feist9eb0b582018-04-27 12:15:46 -0700596void rescanBusses(
James Feist6ebf9de2018-05-15 15:01:17 -0700597 boost::asio::io_service &io, BusMap &busMap,
James Feist9eb0b582018-04-27 12:15:46 -0700598 boost::container::flat_map<std::pair<size_t, size_t>,
599 std::shared_ptr<sdbusplus::asio::dbus_interface>>
600 &dbusInterfaceMap,
James Feist6ebf9de2018-05-15 15:01:17 -0700601 std::shared_ptr<sdbusplus::asio::connection> &systemBus,
602 sdbusplus::asio::object_server &objServer)
James Feist918e18c2018-02-13 15:51:07 -0800603{
James Feist6ebf9de2018-05-15 15:01:17 -0700604 static boost::asio::deadline_timer timer(io);
605 timer.expires_from_now(boost::posix_time::seconds(1));
James Feist918e18c2018-02-13 15:51:07 -0800606
James Feist6ebf9de2018-05-15 15:01:17 -0700607 // setup an async wait incase we get flooded with requests
608 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist4131aea2018-03-09 09:47:30 -0800609 auto devDir = fs::path("/dev/");
610 auto matchString = std::string("i2c*");
611 std::vector<fs::path> i2cBuses;
James Feist918e18c2018-02-13 15:51:07 -0800612
James Feist4131aea2018-03-09 09:47:30 -0800613 if (!find_files(devDir, matchString, i2cBuses, 0))
James Feist918e18c2018-02-13 15:51:07 -0800614 {
James Feist4131aea2018-03-09 09:47:30 -0800615 std::cerr << "unable to find i2c devices\n";
616 return;
James Feist918e18c2018-02-13 15:51:07 -0800617 }
James Feist4131aea2018-03-09 09:47:30 -0800618 // scanning muxes in reverse order seems to have adverse effects
619 // sorting this list seems to be a fix for that
620 std::sort(i2cBuses.begin(), i2cBuses.end());
James Feist4131aea2018-03-09 09:47:30 -0800621
James Feist6ebf9de2018-05-15 15:01:17 -0700622 busMap.clear();
623 auto scan = std::make_shared<FindDevicesWithCallback>(
624 i2cBuses, io, busMap, [&]() {
625 for (auto &busIface : dbusInterfaceMap)
626 {
627 objServer.remove_interface(busIface.second);
628 }
James Feist4131aea2018-03-09 09:47:30 -0800629
James Feist6ebf9de2018-05-15 15:01:17 -0700630 dbusInterfaceMap.clear();
631 UNKNOWN_BUS_OBJECT_COUNT = 0;
James Feist4131aea2018-03-09 09:47:30 -0800632
James Feist6ebf9de2018-05-15 15:01:17 -0700633 // todo, get this from a more sensable place
634 std::vector<char> baseboardFru;
635 if (readBaseboardFru(baseboardFru))
636 {
637 boost::container::flat_map<int, std::vector<char>>
638 baseboardDev;
639 baseboardDev.emplace(0, baseboardFru);
640 busMap[0] = std::make_shared<DeviceMap>(baseboardDev);
641 }
642 for (auto &devicemap : busMap)
643 {
644 for (auto &device : *devicemap.second)
645 {
646 AddFruObjectToDbus(systemBus, device.second, objServer,
647 dbusInterfaceMap, devicemap.first,
648 device.first);
649 }
650 }
651 });
652 scan->run();
653 });
James Feist918e18c2018-02-13 15:51:07 -0800654}
655
James Feist3cb5fec2018-01-23 14:41:51 -0800656int main(int argc, char **argv)
657{
658 auto devDir = fs::path("/dev/");
659 auto matchString = std::string("i2c*");
660 std::vector<fs::path> i2cBuses;
661
662 if (!find_files(devDir, matchString, i2cBuses, 0))
663 {
664 std::cerr << "unable to find i2c devices\n";
665 return 1;
666 }
James Feist3cb5fec2018-01-23 14:41:51 -0800667
668 boost::asio::io_service io;
James Feist9eb0b582018-04-27 12:15:46 -0700669 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
670 auto objServer = sdbusplus::asio::object_server(systemBus);
James Feist3cb5fec2018-01-23 14:41:51 -0800671 systemBus->request_name("com.intel.FruDevice");
672
James Feist6ebf9de2018-05-15 15:01:17 -0700673 // this is a map with keys of pair(bus number, address) and values of
674 // the object on dbus
James Feist3cb5fec2018-01-23 14:41:51 -0800675 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700676 std::shared_ptr<sdbusplus::asio::dbus_interface>>
677 dbusInterfaceMap;
James Feist2a9d6db2018-04-27 15:48:28 -0700678 BusMap busmap;
James Feist3cb5fec2018-01-23 14:41:51 -0800679
James Feist9eb0b582018-04-27 12:15:46 -0700680 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
681 objServer.add_interface("/xyz/openbmc_project/FruDevice",
682 "xyz.openbmc_project.FruDeviceManager");
James Feist3cb5fec2018-01-23 14:41:51 -0800683
684 iface->register_method("ReScan", [&]() {
James Feist6ebf9de2018-05-15 15:01:17 -0700685 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
James Feist3cb5fec2018-01-23 14:41:51 -0800686 });
James Feist2a9d6db2018-04-27 15:48:28 -0700687
688 iface->register_method(
689 "GetRawFru", [&](const uint8_t &bus, const uint8_t &address) {
690 auto deviceMap = busmap.find(bus);
691 if (deviceMap == busmap.end())
692 {
693 throw sdbusplus::xyz::openbmc_project::Common::Error::
694 InvalidArgument();
695 }
696 auto device = deviceMap->second->find(address);
697 if (device == deviceMap->second->end())
698 {
699 throw sdbusplus::xyz::openbmc_project::Common::Error::
700 InvalidArgument();
701 }
702 std::vector<uint8_t> &ret =
703 reinterpret_cast<std::vector<uint8_t> &>(device->second);
704 return ret;
705 });
James Feistb49ffc32018-05-02 11:10:43 -0700706
707 iface->register_method("WriteFru", [&](const uint8_t bus,
708 const uint8_t address,
709 const std::vector<uint8_t> &data) {
710 if (!writeFru(bus, address, data))
711 {
712 throw sdbusplus::xyz::openbmc_project::Common::Error::
713 InvalidArgument();
714 return;
715 }
716 // schedule rescan on success
717 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
718
719 });
James Feist9eb0b582018-04-27 12:15:46 -0700720 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800721
James Feist9eb0b582018-04-27 12:15:46 -0700722 std::function<void(sdbusplus::message::message & message)> eventHandler =
723 [&](sdbusplus::message::message &message) {
James Feist918e18c2018-02-13 15:51:07 -0800724 std::string objectName;
James Feist9eb0b582018-04-27 12:15:46 -0700725 boost::container::flat_map<
726 std::string, sdbusplus::message::variant<
727 std::string, bool, int64_t, uint64_t, double>>
728 values;
729 message.read(objectName, values);
James Feist918e18c2018-02-13 15:51:07 -0800730 auto findPgood = values.find("pgood");
731 if (findPgood != values.end())
732 {
James Feist6ebf9de2018-05-15 15:01:17 -0700733
734 rescanBusses(io, busmap, dbusInterfaceMap, systemBus,
735 objServer);
James Feist918e18c2018-02-13 15:51:07 -0800736 }
James Feist918e18c2018-02-13 15:51:07 -0800737 };
James Feist9eb0b582018-04-27 12:15:46 -0700738
739 sdbusplus::bus::match::match powerMatch = sdbusplus::bus::match::match(
740 static_cast<sdbusplus::bus::bus &>(*systemBus),
741 "type='signal',interface='org.freedesktop.DBus.Properties',path_"
Kuiying Wang1dc43102018-05-09 15:09:29 +0800742 "namespace='/xyz/openbmc_project/Chassis/Control/"
743 "power0',arg0='xyz.openbmc_project.Chassis.Control.Power'",
James Feist9eb0b582018-04-27 12:15:46 -0700744 eventHandler);
745
James Feist4131aea2018-03-09 09:47:30 -0800746 int fd = inotify_init();
747 int wd = inotify_add_watch(fd, I2C_DEV_LOCATION,
748 IN_CREATE | IN_MOVED_TO | IN_DELETE);
749 std::array<char, 4096> readBuffer;
750 std::string pendingBuffer;
751 // monitor for new i2c devices
752 boost::asio::posix::stream_descriptor dirWatch(io, fd);
753 std::function<void(const boost::system::error_code, std::size_t)>
754 watchI2cBusses = [&](const boost::system::error_code &ec,
755 std::size_t bytes_transferred) {
756 if (ec)
757 {
758 std::cout << "Callback Error " << ec << "\n";
759 return;
760 }
761 pendingBuffer += std::string(readBuffer.data(), bytes_transferred);
762 bool devChange = false;
763 while (pendingBuffer.size() > sizeof(inotify_event))
764 {
765 const inotify_event *iEvent =
766 reinterpret_cast<const inotify_event *>(
767 pendingBuffer.data());
768 switch (iEvent->mask)
769 {
James Feist9eb0b582018-04-27 12:15:46 -0700770 case IN_CREATE:
771 case IN_MOVED_TO:
772 case IN_DELETE:
773 if (boost::starts_with(std::string(iEvent->name),
774 "i2c"))
775 {
776 devChange = true;
777 }
James Feist4131aea2018-03-09 09:47:30 -0800778 }
779
780 pendingBuffer.erase(0, sizeof(inotify_event) + iEvent->len);
781 }
James Feist6ebf9de2018-05-15 15:01:17 -0700782 if (devChange)
James Feist4131aea2018-03-09 09:47:30 -0800783 {
James Feist6ebf9de2018-05-15 15:01:17 -0700784 rescanBusses(io, busmap, dbusInterfaceMap, systemBus,
785 objServer);
James Feist4131aea2018-03-09 09:47:30 -0800786 }
James Feist6ebf9de2018-05-15 15:01:17 -0700787
James Feist4131aea2018-03-09 09:47:30 -0800788 dirWatch.async_read_some(boost::asio::buffer(readBuffer),
789 watchI2cBusses);
790 };
791
792 dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses);
Gunnar Millsb3e42fe2018-06-13 15:48:27 -0500793 // run the initial scan
James Feist6ebf9de2018-05-15 15:01:17 -0700794 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
James Feist918e18c2018-02-13 15:51:07 -0800795
James Feist3cb5fec2018-01-23 14:41:51 -0800796 io.run();
797 return 0;
798}