blob: ef1eee15cc079406434051f7b4c56de8e242bcb2 [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 Feist2a9d6db2018-04-27 15:48:28 -070031#include <xyz/openbmc_project/Common/error.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080032
33namespace fs = std::experimental::filesystem;
34static constexpr bool DEBUG = false;
35static size_t UNKNOWN_BUS_OBJECT_COUNT = 0;
36
37const static constexpr char *BASEBOARD_FRU_LOCATION =
38 "/etc/fru/baseboard.fru.bin";
39
James Feist4131aea2018-03-09 09:47:30 -080040const static constexpr char *I2C_DEV_LOCATION = "/dev";
41
James Feist3cb5fec2018-01-23 14:41:51 -080042static constexpr std::array<const char *, 5> FRU_AREAS = {
43 "INTERNAL", "CHASSIS", "BOARD", "PRODUCT", "MULTIRECORD"};
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -070044const static std::regex NON_ASCII_REGEX("[^\x01-\x7f]");
James Feist3cb5fec2018-01-23 14:41:51 -080045using DeviceMap = boost::container::flat_map<int, std::vector<char>>;
46using BusMap = boost::container::flat_map<int, std::shared_ptr<DeviceMap>>;
47
James Feistc95cb142018-02-26 10:41:42 -080048static bool isMuxBus(size_t bus)
49{
50 return is_symlink(std::experimental::filesystem::path(
51 "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device"));
52}
53
James Feist3cb5fec2018-01-23 14:41:51 -080054int get_bus_frus(int file, int first, int last, int bus,
55 std::shared_ptr<DeviceMap> devices)
56{
57 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> block_data;
58
59 for (int ii = first; ii <= last; ii++)
60 {
61 // Set slave address
62 if (ioctl(file, I2C_SLAVE_FORCE, ii) < 0)
63 {
64 std::cerr << "device at bus " << bus << "register " << ii
65 << "busy\n";
66 continue;
67 }
68 // probe
69 else if (i2c_smbus_read_byte(file) < 0)
70 {
71 continue;
72 }
73
74 if (DEBUG)
75 {
76 std::cout << "something at bus " << bus << "addr " << ii << "\n";
77 }
78 if (i2c_smbus_read_i2c_block_data(file, 0x0, 0x8, block_data.data()) <
79 0)
80 {
81 std::cerr << "failed to read bus " << bus << " address " << ii
82 << "\n";
83 continue;
84 }
85 size_t sum = 0;
86 for (int jj = 0; jj < 7; jj++)
87 {
88 sum += block_data[jj];
89 }
90 sum = (256 - sum) & 0xFF;
91
92 // check the header checksum
93 if (sum == block_data[7])
94 {
95 std::vector<char> device;
96 device.insert(device.end(), block_data.begin(),
97 block_data.begin() + 8);
98
99 for (int jj = 1; jj <= FRU_AREAS.size(); jj++)
100 {
101 auto area_offset = device[jj];
102 if (area_offset != 0)
103 {
104 area_offset *= 8;
105 if (i2c_smbus_read_i2c_block_data(file, area_offset, 0x8,
106 block_data.data()) < 0)
107 {
108 std::cerr << "failed to read bus " << bus << " address "
109 << ii << "\n";
110 return -1;
111 }
112 int length = block_data[1] * 8;
113 device.insert(device.end(), block_data.begin(),
114 block_data.begin() + 8);
115 length -= 8;
116 area_offset += 8;
117
118 while (length > 0)
119 {
120 auto to_get = std::min(0x20, length);
121 if (i2c_smbus_read_i2c_block_data(
122 file, area_offset, to_get, block_data.data()) <
123 0)
124 {
125 std::cerr << "failed to read bus " << bus
126 << " address " << ii << "\n";
127 return -1;
128 }
129 device.insert(device.end(), block_data.begin(),
130 block_data.begin() + to_get);
131 area_offset += to_get;
132 length -= to_get;
133 }
134 }
135 }
136 (*devices).emplace(ii, device);
137 }
138 }
139
140 return 0;
141}
142
143static BusMap FindI2CDevices(const std::vector<fs::path> &i2cBuses)
144{
James Feist918e18c2018-02-13 15:51:07 -0800145 std::vector<std::future<void>> futures;
James Feist3cb5fec2018-01-23 14:41:51 -0800146 BusMap busMap;
147 for (auto &i2cBus : i2cBuses)
148 {
149 auto busnum = i2cBus.string();
150 auto lastDash = busnum.rfind(std::string("-"));
151 // delete everything before dash inclusive
152 if (lastDash != std::string::npos)
153 {
154 busnum.erase(0, lastDash + 1);
155 }
156 auto bus = std::stoi(busnum);
157
158 auto file = open(i2cBus.c_str(), O_RDWR);
159 if (file < 0)
160 {
161 std::cerr << "unable to open i2c device " << i2cBus.string()
162 << "\n";
163 continue;
164 }
165 unsigned long funcs = 0;
166
167 if (ioctl(file, I2C_FUNCS, &funcs) < 0)
168 {
169 std::cerr
170 << "Error: Could not get the adapter functionality matrix bus"
171 << bus << "\n";
172 continue;
173 }
174 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE) ||
175 !(I2C_FUNC_SMBUS_READ_I2C_BLOCK))
176 {
177 std::cerr << "Error: Can't use SMBus Receive Byte command bus "
178 << bus << "\n";
179 continue;
180 }
181 auto &device = busMap[bus];
182 device = std::make_shared<DeviceMap>();
183
James Feistc95cb142018-02-26 10:41:42 -0800184 // don't scan muxed buses async as don't want to confuse the mux
185 if (isMuxBus(bus))
186 {
187 get_bus_frus(file, 0x03, 0x77, bus, device);
188 close(file);
189 }
190 else
191 {
192 // todo: call with boost asio?
193 futures.emplace_back(
194 std::async(std::launch::async, [file, device, bus] {
195 // i2cdetect by default uses the range 0x03 to 0x77, as
196 // this is
197 // what we
198 // have tested with, use this range. Could be changed in
199 // future.
200 get_bus_frus(file, 0x03, 0x77, bus, device);
201 close(file);
202 }));
203 }
James Feist3cb5fec2018-01-23 14:41:51 -0800204 }
205 for (auto &fut : futures)
206 {
207 fut.get(); // wait for all scans
208 }
209 return busMap;
210}
211
212static const std::tm intelEpoch(void)
213{
214 std::tm val = {0};
215 val.tm_year = 1996 - 1900;
216 return val;
217}
218
219bool formatFru(const std::vector<char> &fruBytes,
220 boost::container::flat_map<std::string, std::string> &result)
221{
222 static const std::vector<const char *> CHASSIS_FRU_AREAS = {
223 "PART_NUMBER", "SERIAL_NUMBER", "CHASSIS_INFO_AM1", "CHASSIS_INFO_AM2"};
224
225 static const std::vector<const char *> BOARD_FRU_AREAS = {
226 "MANUFACTURER", "PRODUCT_NAME", "SERIAL_NUMBER", "PART_NUMBER",
227 "VERSION_ID"};
228
229 static const std::vector<const char *> PRODUCT_FRU_AREAS = {
230 "MANUFACTURER", "PRODUCT_NAME", "PART_NUMBER",
231 "PRODUCT_VERSION", "PRODUCT_SERIAL_NUMBER", "ASSET_TAG"};
232
233 size_t sum = 0;
234
235 if (fruBytes.size() < 8)
236 {
237 return false;
238 }
239 std::vector<char>::const_iterator fruAreaOffsetField = fruBytes.begin();
James Feist9eb0b582018-04-27 12:15:46 -0700240 result["Common_Format_Version"] =
James Feist3cb5fec2018-01-23 14:41:51 -0800241 std::to_string(static_cast<int>(*fruAreaOffsetField));
242
243 const std::vector<const char *> *fieldData;
244
245 for (auto &area : FRU_AREAS)
246 {
247 fruAreaOffsetField++;
248 if (fruAreaOffsetField >= fruBytes.end())
249 {
250 return false;
251 }
252 size_t offset = (*fruAreaOffsetField) * 8;
253
254 if (offset > 1)
255 {
256 // +2 to skip format and length
257 std::vector<char>::const_iterator fruBytesIter =
258 fruBytes.begin() + offset + 2;
259
260 if (fruBytesIter >= fruBytes.end())
261 {
262 return false;
263 }
264
265 if (area == "CHASSIS")
266 {
267 result["CHASSIS_TYPE"] =
268 std::to_string(static_cast<int>(*fruBytesIter));
269 fruBytesIter += 1;
270 fieldData = &CHASSIS_FRU_AREAS;
271 }
272 else if (area == "BOARD")
273 {
274 result["BOARD_LANGUAGE_CODE"] =
275 std::to_string(static_cast<int>(*fruBytesIter));
276 fruBytesIter += 1;
277 if (fruBytesIter >= fruBytes.end())
278 {
279 return false;
280 }
281
282 unsigned int minutes = *fruBytesIter |
283 *(fruBytesIter + 1) << 8 |
284 *(fruBytesIter + 2) << 16;
285 std::tm fruTime = intelEpoch();
286 time_t timeValue = mktime(&fruTime);
287 timeValue += minutes * 60;
288 fruTime = *gmtime(&timeValue);
289
290 result["BOARD_MANUFACTURE_DATE"] = asctime(&fruTime);
291 result["BOARD_MANUFACTURE_DATE"]
292 .pop_back(); // remove trailing null
293 fruBytesIter += 3;
294 fieldData = &BOARD_FRU_AREAS;
295 }
296 else if (area == "PRODUCT")
297 {
298 result["PRODUCT_LANGUAGE_CODE"] =
299 std::to_string(static_cast<int>(*fruBytesIter));
300 fruBytesIter += 1;
301 fieldData = &PRODUCT_FRU_AREAS;
302 }
303 else
304 {
305 continue;
306 }
307 for (auto &field : *fieldData)
308 {
309 if (fruBytesIter >= fruBytes.end())
310 {
311 return false;
312 }
313
314 size_t length = *fruBytesIter & 0x3f;
315 fruBytesIter += 1;
316
317 if (fruBytesIter >= fruBytes.end())
318 {
319 return false;
320 }
321
322 result[std::string(area) + "_" + field] =
323 std::string(fruBytesIter, fruBytesIter + length);
324 fruBytesIter += length;
325 if (fruBytesIter >= fruBytes.end())
326 {
327 std::cerr << "Warning Fru Length Mismatch:\n ";
328 for (auto &c : fruBytes)
329 {
330 std::cerr << c;
331 }
332 std::cerr << "\n";
333 if (DEBUG)
334 {
335 for (auto &keyPair : result)
336 {
337 std::cerr << keyPair.first << " : "
338 << keyPair.second << "\n";
339 }
340 }
341 return false;
342 }
343 }
344 }
345 }
346
347 return true;
348}
349
350void AddFruObjectToDbus(
James Feist9eb0b582018-04-27 12:15:46 -0700351 std::shared_ptr<sdbusplus::asio::connection> dbusConn,
352 std::vector<char> &device, sdbusplus::asio::object_server &objServer,
James Feist3cb5fec2018-01-23 14:41:51 -0800353 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700354 std::shared_ptr<sdbusplus::asio::dbus_interface>>
355 &dbusInterfaceMap,
James Feist3cb5fec2018-01-23 14:41:51 -0800356 int bus, size_t address)
357{
358 boost::container::flat_map<std::string, std::string> formattedFru;
359 if (!formatFru(device, formattedFru))
360 {
361 std::cerr << "failed to format fru for device at bus " << std::hex
362 << bus << "address " << address << "\n";
363 return;
364 }
365 auto productNameFind = formattedFru.find("BOARD_PRODUCT_NAME");
366 std::string productName;
367 if (productNameFind == formattedFru.end())
368 {
369 productNameFind = formattedFru.find("PRODUCT_PRODUCT_NAME");
370 }
371 if (productNameFind != formattedFru.end())
372 {
373 productName = productNameFind->second;
James Feist3f8a2782018-02-12 09:24:42 -0800374 std::regex illegalObject("[^A-Za-z0-9_]");
375 productName = std::regex_replace(productName, illegalObject, "_");
James Feist3cb5fec2018-01-23 14:41:51 -0800376 }
377 else
378 {
379 productName = "UNKNOWN" + std::to_string(UNKNOWN_BUS_OBJECT_COUNT);
380 UNKNOWN_BUS_OBJECT_COUNT++;
381 }
382
James Feist918e18c2018-02-13 15:51:07 -0800383 productName = "/xyz/openbmc_project/FruDevice/" + productName;
James Feist918e18c2018-02-13 15:51:07 -0800384 // avoid duplicates by checking to see if on a mux
James Feist79e9c0b2018-03-15 15:21:17 -0700385 if (bus > 0)
James Feist918e18c2018-02-13 15:51:07 -0800386 {
James Feist79e9c0b2018-03-15 15:21:17 -0700387 size_t index = 0;
James Feist9eb0b582018-04-27 12:15:46 -0700388 for (auto const &busIface : dbusInterfaceMap)
James Feist918e18c2018-02-13 15:51:07 -0800389 {
James Feist9eb0b582018-04-27 12:15:46 -0700390 if ((busIface.second->get_object_path() == productName))
James Feist918e18c2018-02-13 15:51:07 -0800391 {
James Feist9eb0b582018-04-27 12:15:46 -0700392 if (isMuxBus(bus) && address == busIface.first.second)
James Feist79e9c0b2018-03-15 15:21:17 -0700393 {
394 continue;
395 }
396 // add underscore _index for the same object path on dbus
397 std::string strIndex = std::to_string(index);
398 if (index > 0)
399 {
400 productName.substr(0, productName.size() - strIndex.size());
401 }
402 else
403 {
404 productName += "_";
405 }
406 productName += std::to_string(index++);
James Feist918e18c2018-02-13 15:51:07 -0800407 }
408 }
409 }
James Feist3cb5fec2018-01-23 14:41:51 -0800410
James Feist9eb0b582018-04-27 12:15:46 -0700411 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
412 objServer.add_interface(productName, "xyz.openbmc_project.FruDevice");
413 dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface;
414
James Feist3cb5fec2018-01-23 14:41:51 -0800415 for (auto &property : formattedFru)
416 {
James Feist9eb0b582018-04-27 12:15:46 -0700417
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700418 std::regex_replace(property.second.begin(), property.second.begin(),
419 property.second.end(), NON_ASCII_REGEX, "_");
James Feist9eb0b582018-04-27 12:15:46 -0700420 if (property.second.empty())
421 {
422 continue;
423 }
424 std::string key =
425 std::regex_replace(property.first, NON_ASCII_REGEX, "_");
426 if (!iface->register_property(key, property.second + '\0'))
427 {
428 std::cerr << "illegal key: " << key << "\n";
429 }
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700430 if (DEBUG)
431 {
432 std::cout << property.first << ": " << property.second << "\n";
433 }
James Feist3cb5fec2018-01-23 14:41:51 -0800434 }
James Feist2a9d6db2018-04-27 15:48:28 -0700435
436 // baseboard will be 0, 0
437 std::stringstream data;
438 data << "0x" << std::hex << bus;
439 iface->register_property("BUS", data.str());
440 data.str("");
441 data << "0x" << std::hex << address;
442 iface->register_property("ADDRESS", data.str());
443
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(
James Feist2a9d6db2018-04-27 15:48:28 -0700467 BusMap &busMap,
James Feist9eb0b582018-04-27 12:15:46 -0700468 boost::container::flat_map<std::pair<size_t, size_t>,
469 std::shared_ptr<sdbusplus::asio::dbus_interface>>
470 &dbusInterfaceMap,
471 std::shared_ptr<sdbusplus::asio::connection> systemBus,
472 sdbusplus::asio::object_server &objServer,
473 std::atomic_bool &pendingCallback)
James Feist918e18c2018-02-13 15:51:07 -0800474{
James Feist918e18c2018-02-13 15:51:07 -0800475
James Feist4131aea2018-03-09 09:47:30 -0800476 do
James Feist918e18c2018-02-13 15:51:07 -0800477 {
James Feist4131aea2018-03-09 09:47:30 -0800478 auto devDir = fs::path("/dev/");
479 auto matchString = std::string("i2c*");
480 std::vector<fs::path> i2cBuses;
481 pendingCallback = false;
James Feist918e18c2018-02-13 15:51:07 -0800482
James Feist4131aea2018-03-09 09:47:30 -0800483 if (!find_files(devDir, matchString, i2cBuses, 0))
James Feist918e18c2018-02-13 15:51:07 -0800484 {
James Feist4131aea2018-03-09 09:47:30 -0800485 std::cerr << "unable to find i2c devices\n";
486 return;
James Feist918e18c2018-02-13 15:51:07 -0800487 }
James Feist4131aea2018-03-09 09:47:30 -0800488 // scanning muxes in reverse order seems to have adverse effects
489 // sorting this list seems to be a fix for that
490 std::sort(i2cBuses.begin(), i2cBuses.end());
James Feist2a9d6db2018-04-27 15:48:28 -0700491 busMap = FindI2CDevices(i2cBuses);
James Feist4131aea2018-03-09 09:47:30 -0800492
James Feist9eb0b582018-04-27 12:15:46 -0700493 for (auto &busIface : dbusInterfaceMap)
James Feist4131aea2018-03-09 09:47:30 -0800494 {
James Feist9eb0b582018-04-27 12:15:46 -0700495 objServer.remove_interface(busIface.second);
James Feist4131aea2018-03-09 09:47:30 -0800496 }
497
James Feist9eb0b582018-04-27 12:15:46 -0700498 dbusInterfaceMap.clear();
James Feist4131aea2018-03-09 09:47:30 -0800499 UNKNOWN_BUS_OBJECT_COUNT = 0;
500
James Feist2a9d6db2018-04-27 15:48:28 -0700501 // todo, get this from a more sensable place
502 std::vector<char> baseboardFru;
503 if (readBaseboardFru(baseboardFru))
504 {
505 boost::container::flat_map<int, std::vector<char>> baseboardDev;
506 baseboardDev.emplace(0, baseboardFru);
507 busMap[0] = std::make_shared<DeviceMap>(baseboardDev);
508 }
James Feist4131aea2018-03-09 09:47:30 -0800509 for (auto &devicemap : busMap)
510 {
511 for (auto &device : *devicemap.second)
512 {
513 AddFruObjectToDbus(systemBus, device.second, objServer,
James Feist9eb0b582018-04-27 12:15:46 -0700514 dbusInterfaceMap, devicemap.first,
James Feist4131aea2018-03-09 09:47:30 -0800515 device.first);
516 }
517 }
James Feist4131aea2018-03-09 09:47:30 -0800518 } while (pendingCallback);
James Feist918e18c2018-02-13 15:51:07 -0800519}
520
James Feist3cb5fec2018-01-23 14:41:51 -0800521int main(int argc, char **argv)
522{
523 auto devDir = fs::path("/dev/");
524 auto matchString = std::string("i2c*");
525 std::vector<fs::path> i2cBuses;
526
527 if (!find_files(devDir, matchString, i2cBuses, 0))
528 {
529 std::cerr << "unable to find i2c devices\n";
530 return 1;
531 }
James Feist3cb5fec2018-01-23 14:41:51 -0800532
533 boost::asio::io_service io;
James Feist9eb0b582018-04-27 12:15:46 -0700534 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
535 auto objServer = sdbusplus::asio::object_server(systemBus);
James Feist3cb5fec2018-01-23 14:41:51 -0800536 systemBus->request_name("com.intel.FruDevice");
537
Gunnar Millsb3e42fe2018-06-13 15:48:27 -0500538 // this is a map with keys of pair(bus number, address) and values of the
James Feist3cb5fec2018-01-23 14:41:51 -0800539 // object on dbus
540 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700541 std::shared_ptr<sdbusplus::asio::dbus_interface>>
542 dbusInterfaceMap;
James Feist2a9d6db2018-04-27 15:48:28 -0700543 BusMap busmap;
James Feist3cb5fec2018-01-23 14:41:51 -0800544
James Feist9eb0b582018-04-27 12:15:46 -0700545 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
546 objServer.add_interface("/xyz/openbmc_project/FruDevice",
547 "xyz.openbmc_project.FruDeviceManager");
James Feist3cb5fec2018-01-23 14:41:51 -0800548
James Feist918e18c2018-02-13 15:51:07 -0800549 std::atomic_bool threadRunning(false);
James Feist4131aea2018-03-09 09:47:30 -0800550 std::atomic_bool pendingCallback(false);
James Feist918e18c2018-02-13 15:51:07 -0800551 std::future<void> future;
552
James Feist3cb5fec2018-01-23 14:41:51 -0800553 iface->register_method("ReScan", [&]() {
James Feist4131aea2018-03-09 09:47:30 -0800554 bool notRunning = false;
555 if (threadRunning.compare_exchange_strong(notRunning, true))
James Feist3cb5fec2018-01-23 14:41:51 -0800556 {
James Feist918e18c2018-02-13 15:51:07 -0800557 future = std::async(std::launch::async, [&] {
James Feist2a9d6db2018-04-27 15:48:28 -0700558 rescanBusses(busmap, dbusInterfaceMap, systemBus, objServer,
James Feist4131aea2018-03-09 09:47:30 -0800559 pendingCallback);
James Feist918e18c2018-02-13 15:51:07 -0800560 threadRunning = false;
561 });
James Feist3cb5fec2018-01-23 14:41:51 -0800562 }
James Feist4131aea2018-03-09 09:47:30 -0800563 else
564 {
565 pendingCallback = true;
566 }
James Feist9eb0b582018-04-27 12:15:46 -0700567 return;
James Feist3cb5fec2018-01-23 14:41:51 -0800568 });
James Feist2a9d6db2018-04-27 15:48:28 -0700569
570 iface->register_method(
571 "GetRawFru", [&](const uint8_t &bus, const uint8_t &address) {
572 auto deviceMap = busmap.find(bus);
573 if (deviceMap == busmap.end())
574 {
575 throw sdbusplus::xyz::openbmc_project::Common::Error::
576 InvalidArgument();
577 }
578 auto device = deviceMap->second->find(address);
579 if (device == deviceMap->second->end())
580 {
581 throw sdbusplus::xyz::openbmc_project::Common::Error::
582 InvalidArgument();
583 }
584 std::vector<uint8_t> &ret =
585 reinterpret_cast<std::vector<uint8_t> &>(device->second);
586 return ret;
587 });
James Feist9eb0b582018-04-27 12:15:46 -0700588 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800589
James Feist9eb0b582018-04-27 12:15:46 -0700590 std::function<void(sdbusplus::message::message & message)> eventHandler =
591 [&](sdbusplus::message::message &message) {
James Feist918e18c2018-02-13 15:51:07 -0800592 std::string objectName;
James Feist9eb0b582018-04-27 12:15:46 -0700593 boost::container::flat_map<
594 std::string, sdbusplus::message::variant<
595 std::string, bool, int64_t, uint64_t, double>>
596 values;
597 message.read(objectName, values);
James Feist918e18c2018-02-13 15:51:07 -0800598 auto findPgood = values.find("pgood");
599 if (findPgood != values.end())
600 {
James Feist4131aea2018-03-09 09:47:30 -0800601 bool notRunning = false;
602 if (threadRunning.compare_exchange_strong(notRunning, true))
James Feist918e18c2018-02-13 15:51:07 -0800603 {
James Feist918e18c2018-02-13 15:51:07 -0800604 future = std::async(std::launch::async, [&] {
James Feist2a9d6db2018-04-27 15:48:28 -0700605 rescanBusses(busmap, dbusInterfaceMap, systemBus,
606 objServer, pendingCallback);
James Feist918e18c2018-02-13 15:51:07 -0800607 threadRunning = false;
608 });
609 }
James Feist4131aea2018-03-09 09:47:30 -0800610 else
611 {
612 pendingCallback = true;
613 }
James Feist918e18c2018-02-13 15:51:07 -0800614 }
James Feist918e18c2018-02-13 15:51:07 -0800615 };
James Feist9eb0b582018-04-27 12:15:46 -0700616
617 sdbusplus::bus::match::match powerMatch = sdbusplus::bus::match::match(
618 static_cast<sdbusplus::bus::bus &>(*systemBus),
619 "type='signal',interface='org.freedesktop.DBus.Properties',path_"
Kuiying Wang1dc43102018-05-09 15:09:29 +0800620 "namespace='/xyz/openbmc_project/Chassis/Control/"
621 "power0',arg0='xyz.openbmc_project.Chassis.Control.Power'",
James Feist9eb0b582018-04-27 12:15:46 -0700622 eventHandler);
623
James Feist4131aea2018-03-09 09:47:30 -0800624 int fd = inotify_init();
625 int wd = inotify_add_watch(fd, I2C_DEV_LOCATION,
626 IN_CREATE | IN_MOVED_TO | IN_DELETE);
627 std::array<char, 4096> readBuffer;
628 std::string pendingBuffer;
629 // monitor for new i2c devices
630 boost::asio::posix::stream_descriptor dirWatch(io, fd);
631 std::function<void(const boost::system::error_code, std::size_t)>
632 watchI2cBusses = [&](const boost::system::error_code &ec,
633 std::size_t bytes_transferred) {
634 if (ec)
635 {
636 std::cout << "Callback Error " << ec << "\n";
637 return;
638 }
639 pendingBuffer += std::string(readBuffer.data(), bytes_transferred);
640 bool devChange = false;
641 while (pendingBuffer.size() > sizeof(inotify_event))
642 {
643 const inotify_event *iEvent =
644 reinterpret_cast<const inotify_event *>(
645 pendingBuffer.data());
646 switch (iEvent->mask)
647 {
James Feist9eb0b582018-04-27 12:15:46 -0700648 case IN_CREATE:
649 case IN_MOVED_TO:
650 case IN_DELETE:
651 if (boost::starts_with(std::string(iEvent->name),
652 "i2c"))
653 {
654 devChange = true;
655 }
James Feist4131aea2018-03-09 09:47:30 -0800656 }
657
658 pendingBuffer.erase(0, sizeof(inotify_event) + iEvent->len);
659 }
660 bool notRunning = false;
661 if (devChange &&
662 threadRunning.compare_exchange_strong(notRunning, true))
663 {
664 future = std::async(std::launch::async, [&] {
665 std::this_thread::sleep_for(std::chrono::seconds(2));
James Feist2a9d6db2018-04-27 15:48:28 -0700666 rescanBusses(busmap, dbusInterfaceMap, systemBus, objServer,
James Feist4131aea2018-03-09 09:47:30 -0800667 pendingCallback);
668 threadRunning = false;
669 });
670 }
671 else if (devChange)
672 {
673 pendingCallback = true;
674 }
675 dirWatch.async_read_some(boost::asio::buffer(readBuffer),
676 watchI2cBusses);
677 };
678
679 dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses);
Gunnar Millsb3e42fe2018-06-13 15:48:27 -0500680 // run the initial scan
James Feist2a9d6db2018-04-27 15:48:28 -0700681 rescanBusses(busmap, dbusInterfaceMap, systemBus, objServer,
682 pendingCallback);
James Feist918e18c2018-02-13 15:51:07 -0800683
James Feist3cb5fec2018-01-23 14:41:51 -0800684 io.run();
685 return 0;
686}