blob: 2a89c087322ba04d52a56e8e344c1dc1475e706c [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 Feist3cb5fec2018-01-23 14:41:51 -080041
42const static constexpr char *BASEBOARD_FRU_LOCATION =
43 "/etc/fru/baseboard.fru.bin";
44
James Feist4131aea2018-03-09 09:47:30 -080045const static constexpr char *I2C_DEV_LOCATION = "/dev";
46
James Feist3cb5fec2018-01-23 14:41:51 -080047static constexpr std::array<const char *, 5> FRU_AREAS = {
48 "INTERNAL", "CHASSIS", "BOARD", "PRODUCT", "MULTIRECORD"};
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -070049const static std::regex NON_ASCII_REGEX("[^\x01-\x7f]");
James Feist3cb5fec2018-01-23 14:41:51 -080050using DeviceMap = boost::container::flat_map<int, std::vector<char>>;
51using BusMap = boost::container::flat_map<int, std::shared_ptr<DeviceMap>>;
52
James Feist6ebf9de2018-05-15 15:01:17 -070053struct FindDevicesWithCallback;
54
James Feistc95cb142018-02-26 10:41:42 -080055static bool isMuxBus(size_t bus)
56{
57 return is_symlink(std::experimental::filesystem::path(
58 "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device"));
59}
60
James Feist3cb5fec2018-01-23 14:41:51 -080061int get_bus_frus(int file, int first, int last, int bus,
62 std::shared_ptr<DeviceMap> devices)
63{
64 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> block_data;
65
66 for (int ii = first; ii <= last; ii++)
67 {
68 // Set slave address
69 if (ioctl(file, I2C_SLAVE_FORCE, ii) < 0)
70 {
71 std::cerr << "device at bus " << bus << "register " << ii
72 << "busy\n";
73 continue;
74 }
75 // probe
76 else if (i2c_smbus_read_byte(file) < 0)
77 {
78 continue;
79 }
80
81 if (DEBUG)
82 {
83 std::cout << "something at bus " << bus << "addr " << ii << "\n";
84 }
85 if (i2c_smbus_read_i2c_block_data(file, 0x0, 0x8, block_data.data()) <
86 0)
87 {
88 std::cerr << "failed to read bus " << bus << " address " << ii
89 << "\n";
90 continue;
91 }
92 size_t sum = 0;
93 for (int jj = 0; jj < 7; jj++)
94 {
95 sum += block_data[jj];
96 }
97 sum = (256 - sum) & 0xFF;
98
99 // check the header checksum
100 if (sum == block_data[7])
101 {
102 std::vector<char> device;
103 device.insert(device.end(), block_data.begin(),
104 block_data.begin() + 8);
105
106 for (int jj = 1; jj <= FRU_AREAS.size(); jj++)
107 {
108 auto area_offset = device[jj];
109 if (area_offset != 0)
110 {
111 area_offset *= 8;
112 if (i2c_smbus_read_i2c_block_data(file, area_offset, 0x8,
113 block_data.data()) < 0)
114 {
115 std::cerr << "failed to read bus " << bus << " address "
116 << ii << "\n";
117 return -1;
118 }
119 int length = block_data[1] * 8;
120 device.insert(device.end(), block_data.begin(),
121 block_data.begin() + 8);
122 length -= 8;
123 area_offset += 8;
124
125 while (length > 0)
126 {
127 auto to_get = std::min(0x20, length);
128 if (i2c_smbus_read_i2c_block_data(
129 file, area_offset, to_get, block_data.data()) <
130 0)
131 {
132 std::cerr << "failed to read bus " << bus
133 << " address " << ii << "\n";
134 return -1;
135 }
136 device.insert(device.end(), block_data.begin(),
137 block_data.begin() + to_get);
138 area_offset += to_get;
139 length -= to_get;
140 }
141 }
142 }
143 (*devices).emplace(ii, device);
144 }
145 }
146
147 return 0;
148}
149
James Feist6ebf9de2018-05-15 15:01:17 -0700150static void FindI2CDevices(const std::vector<fs::path> &i2cBuses,
151 std::shared_ptr<FindDevicesWithCallback> context,
152 boost::asio::io_service &io, BusMap &busMap)
James Feist3cb5fec2018-01-23 14:41:51 -0800153{
James Feist3cb5fec2018-01-23 14:41:51 -0800154 for (auto &i2cBus : i2cBuses)
155 {
156 auto busnum = i2cBus.string();
157 auto lastDash = busnum.rfind(std::string("-"));
158 // delete everything before dash inclusive
159 if (lastDash != std::string::npos)
160 {
161 busnum.erase(0, lastDash + 1);
162 }
163 auto bus = std::stoi(busnum);
164
165 auto file = open(i2cBus.c_str(), O_RDWR);
166 if (file < 0)
167 {
168 std::cerr << "unable to open i2c device " << i2cBus.string()
169 << "\n";
170 continue;
171 }
172 unsigned long funcs = 0;
173
174 if (ioctl(file, I2C_FUNCS, &funcs) < 0)
175 {
176 std::cerr
177 << "Error: Could not get the adapter functionality matrix bus"
178 << bus << "\n";
179 continue;
180 }
181 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE) ||
182 !(I2C_FUNC_SMBUS_READ_I2C_BLOCK))
183 {
184 std::cerr << "Error: Can't use SMBus Receive Byte command bus "
185 << bus << "\n";
186 continue;
187 }
188 auto &device = busMap[bus];
189 device = std::make_shared<DeviceMap>();
190
James Feistc95cb142018-02-26 10:41:42 -0800191 // don't scan muxed buses async as don't want to confuse the mux
192 if (isMuxBus(bus))
193 {
194 get_bus_frus(file, 0x03, 0x77, bus, device);
195 close(file);
196 }
197 else
198 {
James Feist6ebf9de2018-05-15 15:01:17 -0700199 io.post([file, device, bus, context] {
200 // i2cdetect by default uses the range 0x03 to 0x77, as
201 // this is
202 // what we
203 // have tested with, use this range. Could be changed in
204 // future.
205 get_bus_frus(file, 0x03, 0x77, bus, device);
206 close(file);
207 });
James Feistc95cb142018-02-26 10:41:42 -0800208 }
James Feist3cb5fec2018-01-23 14:41:51 -0800209 }
James Feist3cb5fec2018-01-23 14:41:51 -0800210}
211
James Feist6ebf9de2018-05-15 15:01:17 -0700212// this class allows an async response after all i2c devices are discovered
213struct FindDevicesWithCallback
214 : std::enable_shared_from_this<FindDevicesWithCallback>
215{
216 FindDevicesWithCallback(const std::vector<fs::path> &i2cBuses,
217 boost::asio::io_service &io, BusMap &busMap,
218 std::function<void(void)> &&callback) :
219 _i2cBuses(i2cBuses),
220 _io(io), _busMap(busMap), _callback(std::move(callback))
221 {
222 }
223 ~FindDevicesWithCallback()
224 {
225 _callback();
226 }
227 void run()
228 {
229 FindI2CDevices(_i2cBuses, shared_from_this(), _io, _busMap);
230 }
231
232 const std::vector<fs::path> &_i2cBuses;
233 boost::asio::io_service &_io;
234 BusMap &_busMap;
235 std::function<void(void)> _callback;
236};
237
James Feist3cb5fec2018-01-23 14:41:51 -0800238static const std::tm intelEpoch(void)
239{
240 std::tm val = {0};
241 val.tm_year = 1996 - 1900;
242 return val;
243}
244
245bool formatFru(const std::vector<char> &fruBytes,
246 boost::container::flat_map<std::string, std::string> &result)
247{
248 static const std::vector<const char *> CHASSIS_FRU_AREAS = {
249 "PART_NUMBER", "SERIAL_NUMBER", "CHASSIS_INFO_AM1", "CHASSIS_INFO_AM2"};
250
251 static const std::vector<const char *> BOARD_FRU_AREAS = {
252 "MANUFACTURER", "PRODUCT_NAME", "SERIAL_NUMBER", "PART_NUMBER",
253 "VERSION_ID"};
254
255 static const std::vector<const char *> PRODUCT_FRU_AREAS = {
256 "MANUFACTURER", "PRODUCT_NAME", "PART_NUMBER",
257 "PRODUCT_VERSION", "PRODUCT_SERIAL_NUMBER", "ASSET_TAG"};
258
259 size_t sum = 0;
260
261 if (fruBytes.size() < 8)
262 {
263 return false;
264 }
265 std::vector<char>::const_iterator fruAreaOffsetField = fruBytes.begin();
James Feist9eb0b582018-04-27 12:15:46 -0700266 result["Common_Format_Version"] =
James Feist3cb5fec2018-01-23 14:41:51 -0800267 std::to_string(static_cast<int>(*fruAreaOffsetField));
268
269 const std::vector<const char *> *fieldData;
270
271 for (auto &area : FRU_AREAS)
272 {
273 fruAreaOffsetField++;
274 if (fruAreaOffsetField >= fruBytes.end())
275 {
276 return false;
277 }
278 size_t offset = (*fruAreaOffsetField) * 8;
279
280 if (offset > 1)
281 {
282 // +2 to skip format and length
283 std::vector<char>::const_iterator fruBytesIter =
284 fruBytes.begin() + offset + 2;
285
286 if (fruBytesIter >= fruBytes.end())
287 {
288 return false;
289 }
290
291 if (area == "CHASSIS")
292 {
293 result["CHASSIS_TYPE"] =
294 std::to_string(static_cast<int>(*fruBytesIter));
295 fruBytesIter += 1;
296 fieldData = &CHASSIS_FRU_AREAS;
297 }
298 else if (area == "BOARD")
299 {
300 result["BOARD_LANGUAGE_CODE"] =
301 std::to_string(static_cast<int>(*fruBytesIter));
302 fruBytesIter += 1;
303 if (fruBytesIter >= fruBytes.end())
304 {
305 return false;
306 }
307
308 unsigned int minutes = *fruBytesIter |
309 *(fruBytesIter + 1) << 8 |
310 *(fruBytesIter + 2) << 16;
311 std::tm fruTime = intelEpoch();
312 time_t timeValue = mktime(&fruTime);
313 timeValue += minutes * 60;
314 fruTime = *gmtime(&timeValue);
315
316 result["BOARD_MANUFACTURE_DATE"] = asctime(&fruTime);
317 result["BOARD_MANUFACTURE_DATE"]
318 .pop_back(); // remove trailing null
319 fruBytesIter += 3;
320 fieldData = &BOARD_FRU_AREAS;
321 }
322 else if (area == "PRODUCT")
323 {
324 result["PRODUCT_LANGUAGE_CODE"] =
325 std::to_string(static_cast<int>(*fruBytesIter));
326 fruBytesIter += 1;
327 fieldData = &PRODUCT_FRU_AREAS;
328 }
329 else
330 {
331 continue;
332 }
333 for (auto &field : *fieldData)
334 {
335 if (fruBytesIter >= fruBytes.end())
336 {
337 return false;
338 }
339
340 size_t length = *fruBytesIter & 0x3f;
341 fruBytesIter += 1;
342
343 if (fruBytesIter >= fruBytes.end())
344 {
345 return false;
346 }
347
348 result[std::string(area) + "_" + field] =
349 std::string(fruBytesIter, fruBytesIter + length);
350 fruBytesIter += length;
351 if (fruBytesIter >= fruBytes.end())
352 {
353 std::cerr << "Warning Fru Length Mismatch:\n ";
354 for (auto &c : fruBytes)
355 {
356 std::cerr << c;
357 }
358 std::cerr << "\n";
359 if (DEBUG)
360 {
361 for (auto &keyPair : result)
362 {
363 std::cerr << keyPair.first << " : "
364 << keyPair.second << "\n";
365 }
366 }
367 return false;
368 }
369 }
370 }
371 }
372
373 return true;
374}
375
376void AddFruObjectToDbus(
James Feist9eb0b582018-04-27 12:15:46 -0700377 std::shared_ptr<sdbusplus::asio::connection> dbusConn,
378 std::vector<char> &device, sdbusplus::asio::object_server &objServer,
James Feist3cb5fec2018-01-23 14:41:51 -0800379 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700380 std::shared_ptr<sdbusplus::asio::dbus_interface>>
381 &dbusInterfaceMap,
James Feist13b86d62018-05-29 11:24:35 -0700382 uint32_t bus, uint32_t address)
James Feist3cb5fec2018-01-23 14:41:51 -0800383{
384 boost::container::flat_map<std::string, std::string> formattedFru;
385 if (!formatFru(device, formattedFru))
386 {
387 std::cerr << "failed to format fru for device at bus " << std::hex
388 << bus << "address " << address << "\n";
389 return;
390 }
391 auto productNameFind = formattedFru.find("BOARD_PRODUCT_NAME");
392 std::string productName;
393 if (productNameFind == formattedFru.end())
394 {
395 productNameFind = formattedFru.find("PRODUCT_PRODUCT_NAME");
396 }
397 if (productNameFind != formattedFru.end())
398 {
399 productName = productNameFind->second;
James Feist3f8a2782018-02-12 09:24:42 -0800400 std::regex illegalObject("[^A-Za-z0-9_]");
401 productName = std::regex_replace(productName, illegalObject, "_");
James Feist3cb5fec2018-01-23 14:41:51 -0800402 }
403 else
404 {
405 productName = "UNKNOWN" + std::to_string(UNKNOWN_BUS_OBJECT_COUNT);
406 UNKNOWN_BUS_OBJECT_COUNT++;
407 }
408
James Feist918e18c2018-02-13 15:51:07 -0800409 productName = "/xyz/openbmc_project/FruDevice/" + productName;
James Feist918e18c2018-02-13 15:51:07 -0800410 // avoid duplicates by checking to see if on a mux
James Feist79e9c0b2018-03-15 15:21:17 -0700411 if (bus > 0)
James Feist918e18c2018-02-13 15:51:07 -0800412 {
James Feist79e9c0b2018-03-15 15:21:17 -0700413 size_t index = 0;
James Feist9eb0b582018-04-27 12:15:46 -0700414 for (auto const &busIface : dbusInterfaceMap)
James Feist918e18c2018-02-13 15:51:07 -0800415 {
James Feist9eb0b582018-04-27 12:15:46 -0700416 if ((busIface.second->get_object_path() == productName))
James Feist918e18c2018-02-13 15:51:07 -0800417 {
James Feist9eb0b582018-04-27 12:15:46 -0700418 if (isMuxBus(bus) && address == busIface.first.second)
James Feist79e9c0b2018-03-15 15:21:17 -0700419 {
420 continue;
421 }
422 // add underscore _index for the same object path on dbus
423 std::string strIndex = std::to_string(index);
424 if (index > 0)
425 {
426 productName.substr(0, productName.size() - strIndex.size());
427 }
428 else
429 {
430 productName += "_";
431 }
432 productName += std::to_string(index++);
James Feist918e18c2018-02-13 15:51:07 -0800433 }
434 }
435 }
James Feist3cb5fec2018-01-23 14:41:51 -0800436
James Feist9eb0b582018-04-27 12:15:46 -0700437 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
438 objServer.add_interface(productName, "xyz.openbmc_project.FruDevice");
439 dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface;
440
James Feist3cb5fec2018-01-23 14:41:51 -0800441 for (auto &property : formattedFru)
442 {
James Feist9eb0b582018-04-27 12:15:46 -0700443
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700444 std::regex_replace(property.second.begin(), property.second.begin(),
445 property.second.end(), NON_ASCII_REGEX, "_");
James Feist9eb0b582018-04-27 12:15:46 -0700446 if (property.second.empty())
447 {
448 continue;
449 }
450 std::string key =
451 std::regex_replace(property.first, NON_ASCII_REGEX, "_");
452 if (!iface->register_property(key, property.second + '\0'))
453 {
454 std::cerr << "illegal key: " << key << "\n";
455 }
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700456 if (DEBUG)
457 {
458 std::cout << property.first << ": " << property.second << "\n";
459 }
James Feist3cb5fec2018-01-23 14:41:51 -0800460 }
James Feist2a9d6db2018-04-27 15:48:28 -0700461
462 // baseboard will be 0, 0
James Feist13b86d62018-05-29 11:24:35 -0700463 iface->register_property("BUS", bus);
464 iface->register_property("ADDRESS", address);
James Feist2a9d6db2018-04-27 15:48:28 -0700465
James Feist9eb0b582018-04-27 12:15:46 -0700466 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800467}
468
469static bool readBaseboardFru(std::vector<char> &baseboardFru)
470{
471 // try to read baseboard fru from file
472 std::ifstream baseboardFruFile(BASEBOARD_FRU_LOCATION, std::ios::binary);
473 if (baseboardFruFile.good())
474 {
475 baseboardFruFile.seekg(0, std::ios_base::end);
476 std::streampos fileSize = baseboardFruFile.tellg();
477 baseboardFru.resize(fileSize);
478 baseboardFruFile.seekg(0, std::ios_base::beg);
479 baseboardFruFile.read(baseboardFru.data(), fileSize);
480 }
481 else
482 {
483 return false;
484 }
485 return true;
486}
487
James Feistb49ffc32018-05-02 11:10:43 -0700488bool writeFru(uint8_t bus, uint8_t address, const std::vector<uint8_t> &fru)
489{
490 boost::container::flat_map<std::string, std::string> tmp;
491 if (fru.size() > MAX_FRU_SIZE)
492 {
493 std::cerr << "Invalid fru.size() during writeFru\n";
494 return false;
495 }
496 // verify legal fru by running it through fru parsing logic
497 if (!formatFru(reinterpret_cast<const std::vector<char> &>(fru), tmp))
498 {
499 std::cerr << "Invalid fru format during writeFru\n";
500 return false;
501 }
502 // baseboard fru
503 if (bus == 0 && address == 0)
504 {
505 std::ofstream file(BASEBOARD_FRU_LOCATION, std::ios_base::binary);
506 if (!file.good())
507 {
508 std::cerr << "Error opening file " << BASEBOARD_FRU_LOCATION
509 << "\n";
510 throw sdbusplus::xyz::openbmc_project::Common::Error::
511 InternalFailure();
512 return false;
513 }
514 file.write(reinterpret_cast<const char *>(fru.data()), fru.size());
515 return file.good();
516 }
517 else
518 {
519 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
520
521 int file = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
522 if (file < 0)
523 {
524 std::cerr << "unable to open i2c device " << i2cBus << "\n";
525 throw sdbusplus::xyz::openbmc_project::Common::Error::
526 InternalFailure();
527 return false;
528 }
529 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
530 {
531 std::cerr << "unable to set device address\n";
532 close(file);
533 throw sdbusplus::xyz::openbmc_project::Common::Error::
534 InternalFailure();
535 return false;
536 }
537
538 constexpr const size_t RETRY_MAX = 2;
539 uint16_t index = 0;
540 size_t retries = RETRY_MAX;
541 while (index < fru.size())
542 {
543 if ((index && ((index % (MAX_EEPROM_PAGE_INDEX + 1)) == 0)) &&
544 (retries == RETRY_MAX))
545 {
546 // The 4K EEPROM only uses the A2 and A1 device address bits
547 // with the third bit being a memory page address bit.
548 if (ioctl(file, I2C_SLAVE_FORCE, ++address) < 0)
549 {
550 std::cerr << "unable to set device address\n";
551 close(file);
552 throw sdbusplus::xyz::openbmc_project::Common::Error::
553 InternalFailure();
554 return false;
555 }
556 }
557
558 if (i2c_smbus_write_byte_data(file, index & 0xFF, fru[index]) < 0)
559 {
560 if (!retries--)
561 {
562 std::cerr << "error writing fru: " << strerror(errno)
563 << "\n";
564 close(file);
565 throw sdbusplus::xyz::openbmc_project::Common::Error::
566 InternalFailure();
567 return false;
568 }
569 }
570 else
571 {
572 retries = RETRY_MAX;
573 index++;
574 }
575 // most eeproms require 5-10ms between writes
576 std::this_thread::sleep_for(std::chrono::milliseconds(10));
577 }
578 close(file);
579 return true;
580 }
581}
582
James Feist9eb0b582018-04-27 12:15:46 -0700583void rescanBusses(
James Feist6ebf9de2018-05-15 15:01:17 -0700584 boost::asio::io_service &io, BusMap &busMap,
James Feist9eb0b582018-04-27 12:15:46 -0700585 boost::container::flat_map<std::pair<size_t, size_t>,
586 std::shared_ptr<sdbusplus::asio::dbus_interface>>
587 &dbusInterfaceMap,
James Feist6ebf9de2018-05-15 15:01:17 -0700588 std::shared_ptr<sdbusplus::asio::connection> &systemBus,
589 sdbusplus::asio::object_server &objServer)
James Feist918e18c2018-02-13 15:51:07 -0800590{
James Feist6ebf9de2018-05-15 15:01:17 -0700591 static boost::asio::deadline_timer timer(io);
592 timer.expires_from_now(boost::posix_time::seconds(1));
James Feist918e18c2018-02-13 15:51:07 -0800593
James Feist6ebf9de2018-05-15 15:01:17 -0700594 // setup an async wait incase we get flooded with requests
595 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist4131aea2018-03-09 09:47:30 -0800596 auto devDir = fs::path("/dev/");
597 auto matchString = std::string("i2c*");
598 std::vector<fs::path> i2cBuses;
James Feist918e18c2018-02-13 15:51:07 -0800599
James Feist4131aea2018-03-09 09:47:30 -0800600 if (!find_files(devDir, matchString, i2cBuses, 0))
James Feist918e18c2018-02-13 15:51:07 -0800601 {
James Feist4131aea2018-03-09 09:47:30 -0800602 std::cerr << "unable to find i2c devices\n";
603 return;
James Feist918e18c2018-02-13 15:51:07 -0800604 }
James Feist4131aea2018-03-09 09:47:30 -0800605 // scanning muxes in reverse order seems to have adverse effects
606 // sorting this list seems to be a fix for that
607 std::sort(i2cBuses.begin(), i2cBuses.end());
James Feist4131aea2018-03-09 09:47:30 -0800608
James Feist6ebf9de2018-05-15 15:01:17 -0700609 busMap.clear();
610 auto scan = std::make_shared<FindDevicesWithCallback>(
611 i2cBuses, io, busMap, [&]() {
612 for (auto &busIface : dbusInterfaceMap)
613 {
614 objServer.remove_interface(busIface.second);
615 }
James Feist4131aea2018-03-09 09:47:30 -0800616
James Feist6ebf9de2018-05-15 15:01:17 -0700617 dbusInterfaceMap.clear();
618 UNKNOWN_BUS_OBJECT_COUNT = 0;
James Feist4131aea2018-03-09 09:47:30 -0800619
James Feist6ebf9de2018-05-15 15:01:17 -0700620 // todo, get this from a more sensable place
621 std::vector<char> baseboardFru;
622 if (readBaseboardFru(baseboardFru))
623 {
624 boost::container::flat_map<int, std::vector<char>>
625 baseboardDev;
626 baseboardDev.emplace(0, baseboardFru);
627 busMap[0] = std::make_shared<DeviceMap>(baseboardDev);
628 }
629 for (auto &devicemap : busMap)
630 {
631 for (auto &device : *devicemap.second)
632 {
633 AddFruObjectToDbus(systemBus, device.second, objServer,
634 dbusInterfaceMap, devicemap.first,
635 device.first);
636 }
637 }
638 });
639 scan->run();
640 });
James Feist918e18c2018-02-13 15:51:07 -0800641}
642
James Feist3cb5fec2018-01-23 14:41:51 -0800643int main(int argc, char **argv)
644{
645 auto devDir = fs::path("/dev/");
646 auto matchString = std::string("i2c*");
647 std::vector<fs::path> i2cBuses;
648
649 if (!find_files(devDir, matchString, i2cBuses, 0))
650 {
651 std::cerr << "unable to find i2c devices\n";
652 return 1;
653 }
James Feist3cb5fec2018-01-23 14:41:51 -0800654
655 boost::asio::io_service io;
James Feist9eb0b582018-04-27 12:15:46 -0700656 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
657 auto objServer = sdbusplus::asio::object_server(systemBus);
James Feist3cb5fec2018-01-23 14:41:51 -0800658 systemBus->request_name("com.intel.FruDevice");
659
James Feist6ebf9de2018-05-15 15:01:17 -0700660 // this is a map with keys of pair(bus number, address) and values of
661 // the object on dbus
James Feist3cb5fec2018-01-23 14:41:51 -0800662 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700663 std::shared_ptr<sdbusplus::asio::dbus_interface>>
664 dbusInterfaceMap;
James Feist2a9d6db2018-04-27 15:48:28 -0700665 BusMap busmap;
James Feist3cb5fec2018-01-23 14:41:51 -0800666
James Feist9eb0b582018-04-27 12:15:46 -0700667 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
668 objServer.add_interface("/xyz/openbmc_project/FruDevice",
669 "xyz.openbmc_project.FruDeviceManager");
James Feist3cb5fec2018-01-23 14:41:51 -0800670
671 iface->register_method("ReScan", [&]() {
James Feist6ebf9de2018-05-15 15:01:17 -0700672 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
James Feist3cb5fec2018-01-23 14:41:51 -0800673 });
James Feist2a9d6db2018-04-27 15:48:28 -0700674
675 iface->register_method(
676 "GetRawFru", [&](const uint8_t &bus, const uint8_t &address) {
677 auto deviceMap = busmap.find(bus);
678 if (deviceMap == busmap.end())
679 {
680 throw sdbusplus::xyz::openbmc_project::Common::Error::
681 InvalidArgument();
682 }
683 auto device = deviceMap->second->find(address);
684 if (device == deviceMap->second->end())
685 {
686 throw sdbusplus::xyz::openbmc_project::Common::Error::
687 InvalidArgument();
688 }
689 std::vector<uint8_t> &ret =
690 reinterpret_cast<std::vector<uint8_t> &>(device->second);
691 return ret;
692 });
James Feistb49ffc32018-05-02 11:10:43 -0700693
694 iface->register_method("WriteFru", [&](const uint8_t bus,
695 const uint8_t address,
696 const std::vector<uint8_t> &data) {
697 if (!writeFru(bus, address, data))
698 {
699 throw sdbusplus::xyz::openbmc_project::Common::Error::
700 InvalidArgument();
701 return;
702 }
703 // schedule rescan on success
704 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
705
706 });
James Feist9eb0b582018-04-27 12:15:46 -0700707 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800708
James Feist9eb0b582018-04-27 12:15:46 -0700709 std::function<void(sdbusplus::message::message & message)> eventHandler =
710 [&](sdbusplus::message::message &message) {
James Feist918e18c2018-02-13 15:51:07 -0800711 std::string objectName;
James Feist9eb0b582018-04-27 12:15:46 -0700712 boost::container::flat_map<
713 std::string, sdbusplus::message::variant<
714 std::string, bool, int64_t, uint64_t, double>>
715 values;
716 message.read(objectName, values);
James Feist918e18c2018-02-13 15:51:07 -0800717 auto findPgood = values.find("pgood");
718 if (findPgood != values.end())
719 {
James Feist6ebf9de2018-05-15 15:01:17 -0700720
721 rescanBusses(io, busmap, dbusInterfaceMap, systemBus,
722 objServer);
James Feist918e18c2018-02-13 15:51:07 -0800723 }
James Feist918e18c2018-02-13 15:51:07 -0800724 };
James Feist9eb0b582018-04-27 12:15:46 -0700725
726 sdbusplus::bus::match::match powerMatch = sdbusplus::bus::match::match(
727 static_cast<sdbusplus::bus::bus &>(*systemBus),
728 "type='signal',interface='org.freedesktop.DBus.Properties',path_"
Kuiying Wang1dc43102018-05-09 15:09:29 +0800729 "namespace='/xyz/openbmc_project/Chassis/Control/"
730 "power0',arg0='xyz.openbmc_project.Chassis.Control.Power'",
James Feist9eb0b582018-04-27 12:15:46 -0700731 eventHandler);
732
James Feist4131aea2018-03-09 09:47:30 -0800733 int fd = inotify_init();
734 int wd = inotify_add_watch(fd, I2C_DEV_LOCATION,
735 IN_CREATE | IN_MOVED_TO | IN_DELETE);
736 std::array<char, 4096> readBuffer;
737 std::string pendingBuffer;
738 // monitor for new i2c devices
739 boost::asio::posix::stream_descriptor dirWatch(io, fd);
740 std::function<void(const boost::system::error_code, std::size_t)>
741 watchI2cBusses = [&](const boost::system::error_code &ec,
742 std::size_t bytes_transferred) {
743 if (ec)
744 {
745 std::cout << "Callback Error " << ec << "\n";
746 return;
747 }
748 pendingBuffer += std::string(readBuffer.data(), bytes_transferred);
749 bool devChange = false;
750 while (pendingBuffer.size() > sizeof(inotify_event))
751 {
752 const inotify_event *iEvent =
753 reinterpret_cast<const inotify_event *>(
754 pendingBuffer.data());
755 switch (iEvent->mask)
756 {
James Feist9eb0b582018-04-27 12:15:46 -0700757 case IN_CREATE:
758 case IN_MOVED_TO:
759 case IN_DELETE:
760 if (boost::starts_with(std::string(iEvent->name),
761 "i2c"))
762 {
763 devChange = true;
764 }
James Feist4131aea2018-03-09 09:47:30 -0800765 }
766
767 pendingBuffer.erase(0, sizeof(inotify_event) + iEvent->len);
768 }
James Feist6ebf9de2018-05-15 15:01:17 -0700769 if (devChange)
James Feist4131aea2018-03-09 09:47:30 -0800770 {
James Feist6ebf9de2018-05-15 15:01:17 -0700771 rescanBusses(io, busmap, dbusInterfaceMap, systemBus,
772 objServer);
James Feist4131aea2018-03-09 09:47:30 -0800773 }
James Feist6ebf9de2018-05-15 15:01:17 -0700774
James Feist4131aea2018-03-09 09:47:30 -0800775 dirWatch.async_read_some(boost::asio::buffer(readBuffer),
776 watchI2cBusses);
777 };
778
779 dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses);
Gunnar Millsb3e42fe2018-06-13 15:48:27 -0500780 // run the initial scan
James Feist6ebf9de2018-05-15 15:01:17 -0700781 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
James Feist918e18c2018-02-13 15:51:07 -0800782
James Feist3cb5fec2018-01-23 14:41:51 -0800783 io.run();
784 return 0;
785}