blob: 781f7a1954a10f2e09b0667079df1e5c29daed15 [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>
19#include <ctime>
James Feist9eb0b582018-04-27 12:15:46 -070020#include <sdbusplus/asio/connection.hpp>
21#include <sdbusplus/asio/object_server.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080022#include <dbus/properties.hpp>
23#include <fcntl.h>
24#include <fstream>
25#include <future>
James Feistb5320a72018-01-24 12:28:12 -080026#include <linux/i2c-dev-user.h>
James Feist3cb5fec2018-01-23 14:41:51 -080027#include <iostream>
28#include <sys/ioctl.h>
James Feist3f8a2782018-02-12 09:24:42 -080029#include <regex>
James Feist4131aea2018-03-09 09:47:30 -080030#include <sys/inotify.h>
James Feist3cb5fec2018-01-23 14:41:51 -080031
32namespace fs = std::experimental::filesystem;
33static constexpr bool DEBUG = false;
34static size_t UNKNOWN_BUS_OBJECT_COUNT = 0;
35
36const static constexpr char *BASEBOARD_FRU_LOCATION =
37 "/etc/fru/baseboard.fru.bin";
38
James Feist4131aea2018-03-09 09:47:30 -080039const static constexpr char *I2C_DEV_LOCATION = "/dev";
40
James Feist3cb5fec2018-01-23 14:41:51 -080041static constexpr std::array<const char *, 5> FRU_AREAS = {
42 "INTERNAL", "CHASSIS", "BOARD", "PRODUCT", "MULTIRECORD"};
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -070043const static std::regex NON_ASCII_REGEX("[^\x01-\x7f]");
James Feist3cb5fec2018-01-23 14:41:51 -080044using DeviceMap = boost::container::flat_map<int, std::vector<char>>;
45using BusMap = boost::container::flat_map<int, std::shared_ptr<DeviceMap>>;
46
James Feistc95cb142018-02-26 10:41:42 -080047static bool isMuxBus(size_t bus)
48{
49 return is_symlink(std::experimental::filesystem::path(
50 "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device"));
51}
52
James Feist3cb5fec2018-01-23 14:41:51 -080053int get_bus_frus(int file, int first, int last, int bus,
54 std::shared_ptr<DeviceMap> devices)
55{
56 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> block_data;
57
58 for (int ii = first; ii <= last; ii++)
59 {
60 // Set slave address
61 if (ioctl(file, I2C_SLAVE_FORCE, ii) < 0)
62 {
63 std::cerr << "device at bus " << bus << "register " << ii
64 << "busy\n";
65 continue;
66 }
67 // probe
68 else if (i2c_smbus_read_byte(file) < 0)
69 {
70 continue;
71 }
72
73 if (DEBUG)
74 {
75 std::cout << "something at bus " << bus << "addr " << ii << "\n";
76 }
77 if (i2c_smbus_read_i2c_block_data(file, 0x0, 0x8, block_data.data()) <
78 0)
79 {
80 std::cerr << "failed to read bus " << bus << " address " << ii
81 << "\n";
82 continue;
83 }
84 size_t sum = 0;
85 for (int jj = 0; jj < 7; jj++)
86 {
87 sum += block_data[jj];
88 }
89 sum = (256 - sum) & 0xFF;
90
91 // check the header checksum
92 if (sum == block_data[7])
93 {
94 std::vector<char> device;
95 device.insert(device.end(), block_data.begin(),
96 block_data.begin() + 8);
97
98 for (int jj = 1; jj <= FRU_AREAS.size(); jj++)
99 {
100 auto area_offset = device[jj];
101 if (area_offset != 0)
102 {
103 area_offset *= 8;
104 if (i2c_smbus_read_i2c_block_data(file, area_offset, 0x8,
105 block_data.data()) < 0)
106 {
107 std::cerr << "failed to read bus " << bus << " address "
108 << ii << "\n";
109 return -1;
110 }
111 int length = block_data[1] * 8;
112 device.insert(device.end(), block_data.begin(),
113 block_data.begin() + 8);
114 length -= 8;
115 area_offset += 8;
116
117 while (length > 0)
118 {
119 auto to_get = std::min(0x20, length);
120 if (i2c_smbus_read_i2c_block_data(
121 file, area_offset, to_get, block_data.data()) <
122 0)
123 {
124 std::cerr << "failed to read bus " << bus
125 << " address " << ii << "\n";
126 return -1;
127 }
128 device.insert(device.end(), block_data.begin(),
129 block_data.begin() + to_get);
130 area_offset += to_get;
131 length -= to_get;
132 }
133 }
134 }
135 (*devices).emplace(ii, device);
136 }
137 }
138
139 return 0;
140}
141
142static BusMap FindI2CDevices(const std::vector<fs::path> &i2cBuses)
143{
James Feist918e18c2018-02-13 15:51:07 -0800144 std::vector<std::future<void>> futures;
James Feist3cb5fec2018-01-23 14:41:51 -0800145 BusMap busMap;
146 for (auto &i2cBus : i2cBuses)
147 {
148 auto busnum = i2cBus.string();
149 auto lastDash = busnum.rfind(std::string("-"));
150 // delete everything before dash inclusive
151 if (lastDash != std::string::npos)
152 {
153 busnum.erase(0, lastDash + 1);
154 }
155 auto bus = std::stoi(busnum);
156
157 auto file = open(i2cBus.c_str(), O_RDWR);
158 if (file < 0)
159 {
160 std::cerr << "unable to open i2c device " << i2cBus.string()
161 << "\n";
162 continue;
163 }
164 unsigned long funcs = 0;
165
166 if (ioctl(file, I2C_FUNCS, &funcs) < 0)
167 {
168 std::cerr
169 << "Error: Could not get the adapter functionality matrix bus"
170 << bus << "\n";
171 continue;
172 }
173 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE) ||
174 !(I2C_FUNC_SMBUS_READ_I2C_BLOCK))
175 {
176 std::cerr << "Error: Can't use SMBus Receive Byte command bus "
177 << bus << "\n";
178 continue;
179 }
180 auto &device = busMap[bus];
181 device = std::make_shared<DeviceMap>();
182
James Feistc95cb142018-02-26 10:41:42 -0800183 // don't scan muxed buses async as don't want to confuse the mux
184 if (isMuxBus(bus))
185 {
186 get_bus_frus(file, 0x03, 0x77, bus, device);
187 close(file);
188 }
189 else
190 {
191 // todo: call with boost asio?
192 futures.emplace_back(
193 std::async(std::launch::async, [file, device, bus] {
194 // i2cdetect by default uses the range 0x03 to 0x77, as
195 // this is
196 // what we
197 // have tested with, use this range. Could be changed in
198 // future.
199 get_bus_frus(file, 0x03, 0x77, bus, device);
200 close(file);
201 }));
202 }
James Feist3cb5fec2018-01-23 14:41:51 -0800203 }
204 for (auto &fut : futures)
205 {
206 fut.get(); // wait for all scans
207 }
208 return busMap;
209}
210
211static const std::tm intelEpoch(void)
212{
213 std::tm val = {0};
214 val.tm_year = 1996 - 1900;
215 return val;
216}
217
218bool formatFru(const std::vector<char> &fruBytes,
219 boost::container::flat_map<std::string, std::string> &result)
220{
221 static const std::vector<const char *> CHASSIS_FRU_AREAS = {
222 "PART_NUMBER", "SERIAL_NUMBER", "CHASSIS_INFO_AM1", "CHASSIS_INFO_AM2"};
223
224 static const std::vector<const char *> BOARD_FRU_AREAS = {
225 "MANUFACTURER", "PRODUCT_NAME", "SERIAL_NUMBER", "PART_NUMBER",
226 "VERSION_ID"};
227
228 static const std::vector<const char *> PRODUCT_FRU_AREAS = {
229 "MANUFACTURER", "PRODUCT_NAME", "PART_NUMBER",
230 "PRODUCT_VERSION", "PRODUCT_SERIAL_NUMBER", "ASSET_TAG"};
231
232 size_t sum = 0;
233
234 if (fruBytes.size() < 8)
235 {
236 return false;
237 }
238 std::vector<char>::const_iterator fruAreaOffsetField = fruBytes.begin();
James Feist9eb0b582018-04-27 12:15:46 -0700239 result["Common_Format_Version"] =
James Feist3cb5fec2018-01-23 14:41:51 -0800240 std::to_string(static_cast<int>(*fruAreaOffsetField));
241
242 const std::vector<const char *> *fieldData;
243
244 for (auto &area : FRU_AREAS)
245 {
246 fruAreaOffsetField++;
247 if (fruAreaOffsetField >= fruBytes.end())
248 {
249 return false;
250 }
251 size_t offset = (*fruAreaOffsetField) * 8;
252
253 if (offset > 1)
254 {
255 // +2 to skip format and length
256 std::vector<char>::const_iterator fruBytesIter =
257 fruBytes.begin() + offset + 2;
258
259 if (fruBytesIter >= fruBytes.end())
260 {
261 return false;
262 }
263
264 if (area == "CHASSIS")
265 {
266 result["CHASSIS_TYPE"] =
267 std::to_string(static_cast<int>(*fruBytesIter));
268 fruBytesIter += 1;
269 fieldData = &CHASSIS_FRU_AREAS;
270 }
271 else if (area == "BOARD")
272 {
273 result["BOARD_LANGUAGE_CODE"] =
274 std::to_string(static_cast<int>(*fruBytesIter));
275 fruBytesIter += 1;
276 if (fruBytesIter >= fruBytes.end())
277 {
278 return false;
279 }
280
281 unsigned int minutes = *fruBytesIter |
282 *(fruBytesIter + 1) << 8 |
283 *(fruBytesIter + 2) << 16;
284 std::tm fruTime = intelEpoch();
285 time_t timeValue = mktime(&fruTime);
286 timeValue += minutes * 60;
287 fruTime = *gmtime(&timeValue);
288
289 result["BOARD_MANUFACTURE_DATE"] = asctime(&fruTime);
290 result["BOARD_MANUFACTURE_DATE"]
291 .pop_back(); // remove trailing null
292 fruBytesIter += 3;
293 fieldData = &BOARD_FRU_AREAS;
294 }
295 else if (area == "PRODUCT")
296 {
297 result["PRODUCT_LANGUAGE_CODE"] =
298 std::to_string(static_cast<int>(*fruBytesIter));
299 fruBytesIter += 1;
300 fieldData = &PRODUCT_FRU_AREAS;
301 }
302 else
303 {
304 continue;
305 }
306 for (auto &field : *fieldData)
307 {
308 if (fruBytesIter >= fruBytes.end())
309 {
310 return false;
311 }
312
313 size_t length = *fruBytesIter & 0x3f;
314 fruBytesIter += 1;
315
316 if (fruBytesIter >= fruBytes.end())
317 {
318 return false;
319 }
320
321 result[std::string(area) + "_" + field] =
322 std::string(fruBytesIter, fruBytesIter + length);
323 fruBytesIter += length;
324 if (fruBytesIter >= fruBytes.end())
325 {
326 std::cerr << "Warning Fru Length Mismatch:\n ";
327 for (auto &c : fruBytes)
328 {
329 std::cerr << c;
330 }
331 std::cerr << "\n";
332 if (DEBUG)
333 {
334 for (auto &keyPair : result)
335 {
336 std::cerr << keyPair.first << " : "
337 << keyPair.second << "\n";
338 }
339 }
340 return false;
341 }
342 }
343 }
344 }
345
346 return true;
347}
348
349void AddFruObjectToDbus(
James Feist9eb0b582018-04-27 12:15:46 -0700350 std::shared_ptr<sdbusplus::asio::connection> dbusConn,
351 std::vector<char> &device, sdbusplus::asio::object_server &objServer,
James Feist3cb5fec2018-01-23 14:41:51 -0800352 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700353 std::shared_ptr<sdbusplus::asio::dbus_interface>>
354 &dbusInterfaceMap,
James Feist3cb5fec2018-01-23 14:41:51 -0800355 int bus, size_t address)
356{
357 boost::container::flat_map<std::string, std::string> formattedFru;
358 if (!formatFru(device, formattedFru))
359 {
360 std::cerr << "failed to format fru for device at bus " << std::hex
361 << bus << "address " << address << "\n";
362 return;
363 }
364 auto productNameFind = formattedFru.find("BOARD_PRODUCT_NAME");
365 std::string productName;
366 if (productNameFind == formattedFru.end())
367 {
368 productNameFind = formattedFru.find("PRODUCT_PRODUCT_NAME");
369 }
370 if (productNameFind != formattedFru.end())
371 {
372 productName = productNameFind->second;
James Feist3f8a2782018-02-12 09:24:42 -0800373 std::regex illegalObject("[^A-Za-z0-9_]");
374 productName = std::regex_replace(productName, illegalObject, "_");
James Feist3cb5fec2018-01-23 14:41:51 -0800375 }
376 else
377 {
378 productName = "UNKNOWN" + std::to_string(UNKNOWN_BUS_OBJECT_COUNT);
379 UNKNOWN_BUS_OBJECT_COUNT++;
380 }
381
James Feist918e18c2018-02-13 15:51:07 -0800382 productName = "/xyz/openbmc_project/FruDevice/" + productName;
James Feist918e18c2018-02-13 15:51:07 -0800383 // avoid duplicates by checking to see if on a mux
James Feist79e9c0b2018-03-15 15:21:17 -0700384 if (bus > 0)
James Feist918e18c2018-02-13 15:51:07 -0800385 {
James Feist79e9c0b2018-03-15 15:21:17 -0700386 size_t index = 0;
James Feist9eb0b582018-04-27 12:15:46 -0700387 for (auto const &busIface : dbusInterfaceMap)
James Feist918e18c2018-02-13 15:51:07 -0800388 {
James Feist9eb0b582018-04-27 12:15:46 -0700389 if ((busIface.second->get_object_path() == productName))
James Feist918e18c2018-02-13 15:51:07 -0800390 {
James Feist9eb0b582018-04-27 12:15:46 -0700391 if (isMuxBus(bus) && address == busIface.first.second)
James Feist79e9c0b2018-03-15 15:21:17 -0700392 {
393 continue;
394 }
395 // add underscore _index for the same object path on dbus
396 std::string strIndex = std::to_string(index);
397 if (index > 0)
398 {
399 productName.substr(0, productName.size() - strIndex.size());
400 }
401 else
402 {
403 productName += "_";
404 }
405 productName += std::to_string(index++);
James Feist918e18c2018-02-13 15:51:07 -0800406 }
407 }
408 }
James Feist3cb5fec2018-01-23 14:41:51 -0800409
James Feist9eb0b582018-04-27 12:15:46 -0700410 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
411 objServer.add_interface(productName, "xyz.openbmc_project.FruDevice");
412 dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface;
413
James Feist3cb5fec2018-01-23 14:41:51 -0800414 for (auto &property : formattedFru)
415 {
James Feist9eb0b582018-04-27 12:15:46 -0700416
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700417 std::regex_replace(property.second.begin(), property.second.begin(),
418 property.second.end(), NON_ASCII_REGEX, "_");
James Feist9eb0b582018-04-27 12:15:46 -0700419 if (property.second.empty())
420 {
421 continue;
422 }
423 std::string key =
424 std::regex_replace(property.first, NON_ASCII_REGEX, "_");
425 if (!iface->register_property(key, property.second + '\0'))
426 {
427 std::cerr << "illegal key: " << key << "\n";
428 }
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700429 if (DEBUG)
430 {
431 std::cout << property.first << ": " << property.second << "\n";
432 }
James Feist3cb5fec2018-01-23 14:41:51 -0800433 }
434 // baseboard can set this to -1 to not set a bus / address
435 if (bus > 0)
436 {
437 std::stringstream data;
438 data << "0x" << std::hex << bus;
James Feist9eb0b582018-04-27 12:15:46 -0700439 iface->register_property("BUS", data.str());
James Feist3cb5fec2018-01-23 14:41:51 -0800440 data.str("");
441 data << "0x" << std::hex << address;
James Feist9eb0b582018-04-27 12:15:46 -0700442 iface->register_property("ADDRESS", data.str());
James Feist3cb5fec2018-01-23 14:41:51 -0800443 }
James Feist9eb0b582018-04-27 12:15:46 -0700444 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800445}
446
447static bool readBaseboardFru(std::vector<char> &baseboardFru)
448{
449 // try to read baseboard fru from file
450 std::ifstream baseboardFruFile(BASEBOARD_FRU_LOCATION, std::ios::binary);
451 if (baseboardFruFile.good())
452 {
453 baseboardFruFile.seekg(0, std::ios_base::end);
454 std::streampos fileSize = baseboardFruFile.tellg();
455 baseboardFru.resize(fileSize);
456 baseboardFruFile.seekg(0, std::ios_base::beg);
457 baseboardFruFile.read(baseboardFru.data(), fileSize);
458 }
459 else
460 {
461 return false;
462 }
463 return true;
464}
465
James Feist9eb0b582018-04-27 12:15:46 -0700466void rescanBusses(
467 boost::container::flat_map<std::pair<size_t, size_t>,
468 std::shared_ptr<sdbusplus::asio::dbus_interface>>
469 &dbusInterfaceMap,
470 std::shared_ptr<sdbusplus::asio::connection> systemBus,
471 sdbusplus::asio::object_server &objServer,
472 std::atomic_bool &pendingCallback)
James Feist918e18c2018-02-13 15:51:07 -0800473{
James Feist918e18c2018-02-13 15:51:07 -0800474
James Feist4131aea2018-03-09 09:47:30 -0800475 do
James Feist918e18c2018-02-13 15:51:07 -0800476 {
James Feist4131aea2018-03-09 09:47:30 -0800477 auto devDir = fs::path("/dev/");
478 auto matchString = std::string("i2c*");
479 std::vector<fs::path> i2cBuses;
480 pendingCallback = false;
James Feist918e18c2018-02-13 15:51:07 -0800481
James Feist4131aea2018-03-09 09:47:30 -0800482 if (!find_files(devDir, matchString, i2cBuses, 0))
James Feist918e18c2018-02-13 15:51:07 -0800483 {
James Feist4131aea2018-03-09 09:47:30 -0800484 std::cerr << "unable to find i2c devices\n";
485 return;
James Feist918e18c2018-02-13 15:51:07 -0800486 }
James Feist4131aea2018-03-09 09:47:30 -0800487 // scanning muxes in reverse order seems to have adverse effects
488 // sorting this list seems to be a fix for that
489 std::sort(i2cBuses.begin(), i2cBuses.end());
490 BusMap busMap = FindI2CDevices(i2cBuses);
491
James Feist9eb0b582018-04-27 12:15:46 -0700492 for (auto &busIface : dbusInterfaceMap)
James Feist4131aea2018-03-09 09:47:30 -0800493 {
James Feist9eb0b582018-04-27 12:15:46 -0700494 objServer.remove_interface(busIface.second);
James Feist4131aea2018-03-09 09:47:30 -0800495 }
496
James Feist9eb0b582018-04-27 12:15:46 -0700497 dbusInterfaceMap.clear();
James Feist4131aea2018-03-09 09:47:30 -0800498 UNKNOWN_BUS_OBJECT_COUNT = 0;
499
500 for (auto &devicemap : busMap)
501 {
502 for (auto &device : *devicemap.second)
503 {
504 AddFruObjectToDbus(systemBus, device.second, objServer,
James Feist9eb0b582018-04-27 12:15:46 -0700505 dbusInterfaceMap, devicemap.first,
James Feist4131aea2018-03-09 09:47:30 -0800506 device.first);
507 }
508 }
509 // todo, get this from a more sensable place
510 std::vector<char> baseboardFru;
511 if (readBaseboardFru(baseboardFru))
512 {
513 AddFruObjectToDbus(systemBus, baseboardFru, objServer,
James Feist9eb0b582018-04-27 12:15:46 -0700514 dbusInterfaceMap, -1, -1);
James Feist4131aea2018-03-09 09:47:30 -0800515 }
516 } while (pendingCallback);
James Feist918e18c2018-02-13 15:51:07 -0800517}
518
James Feist3cb5fec2018-01-23 14:41:51 -0800519int main(int argc, char **argv)
520{
521 auto devDir = fs::path("/dev/");
522 auto matchString = std::string("i2c*");
523 std::vector<fs::path> i2cBuses;
524
525 if (!find_files(devDir, matchString, i2cBuses, 0))
526 {
527 std::cerr << "unable to find i2c devices\n";
528 return 1;
529 }
James Feist3cb5fec2018-01-23 14:41:51 -0800530
531 boost::asio::io_service io;
James Feist9eb0b582018-04-27 12:15:46 -0700532 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
533 auto objServer = sdbusplus::asio::object_server(systemBus);
James Feist3cb5fec2018-01-23 14:41:51 -0800534 systemBus->request_name("com.intel.FruDevice");
535
Gunnar Millsb3e42fe2018-06-13 15:48:27 -0500536 // this is a map with keys of pair(bus number, address) and values of the
James Feist3cb5fec2018-01-23 14:41:51 -0800537 // object on dbus
538 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700539 std::shared_ptr<sdbusplus::asio::dbus_interface>>
540 dbusInterfaceMap;
James Feist3cb5fec2018-01-23 14:41:51 -0800541
James Feist9eb0b582018-04-27 12:15:46 -0700542 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
543 objServer.add_interface("/xyz/openbmc_project/FruDevice",
544 "xyz.openbmc_project.FruDeviceManager");
James Feist3cb5fec2018-01-23 14:41:51 -0800545
James Feist918e18c2018-02-13 15:51:07 -0800546 std::atomic_bool threadRunning(false);
James Feist4131aea2018-03-09 09:47:30 -0800547 std::atomic_bool pendingCallback(false);
James Feist918e18c2018-02-13 15:51:07 -0800548 std::future<void> future;
549
James Feist3cb5fec2018-01-23 14:41:51 -0800550 iface->register_method("ReScan", [&]() {
James Feist4131aea2018-03-09 09:47:30 -0800551 bool notRunning = false;
552 if (threadRunning.compare_exchange_strong(notRunning, true))
James Feist3cb5fec2018-01-23 14:41:51 -0800553 {
James Feist918e18c2018-02-13 15:51:07 -0800554 future = std::async(std::launch::async, [&] {
James Feist9eb0b582018-04-27 12:15:46 -0700555 rescanBusses(dbusInterfaceMap, systemBus, objServer,
James Feist4131aea2018-03-09 09:47:30 -0800556 pendingCallback);
James Feist918e18c2018-02-13 15:51:07 -0800557 threadRunning = false;
558 });
James Feist3cb5fec2018-01-23 14:41:51 -0800559 }
James Feist4131aea2018-03-09 09:47:30 -0800560 else
561 {
562 pendingCallback = true;
563 }
James Feist9eb0b582018-04-27 12:15:46 -0700564 return;
James Feist3cb5fec2018-01-23 14:41:51 -0800565 });
James Feist9eb0b582018-04-27 12:15:46 -0700566 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800567
James Feist9eb0b582018-04-27 12:15:46 -0700568 std::function<void(sdbusplus::message::message & message)> eventHandler =
569 [&](sdbusplus::message::message &message) {
James Feist918e18c2018-02-13 15:51:07 -0800570 std::string objectName;
James Feist9eb0b582018-04-27 12:15:46 -0700571 boost::container::flat_map<
572 std::string, sdbusplus::message::variant<
573 std::string, bool, int64_t, uint64_t, double>>
574 values;
575 message.read(objectName, values);
James Feist918e18c2018-02-13 15:51:07 -0800576 auto findPgood = values.find("pgood");
577 if (findPgood != values.end())
578 {
James Feist4131aea2018-03-09 09:47:30 -0800579 bool notRunning = false;
580 if (threadRunning.compare_exchange_strong(notRunning, true))
James Feist918e18c2018-02-13 15:51:07 -0800581 {
James Feist918e18c2018-02-13 15:51:07 -0800582 future = std::async(std::launch::async, [&] {
James Feist9eb0b582018-04-27 12:15:46 -0700583 rescanBusses(dbusInterfaceMap, systemBus, objServer,
James Feist4131aea2018-03-09 09:47:30 -0800584 pendingCallback);
James Feist918e18c2018-02-13 15:51:07 -0800585 threadRunning = false;
586 });
587 }
James Feist4131aea2018-03-09 09:47:30 -0800588 else
589 {
590 pendingCallback = true;
591 }
James Feist918e18c2018-02-13 15:51:07 -0800592 }
James Feist918e18c2018-02-13 15:51:07 -0800593 };
James Feist9eb0b582018-04-27 12:15:46 -0700594
595 sdbusplus::bus::match::match powerMatch = sdbusplus::bus::match::match(
596 static_cast<sdbusplus::bus::bus &>(*systemBus),
597 "type='signal',interface='org.freedesktop.DBus.Properties',path_"
598 "namespace='/org/openbmc/control/"
599 "power0',arg0='org.openbmc.control.Power'",
600 eventHandler);
601
James Feist4131aea2018-03-09 09:47:30 -0800602 int fd = inotify_init();
603 int wd = inotify_add_watch(fd, I2C_DEV_LOCATION,
604 IN_CREATE | IN_MOVED_TO | IN_DELETE);
605 std::array<char, 4096> readBuffer;
606 std::string pendingBuffer;
607 // monitor for new i2c devices
608 boost::asio::posix::stream_descriptor dirWatch(io, fd);
609 std::function<void(const boost::system::error_code, std::size_t)>
610 watchI2cBusses = [&](const boost::system::error_code &ec,
611 std::size_t bytes_transferred) {
612 if (ec)
613 {
614 std::cout << "Callback Error " << ec << "\n";
615 return;
616 }
617 pendingBuffer += std::string(readBuffer.data(), bytes_transferred);
618 bool devChange = false;
619 while (pendingBuffer.size() > sizeof(inotify_event))
620 {
621 const inotify_event *iEvent =
622 reinterpret_cast<const inotify_event *>(
623 pendingBuffer.data());
624 switch (iEvent->mask)
625 {
James Feist9eb0b582018-04-27 12:15:46 -0700626 case IN_CREATE:
627 case IN_MOVED_TO:
628 case IN_DELETE:
629 if (boost::starts_with(std::string(iEvent->name),
630 "i2c"))
631 {
632 devChange = true;
633 }
James Feist4131aea2018-03-09 09:47:30 -0800634 }
635
636 pendingBuffer.erase(0, sizeof(inotify_event) + iEvent->len);
637 }
638 bool notRunning = false;
639 if (devChange &&
640 threadRunning.compare_exchange_strong(notRunning, true))
641 {
642 future = std::async(std::launch::async, [&] {
643 std::this_thread::sleep_for(std::chrono::seconds(2));
James Feist9eb0b582018-04-27 12:15:46 -0700644 rescanBusses(dbusInterfaceMap, systemBus, objServer,
James Feist4131aea2018-03-09 09:47:30 -0800645 pendingCallback);
646 threadRunning = false;
647 });
648 }
649 else if (devChange)
650 {
651 pendingCallback = true;
652 }
653 dirWatch.async_read_some(boost::asio::buffer(readBuffer),
654 watchI2cBusses);
655 };
656
657 dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses);
Gunnar Millsb3e42fe2018-06-13 15:48:27 -0500658 // run the initial scan
James Feist9eb0b582018-04-27 12:15:46 -0700659 rescanBusses(dbusInterfaceMap, systemBus, objServer, pendingCallback);
James Feist918e18c2018-02-13 15:51:07 -0800660
James Feist3cb5fec2018-01-23 14:41:51 -0800661 io.run();
662 return 0;
663}