blob: 1a389e5c92862494c93cf5eb680b78b32d0d2986 [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
James Feist3b860982018-10-02 14:34:07 -070017#include <errno.h>
James Feist3cb5fec2018-01-23 14:41:51 -080018#include <fcntl.h>
James Feist3b860982018-10-02 14:34:07 -070019#include <sys/inotify.h>
20#include <sys/ioctl.h>
21
22#include <Utils.hpp>
23#include <boost/algorithm/string/predicate.hpp>
24#include <boost/container/flat_map.hpp>
25#include <chrono>
26#include <ctime>
Patrick Venturee3754002019-08-06 09:39:12 -070027#include <filesystem>
James Feist3cb5fec2018-01-23 14:41:51 -080028#include <fstream>
29#include <future>
James Feist3cb5fec2018-01-23 14:41:51 -080030#include <iostream>
Patrick Venture11f1ff42019-08-01 10:42:12 -070031#include <nlohmann/json.hpp>
James Feist3f8a2782018-02-12 09:24:42 -080032#include <regex>
James Feist3b860982018-10-02 14:34:07 -070033#include <sdbusplus/asio/connection.hpp>
34#include <sdbusplus/asio/object_server.hpp>
Patrick Venture11f1ff42019-08-01 10:42:12 -070035#include <string>
James Feist3b860982018-10-02 14:34:07 -070036#include <thread>
James Feista465ccc2019-02-08 12:51:01 -080037#include <variant>
James Feist3b860982018-10-02 14:34:07 -070038
39extern "C" {
40#include <i2c/smbus.h>
41#include <linux/i2c-dev.h>
42}
James Feist3cb5fec2018-01-23 14:41:51 -080043
Ed Tanous072e25d2018-12-16 21:45:20 -080044namespace fs = std::filesystem;
James Feist3cb5fec2018-01-23 14:41:51 -080045static constexpr bool DEBUG = false;
46static size_t UNKNOWN_BUS_OBJECT_COUNT = 0;
James Feistb49ffc32018-05-02 11:10:43 -070047constexpr size_t MAX_FRU_SIZE = 512;
48constexpr size_t MAX_EEPROM_PAGE_INDEX = 255;
James Feist26c27ad2018-07-25 15:09:40 -070049constexpr size_t busTimeoutSeconds = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080050
Patrick Venture11f1ff42019-08-01 10:42:12 -070051constexpr const char* blacklistPath = PACKAGE_DIR "blacklist.json";
52
James Feista465ccc2019-02-08 12:51:01 -080053const static constexpr char* BASEBOARD_FRU_LOCATION =
James Feist3cb5fec2018-01-23 14:41:51 -080054 "/etc/fru/baseboard.fru.bin";
55
James Feista465ccc2019-02-08 12:51:01 -080056const static constexpr char* I2C_DEV_LOCATION = "/dev";
James Feist4131aea2018-03-09 09:47:30 -080057
James Feista465ccc2019-02-08 12:51:01 -080058static constexpr std::array<const char*, 5> FRU_AREAS = {
James Feist3cb5fec2018-01-23 14:41:51 -080059 "INTERNAL", "CHASSIS", "BOARD", "PRODUCT", "MULTIRECORD"};
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -070060const static std::regex NON_ASCII_REGEX("[^\x01-\x7f]");
James Feist3cb5fec2018-01-23 14:41:51 -080061using DeviceMap = boost::container::flat_map<int, std::vector<char>>;
62using BusMap = boost::container::flat_map<int, std::shared_ptr<DeviceMap>>;
63
James Feist444830e2019-04-05 08:38:16 -070064static std::set<size_t> busBlacklist;
James Feist6ebf9de2018-05-15 15:01:17 -070065struct FindDevicesWithCallback;
66
Nikhil Potaded8884f12019-03-27 13:27:13 -070067static BusMap busMap;
68
James Feistc95cb142018-02-26 10:41:42 -080069static bool isMuxBus(size_t bus)
70{
Ed Tanous072e25d2018-12-16 21:45:20 -080071 return is_symlink(std::filesystem::path(
James Feistc95cb142018-02-26 10:41:42 -080072 "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device"));
73}
74
Vijay Khemka2d681f62018-11-06 15:51:00 -080075static int isDevice16Bit(int file)
76{
77 /* Get first byte */
78 int byte1 = i2c_smbus_read_byte_data(file, 0);
79 if (byte1 < 0)
80 {
81 return byte1;
82 }
83 /* Read 7 more bytes, it will read same first byte in case of
84 * 8 bit but it will read next byte in case of 16 bit
85 */
86 for (int i = 0; i < 7; i++)
87 {
88 int byte2 = i2c_smbus_read_byte_data(file, 0);
89 if (byte2 < 0)
90 {
91 return byte2;
92 }
93 if (byte2 != byte1)
94 {
95 return 1;
96 }
97 }
98 return 0;
99}
100
101static int read_block_data(int flag, int file, uint16_t offset, uint8_t len,
James Feista465ccc2019-02-08 12:51:01 -0800102 uint8_t* buf)
Vijay Khemka2d681f62018-11-06 15:51:00 -0800103{
James Feist98132792019-07-09 13:29:09 -0700104 uint8_t low_addr = static_cast<uint8_t>(offset);
105 uint8_t high_addr = static_cast<uint8_t>(offset >> 8);
Vijay Khemka2d681f62018-11-06 15:51:00 -0800106
107 if (flag == 0)
108 {
109 return i2c_smbus_read_i2c_block_data(file, low_addr, len, buf);
110 }
111
112 /* This is for 16 bit addressing EEPROM device. First an offset
113 * needs to be written before read data from a offset
114 */
115 int ret = i2c_smbus_write_byte_data(file, 0, low_addr);
116 if (ret < 0)
117 {
118 return ret;
119 }
120
121 return i2c_smbus_read_i2c_block_data(file, high_addr, len, buf);
122}
123
James Feist24bae7a2019-04-03 09:50:56 -0700124bool validateHeader(const std::array<uint8_t, I2C_SMBUS_BLOCK_MAX>& blockData)
125{
126 // ipmi spec format version number is currently at 1, verify it
127 if (blockData[0] != 0x1)
128 {
129 return false;
130 }
131
132 // verify pad is set to 0
133 if (blockData[6] != 0x0)
134 {
135 return false;
136 }
137
138 // verify offsets are 0, or don't point to another offset
139 std::set<uint8_t> foundOffsets;
140 for (int ii = 1; ii < 6; ii++)
141 {
142 if (blockData[ii] == 0)
143 {
144 continue;
145 }
James Feist0eb40352019-04-09 14:44:04 -0700146 auto inserted = foundOffsets.insert(blockData[ii]);
147 if (!inserted.second)
James Feist24bae7a2019-04-03 09:50:56 -0700148 {
149 return false;
150 }
151 }
152
153 // validate checksum
154 size_t sum = 0;
155 for (int jj = 0; jj < 7; jj++)
156 {
157 sum += blockData[jj];
158 }
159 sum = (256 - sum) & 0xFF;
160
161 if (sum != blockData[7])
162 {
163 return false;
164 }
165 return true;
166}
167
James Feist3cb5fec2018-01-23 14:41:51 -0800168int get_bus_frus(int file, int first, int last, int bus,
169 std::shared_ptr<DeviceMap> devices)
170{
James Feist3cb5fec2018-01-23 14:41:51 -0800171
James Feist26c27ad2018-07-25 15:09:40 -0700172 std::future<int> future = std::async(std::launch::async, [&]() {
173 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> block_data;
Vijay Khemka2d681f62018-11-06 15:51:00 -0800174
James Feist26c27ad2018-07-25 15:09:40 -0700175 for (int ii = first; ii <= last; ii++)
James Feist3cb5fec2018-01-23 14:41:51 -0800176 {
James Feist3cb5fec2018-01-23 14:41:51 -0800177
James Feist26c27ad2018-07-25 15:09:40 -0700178 // Set slave address
179 if (ioctl(file, I2C_SLAVE_FORCE, ii) < 0)
James Feist3cb5fec2018-01-23 14:41:51 -0800180 {
Patrick Venture98e0cf32019-08-02 11:11:03 -0700181 std::cerr << "device at bus " << bus << " register " << ii
Patrick Venture5d8b61d2019-08-06 12:36:10 -0700182 << " busy\n";
James Feist26c27ad2018-07-25 15:09:40 -0700183 continue;
184 }
185 // probe
186 else if (i2c_smbus_read_byte(file) < 0)
187 {
188 continue;
189 }
James Feist3cb5fec2018-01-23 14:41:51 -0800190
James Feist26c27ad2018-07-25 15:09:40 -0700191 if (DEBUG)
192 {
Patrick Venture98e0cf32019-08-02 11:11:03 -0700193 std::cout << "something at bus " << bus << " addr " << ii
James Feist26c27ad2018-07-25 15:09:40 -0700194 << "\n";
195 }
Vijay Khemka2d681f62018-11-06 15:51:00 -0800196
197 /* Check for Device type if it is 8 bit or 16 bit */
198 int flag = isDevice16Bit(file);
199 if (flag < 0)
200 {
201 std::cerr << "failed to read bus " << bus << " address " << ii
202 << "\n";
203 continue;
204 }
205
206 if (read_block_data(flag, file, 0x0, 0x8, block_data.data()) < 0)
James Feist26c27ad2018-07-25 15:09:40 -0700207 {
208 std::cerr << "failed to read bus " << bus << " address " << ii
209 << "\n";
210 continue;
211 }
James Feist26c27ad2018-07-25 15:09:40 -0700212
213 // check the header checksum
Patrick Venture786f1792019-08-05 16:33:44 -0700214 if (!validateHeader(block_data))
James Feist26c27ad2018-07-25 15:09:40 -0700215 {
Patrick Venture786f1792019-08-05 16:33:44 -0700216 if (DEBUG)
James Feist26c27ad2018-07-25 15:09:40 -0700217 {
Patrick Venture786f1792019-08-05 16:33:44 -0700218 std::cerr << "Illegal header at bus " << bus << " address "
219 << ii << "\n";
220 }
221 continue;
222 }
223
224 std::vector<char> device;
225 device.insert(device.end(), block_data.begin(),
226 block_data.begin() + 8);
227
228 for (size_t jj = 1; jj <= FRU_AREAS.size(); jj++)
229 {
230 auto area_offset = device[jj];
Patrick Venture64fd7e22019-08-05 16:38:20 -0700231 if (area_offset == 0)
Patrick Venture786f1792019-08-05 16:33:44 -0700232 {
Patrick Venture64fd7e22019-08-05 16:38:20 -0700233 continue;
234 }
235
236 area_offset = static_cast<char>(area_offset * 8);
237 if (read_block_data(flag, file, area_offset, 0x8,
238 block_data.data()) < 0)
239 {
240 std::cerr << "failed to read bus " << bus << " address "
241 << ii << "\n";
242 return -1;
243 }
244 int length = block_data[1] * 8;
245 device.insert(device.end(), block_data.begin(),
246 block_data.begin() + 8);
247 length -= 8;
248 area_offset = static_cast<char>(area_offset + 8);
249
250 while (length > 0)
251 {
252 auto to_get = std::min(0x20, length);
253
254 if (read_block_data(flag, file, area_offset,
255 static_cast<uint8_t>(to_get),
Patrick Venture786f1792019-08-05 16:33:44 -0700256 block_data.data()) < 0)
James Feist3cb5fec2018-01-23 14:41:51 -0800257 {
Patrick Venture786f1792019-08-05 16:33:44 -0700258 std::cerr << "failed to read bus " << bus << " address "
259 << ii << "\n";
260 return -1;
261 }
Patrick Venture786f1792019-08-05 16:33:44 -0700262 device.insert(device.end(), block_data.begin(),
Patrick Venture64fd7e22019-08-05 16:38:20 -0700263 block_data.begin() + to_get);
264 area_offset = static_cast<char>(area_offset + to_get);
265 length -= to_get;
James Feist3cb5fec2018-01-23 14:41:51 -0800266 }
267 }
Patrick Venture786f1792019-08-05 16:33:44 -0700268 devices->emplace(ii, device);
James Feist3cb5fec2018-01-23 14:41:51 -0800269 }
James Feist26c27ad2018-07-25 15:09:40 -0700270 return 1;
271 });
272 std::future_status status =
273 future.wait_for(std::chrono::seconds(busTimeoutSeconds));
274 if (status == std::future_status::timeout)
275 {
276 std::cerr << "Error reading bus " << bus << "\n";
James Feist444830e2019-04-05 08:38:16 -0700277 busBlacklist.insert(bus);
278 close(file);
James Feist26c27ad2018-07-25 15:09:40 -0700279 return -1;
James Feist3cb5fec2018-01-23 14:41:51 -0800280 }
281
James Feist444830e2019-04-05 08:38:16 -0700282 close(file);
James Feist26c27ad2018-07-25 15:09:40 -0700283 return future.get();
James Feist3cb5fec2018-01-23 14:41:51 -0800284}
285
Patrick Venture11f1ff42019-08-01 10:42:12 -0700286void loadBlacklist(const char* path)
287{
288 std::ifstream blacklistStream(path);
289 if (!blacklistStream.good())
290 {
291 // File is optional.
292 std::cerr << "Cannot open blacklist file.\n\n";
293 return;
294 }
295
296 nlohmann::json data =
297 nlohmann::json::parse(blacklistStream, nullptr, false);
298 if (data.is_discarded())
299 {
300 std::cerr << "Illegal blacklist file detected, cannot validate JSON, "
301 "exiting\n";
302 std::exit(EXIT_FAILURE);
303 return;
304 }
305
306 // It's expected to have at least one field, "buses" that is an array of the
307 // buses by integer. Allow for future options to exclude further aspects,
308 // such as specific addresses or ranges.
309 if (data.type() != nlohmann::json::value_t::object)
310 {
311 std::cerr << "Illegal blacklist, expected to read dictionary\n";
312 std::exit(EXIT_FAILURE);
313 return;
314 }
315
316 // If buses field is missing, that's fine.
317 if (data.count("buses") == 1)
318 {
319 // Parse the buses array after a little validation.
320 auto buses = data.at("buses");
321 if (buses.type() != nlohmann::json::value_t::array)
322 {
323 // Buses field present but invalid, therefore this is an error.
324 std::cerr << "Invalid contents for blacklist buses field\n";
325 std::exit(EXIT_FAILURE);
326 return;
327 }
328
329 // Catch exception here for type mis-match.
330 try
331 {
332 for (const auto& bus : buses)
333 {
334 busBlacklist.insert(bus.get<size_t>());
335 }
336 }
337 catch (const nlohmann::detail::type_error& e)
338 {
339 // Type mis-match is a critical error.
340 std::cerr << "Invalid bus type: " << e.what() << "\n";
341 std::exit(EXIT_FAILURE);
342 return;
343 }
344 }
345
346 return;
347}
348
James Feista465ccc2019-02-08 12:51:01 -0800349static void FindI2CDevices(const std::vector<fs::path>& i2cBuses,
James Feist98132792019-07-09 13:29:09 -0700350 BusMap& busmap)
James Feist3cb5fec2018-01-23 14:41:51 -0800351{
James Feista465ccc2019-02-08 12:51:01 -0800352 for (auto& i2cBus : i2cBuses)
James Feist3cb5fec2018-01-23 14:41:51 -0800353 {
354 auto busnum = i2cBus.string();
355 auto lastDash = busnum.rfind(std::string("-"));
356 // delete everything before dash inclusive
357 if (lastDash != std::string::npos)
358 {
359 busnum.erase(0, lastDash + 1);
360 }
361 auto bus = std::stoi(busnum);
James Feist444830e2019-04-05 08:38:16 -0700362 if (busBlacklist.find(bus) != busBlacklist.end())
363 {
364 continue; // skip previously failed busses
365 }
James Feist3cb5fec2018-01-23 14:41:51 -0800366
367 auto file = open(i2cBus.c_str(), O_RDWR);
368 if (file < 0)
369 {
370 std::cerr << "unable to open i2c device " << i2cBus.string()
371 << "\n";
372 continue;
373 }
374 unsigned long funcs = 0;
375
376 if (ioctl(file, I2C_FUNCS, &funcs) < 0)
377 {
378 std::cerr
Patrick Venture98e0cf32019-08-02 11:11:03 -0700379 << "Error: Could not get the adapter functionality matrix bus "
James Feist3cb5fec2018-01-23 14:41:51 -0800380 << bus << "\n";
381 continue;
382 }
383 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE) ||
384 !(I2C_FUNC_SMBUS_READ_I2C_BLOCK))
385 {
386 std::cerr << "Error: Can't use SMBus Receive Byte command bus "
387 << bus << "\n";
388 continue;
389 }
James Feist98132792019-07-09 13:29:09 -0700390 auto& device = busmap[bus];
James Feist3cb5fec2018-01-23 14:41:51 -0800391 device = std::make_shared<DeviceMap>();
392
Nikhil Potaded8884f12019-03-27 13:27:13 -0700393 // i2cdetect by default uses the range 0x03 to 0x77, as
394 // this is what we have tested with, use this range. Could be
395 // changed in future.
396 if (DEBUG)
James Feistc95cb142018-02-26 10:41:42 -0800397 {
Nikhil Potaded8884f12019-03-27 13:27:13 -0700398 std::cerr << "Scanning bus " << bus << "\n";
James Feistc95cb142018-02-26 10:41:42 -0800399 }
Nikhil Potaded8884f12019-03-27 13:27:13 -0700400
401 // fd is closed in this function in case the bus locks up
402 get_bus_frus(file, 0x03, 0x77, bus, device);
403
404 if (DEBUG)
James Feistc95cb142018-02-26 10:41:42 -0800405 {
Nikhil Potaded8884f12019-03-27 13:27:13 -0700406 std::cerr << "Done scanning bus " << bus << "\n";
James Feistc95cb142018-02-26 10:41:42 -0800407 }
James Feist3cb5fec2018-01-23 14:41:51 -0800408 }
James Feist3cb5fec2018-01-23 14:41:51 -0800409}
410
James Feist6ebf9de2018-05-15 15:01:17 -0700411// this class allows an async response after all i2c devices are discovered
412struct FindDevicesWithCallback
413 : std::enable_shared_from_this<FindDevicesWithCallback>
414{
James Feista465ccc2019-02-08 12:51:01 -0800415 FindDevicesWithCallback(const std::vector<fs::path>& i2cBuses,
James Feist98132792019-07-09 13:29:09 -0700416 boost::asio::io_service& io, BusMap& busmap,
James Feista465ccc2019-02-08 12:51:01 -0800417 std::function<void(void)>&& callback) :
James Feist6ebf9de2018-05-15 15:01:17 -0700418 _i2cBuses(i2cBuses),
James Feist98132792019-07-09 13:29:09 -0700419 _io(io), _busMap(busmap), _callback(std::move(callback))
James Feist6ebf9de2018-05-15 15:01:17 -0700420 {
421 }
422 ~FindDevicesWithCallback()
423 {
424 _callback();
425 }
426 void run()
427 {
James Feist98132792019-07-09 13:29:09 -0700428 FindI2CDevices(_i2cBuses, _busMap);
James Feist6ebf9de2018-05-15 15:01:17 -0700429 }
430
James Feista465ccc2019-02-08 12:51:01 -0800431 const std::vector<fs::path>& _i2cBuses;
432 boost::asio::io_service& _io;
433 BusMap& _busMap;
James Feist6ebf9de2018-05-15 15:01:17 -0700434 std::function<void(void)> _callback;
435};
436
James Feist3cb5fec2018-01-23 14:41:51 -0800437static const std::tm intelEpoch(void)
438{
James Feist98132792019-07-09 13:29:09 -0700439 std::tm val = {};
James Feist3cb5fec2018-01-23 14:41:51 -0800440 val.tm_year = 1996 - 1900;
441 return val;
442}
443
James Feista465ccc2019-02-08 12:51:01 -0800444bool formatFru(const std::vector<char>& fruBytes,
445 boost::container::flat_map<std::string, std::string>& result)
James Feist3cb5fec2018-01-23 14:41:51 -0800446{
James Feista465ccc2019-02-08 12:51:01 -0800447 static const std::vector<const char*> CHASSIS_FRU_AREAS = {
Vijay Khemka5d5de442018-11-07 10:51:25 -0800448 "PART_NUMBER", "SERIAL_NUMBER", "INFO_AM1", "INFO_AM2"};
James Feist3cb5fec2018-01-23 14:41:51 -0800449
James Feista465ccc2019-02-08 12:51:01 -0800450 static const std::vector<const char*> BOARD_FRU_AREAS = {
Vijay Khemka5d5de442018-11-07 10:51:25 -0800451 "MANUFACTURER", "PRODUCT_NAME", "SERIAL_NUMBER", "PART_NUMBER",
452 "FRU_VERSION_ID", "INFO_AM1", "INFO_AM2"};
James Feist3cb5fec2018-01-23 14:41:51 -0800453
James Feista465ccc2019-02-08 12:51:01 -0800454 static const std::vector<const char*> PRODUCT_FRU_AREAS = {
Vijay Khemka5d5de442018-11-07 10:51:25 -0800455 "MANUFACTURER", "PRODUCT_NAME", "PART_NUMBER",
456 "VERSION", "SERIAL_NUMBER", "ASSET_TAG",
457 "FRU_VERSION_ID", "INFO_AM1", "INFO_AM2"};
James Feist3cb5fec2018-01-23 14:41:51 -0800458
James Feistd068e932018-09-20 10:53:07 -0700459 if (fruBytes.size() <= 8)
James Feist3cb5fec2018-01-23 14:41:51 -0800460 {
461 return false;
462 }
463 std::vector<char>::const_iterator fruAreaOffsetField = fruBytes.begin();
James Feist9eb0b582018-04-27 12:15:46 -0700464 result["Common_Format_Version"] =
James Feist3cb5fec2018-01-23 14:41:51 -0800465 std::to_string(static_cast<int>(*fruAreaOffsetField));
466
James Feista465ccc2019-02-08 12:51:01 -0800467 const std::vector<const char*>* fieldData;
James Feist3cb5fec2018-01-23 14:41:51 -0800468
James Feist0eb40352019-04-09 14:44:04 -0700469 for (const std::string& area : FRU_AREAS)
James Feist3cb5fec2018-01-23 14:41:51 -0800470 {
471 fruAreaOffsetField++;
472 if (fruAreaOffsetField >= fruBytes.end())
473 {
474 return false;
475 }
476 size_t offset = (*fruAreaOffsetField) * 8;
477
478 if (offset > 1)
479 {
480 // +2 to skip format and length
481 std::vector<char>::const_iterator fruBytesIter =
482 fruBytes.begin() + offset + 2;
483
484 if (fruBytesIter >= fruBytes.end())
485 {
486 return false;
487 }
488
489 if (area == "CHASSIS")
490 {
491 result["CHASSIS_TYPE"] =
492 std::to_string(static_cast<int>(*fruBytesIter));
493 fruBytesIter += 1;
494 fieldData = &CHASSIS_FRU_AREAS;
495 }
496 else if (area == "BOARD")
497 {
498 result["BOARD_LANGUAGE_CODE"] =
499 std::to_string(static_cast<int>(*fruBytesIter));
500 fruBytesIter += 1;
501 if (fruBytesIter >= fruBytes.end())
502 {
503 return false;
504 }
505
506 unsigned int minutes = *fruBytesIter |
507 *(fruBytesIter + 1) << 8 |
508 *(fruBytesIter + 2) << 16;
509 std::tm fruTime = intelEpoch();
510 time_t timeValue = mktime(&fruTime);
511 timeValue += minutes * 60;
512 fruTime = *gmtime(&timeValue);
513
514 result["BOARD_MANUFACTURE_DATE"] = asctime(&fruTime);
515 result["BOARD_MANUFACTURE_DATE"]
516 .pop_back(); // remove trailing null
517 fruBytesIter += 3;
518 fieldData = &BOARD_FRU_AREAS;
519 }
520 else if (area == "PRODUCT")
521 {
522 result["PRODUCT_LANGUAGE_CODE"] =
523 std::to_string(static_cast<int>(*fruBytesIter));
524 fruBytesIter += 1;
525 fieldData = &PRODUCT_FRU_AREAS;
526 }
527 else
528 {
529 continue;
530 }
James Feista465ccc2019-02-08 12:51:01 -0800531 for (auto& field : *fieldData)
James Feist3cb5fec2018-01-23 14:41:51 -0800532 {
533 if (fruBytesIter >= fruBytes.end())
534 {
535 return false;
536 }
537
Vijay Khemka5d5de442018-11-07 10:51:25 -0800538 /* Checking for last byte C1 to indicate that no more
539 * field to be read */
James Feist98132792019-07-09 13:29:09 -0700540 if (static_cast<uint8_t>(*fruBytesIter) == 0xC1)
Vijay Khemka5d5de442018-11-07 10:51:25 -0800541 {
542 break;
543 }
544
Ed Tanous2147e672019-02-27 13:59:56 -0800545 size_t length = *fruBytesIter & 0x3f;
546 fruBytesIter += 1;
547
James Feist3cb5fec2018-01-23 14:41:51 -0800548 if (fruBytesIter >= fruBytes.end())
549 {
550 return false;
551 }
Ed Tanous2147e672019-02-27 13:59:56 -0800552 std::string value(fruBytesIter, fruBytesIter + length);
James Feist3cb5fec2018-01-23 14:41:51 -0800553
Ed Tanous2147e672019-02-27 13:59:56 -0800554 // Strip non null characters from the end
555 value.erase(std::find_if(value.rbegin(), value.rend(),
556 [](char ch) { return ch != 0; })
557 .base(),
558 value.end());
559
James Feist0eb40352019-04-09 14:44:04 -0700560 result[area + "_" + field] = std::move(value);
Ed Tanous2147e672019-02-27 13:59:56 -0800561
James Feist3cb5fec2018-01-23 14:41:51 -0800562 fruBytesIter += length;
563 if (fruBytesIter >= fruBytes.end())
564 {
565 std::cerr << "Warning Fru Length Mismatch:\n ";
James Feista465ccc2019-02-08 12:51:01 -0800566 for (auto& c : fruBytes)
James Feist3cb5fec2018-01-23 14:41:51 -0800567 {
568 std::cerr << c;
569 }
570 std::cerr << "\n";
571 if (DEBUG)
572 {
James Feista465ccc2019-02-08 12:51:01 -0800573 for (auto& keyPair : result)
James Feist3cb5fec2018-01-23 14:41:51 -0800574 {
575 std::cerr << keyPair.first << " : "
576 << keyPair.second << "\n";
577 }
578 }
579 return false;
580 }
581 }
582 }
583 }
584
585 return true;
586}
587
Nikhil Potaded8884f12019-03-27 13:27:13 -0700588std::vector<uint8_t>& getFruInfo(const uint8_t& bus, const uint8_t& address)
589{
590 auto deviceMap = busMap.find(bus);
591 if (deviceMap == busMap.end())
592 {
593 throw std::invalid_argument("Invalid Bus.");
594 }
595 auto device = deviceMap->second->find(address);
596 if (device == deviceMap->second->end())
597 {
598 throw std::invalid_argument("Invalid Address.");
599 }
600 std::vector<uint8_t>& ret =
601 reinterpret_cast<std::vector<uint8_t>&>(device->second);
602
603 return ret;
604}
605
James Feist3cb5fec2018-01-23 14:41:51 -0800606void AddFruObjectToDbus(
James Feista465ccc2019-02-08 12:51:01 -0800607 std::vector<char>& device, sdbusplus::asio::object_server& objServer,
608 boost::container::flat_map<
609 std::pair<size_t, size_t>,
610 std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
James Feist13b86d62018-05-29 11:24:35 -0700611 uint32_t bus, uint32_t address)
James Feist3cb5fec2018-01-23 14:41:51 -0800612{
613 boost::container::flat_map<std::string, std::string> formattedFru;
614 if (!formatFru(device, formattedFru))
615 {
616 std::cerr << "failed to format fru for device at bus " << std::hex
Nikhil Potaded8884f12019-03-27 13:27:13 -0700617 << bus << " address " << address << "\n";
James Feist3cb5fec2018-01-23 14:41:51 -0800618 return;
619 }
Patrick Venture96cdaef2019-07-30 13:30:52 -0700620
James Feist3cb5fec2018-01-23 14:41:51 -0800621 auto productNameFind = formattedFru.find("BOARD_PRODUCT_NAME");
622 std::string productName;
Patrick Venture96cdaef2019-07-30 13:30:52 -0700623 // Not found under Board section or an empty string.
624 if (productNameFind == formattedFru.end() ||
625 productNameFind->second.empty())
James Feist3cb5fec2018-01-23 14:41:51 -0800626 {
627 productNameFind = formattedFru.find("PRODUCT_PRODUCT_NAME");
628 }
Patrick Venture96cdaef2019-07-30 13:30:52 -0700629 // Found under Product section and not an empty string.
630 if (productNameFind != formattedFru.end() &&
631 !productNameFind->second.empty())
James Feist3cb5fec2018-01-23 14:41:51 -0800632 {
633 productName = productNameFind->second;
James Feist3f8a2782018-02-12 09:24:42 -0800634 std::regex illegalObject("[^A-Za-z0-9_]");
635 productName = std::regex_replace(productName, illegalObject, "_");
James Feist3cb5fec2018-01-23 14:41:51 -0800636 }
637 else
638 {
639 productName = "UNKNOWN" + std::to_string(UNKNOWN_BUS_OBJECT_COUNT);
640 UNKNOWN_BUS_OBJECT_COUNT++;
641 }
642
James Feist918e18c2018-02-13 15:51:07 -0800643 productName = "/xyz/openbmc_project/FruDevice/" + productName;
James Feist918e18c2018-02-13 15:51:07 -0800644 // avoid duplicates by checking to see if on a mux
James Feist79e9c0b2018-03-15 15:21:17 -0700645 if (bus > 0)
James Feist918e18c2018-02-13 15:51:07 -0800646 {
James Feist79e9c0b2018-03-15 15:21:17 -0700647 size_t index = 0;
James Feista465ccc2019-02-08 12:51:01 -0800648 for (auto const& busIface : dbusInterfaceMap)
James Feist918e18c2018-02-13 15:51:07 -0800649 {
James Feist9eb0b582018-04-27 12:15:46 -0700650 if ((busIface.second->get_object_path() == productName))
James Feist918e18c2018-02-13 15:51:07 -0800651 {
Nikhil Potaded8884f12019-03-27 13:27:13 -0700652 if (isMuxBus(bus) && address == busIface.first.second &&
James Feist98132792019-07-09 13:29:09 -0700653 (getFruInfo(static_cast<uint8_t>(busIface.first.first),
654 static_cast<uint8_t>(busIface.first.second)) ==
655 getFruInfo(static_cast<uint8_t>(bus),
656 static_cast<uint8_t>(address))))
James Feist79e9c0b2018-03-15 15:21:17 -0700657 {
Nikhil Potaded8884f12019-03-27 13:27:13 -0700658 // This device is already added to the lower numbered bus,
659 // do not replicate it.
660 return;
James Feist79e9c0b2018-03-15 15:21:17 -0700661 }
662 // add underscore _index for the same object path on dbus
663 std::string strIndex = std::to_string(index);
664 if (index > 0)
665 {
666 productName.substr(0, productName.size() - strIndex.size());
667 }
668 else
669 {
670 productName += "_";
671 }
672 productName += std::to_string(index++);
James Feist918e18c2018-02-13 15:51:07 -0800673 }
674 }
675 }
James Feist3cb5fec2018-01-23 14:41:51 -0800676
James Feist9eb0b582018-04-27 12:15:46 -0700677 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
678 objServer.add_interface(productName, "xyz.openbmc_project.FruDevice");
679 dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface;
680
James Feista465ccc2019-02-08 12:51:01 -0800681 for (auto& property : formattedFru)
James Feist3cb5fec2018-01-23 14:41:51 -0800682 {
James Feist9eb0b582018-04-27 12:15:46 -0700683
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700684 std::regex_replace(property.second.begin(), property.second.begin(),
685 property.second.end(), NON_ASCII_REGEX, "_");
James Feist9eb0b582018-04-27 12:15:46 -0700686 if (property.second.empty())
687 {
688 continue;
689 }
690 std::string key =
691 std::regex_replace(property.first, NON_ASCII_REGEX, "_");
692 if (!iface->register_property(key, property.second + '\0'))
693 {
694 std::cerr << "illegal key: " << key << "\n";
695 }
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700696 if (DEBUG)
697 {
698 std::cout << property.first << ": " << property.second << "\n";
699 }
James Feist3cb5fec2018-01-23 14:41:51 -0800700 }
James Feist2a9d6db2018-04-27 15:48:28 -0700701
702 // baseboard will be 0, 0
James Feist13b86d62018-05-29 11:24:35 -0700703 iface->register_property("BUS", bus);
704 iface->register_property("ADDRESS", address);
James Feist2a9d6db2018-04-27 15:48:28 -0700705
James Feist9eb0b582018-04-27 12:15:46 -0700706 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800707}
708
James Feista465ccc2019-02-08 12:51:01 -0800709static bool readBaseboardFru(std::vector<char>& baseboardFru)
James Feist3cb5fec2018-01-23 14:41:51 -0800710{
711 // try to read baseboard fru from file
712 std::ifstream baseboardFruFile(BASEBOARD_FRU_LOCATION, std::ios::binary);
713 if (baseboardFruFile.good())
714 {
715 baseboardFruFile.seekg(0, std::ios_base::end);
James Feist98132792019-07-09 13:29:09 -0700716 size_t fileSize = static_cast<size_t>(baseboardFruFile.tellg());
James Feist3cb5fec2018-01-23 14:41:51 -0800717 baseboardFru.resize(fileSize);
718 baseboardFruFile.seekg(0, std::ios_base::beg);
719 baseboardFruFile.read(baseboardFru.data(), fileSize);
720 }
721 else
722 {
723 return false;
724 }
725 return true;
726}
727
James Feista465ccc2019-02-08 12:51:01 -0800728bool writeFru(uint8_t bus, uint8_t address, const std::vector<uint8_t>& fru)
James Feistb49ffc32018-05-02 11:10:43 -0700729{
730 boost::container::flat_map<std::string, std::string> tmp;
731 if (fru.size() > MAX_FRU_SIZE)
732 {
733 std::cerr << "Invalid fru.size() during writeFru\n";
734 return false;
735 }
736 // verify legal fru by running it through fru parsing logic
James Feista465ccc2019-02-08 12:51:01 -0800737 if (!formatFru(reinterpret_cast<const std::vector<char>&>(fru), tmp))
James Feistb49ffc32018-05-02 11:10:43 -0700738 {
739 std::cerr << "Invalid fru format during writeFru\n";
740 return false;
741 }
742 // baseboard fru
743 if (bus == 0 && address == 0)
744 {
745 std::ofstream file(BASEBOARD_FRU_LOCATION, std::ios_base::binary);
746 if (!file.good())
747 {
748 std::cerr << "Error opening file " << BASEBOARD_FRU_LOCATION
749 << "\n";
James Feistddb78302018-09-06 11:45:42 -0700750 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700751 return false;
752 }
James Feista465ccc2019-02-08 12:51:01 -0800753 file.write(reinterpret_cast<const char*>(fru.data()), fru.size());
James Feistb49ffc32018-05-02 11:10:43 -0700754 return file.good();
755 }
756 else
757 {
758 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
759
760 int file = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
761 if (file < 0)
762 {
763 std::cerr << "unable to open i2c device " << i2cBus << "\n";
James Feistddb78302018-09-06 11:45:42 -0700764 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700765 return false;
766 }
767 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
768 {
769 std::cerr << "unable to set device address\n";
770 close(file);
James Feistddb78302018-09-06 11:45:42 -0700771 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700772 return false;
773 }
774
775 constexpr const size_t RETRY_MAX = 2;
776 uint16_t index = 0;
777 size_t retries = RETRY_MAX;
778 while (index < fru.size())
779 {
780 if ((index && ((index % (MAX_EEPROM_PAGE_INDEX + 1)) == 0)) &&
781 (retries == RETRY_MAX))
782 {
783 // The 4K EEPROM only uses the A2 and A1 device address bits
784 // with the third bit being a memory page address bit.
785 if (ioctl(file, I2C_SLAVE_FORCE, ++address) < 0)
786 {
787 std::cerr << "unable to set device address\n";
788 close(file);
James Feistddb78302018-09-06 11:45:42 -0700789 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700790 return false;
791 }
792 }
793
James Feist98132792019-07-09 13:29:09 -0700794 if (i2c_smbus_write_byte_data(file, static_cast<uint8_t>(index),
795 fru[index]) < 0)
James Feistb49ffc32018-05-02 11:10:43 -0700796 {
797 if (!retries--)
798 {
799 std::cerr << "error writing fru: " << strerror(errno)
800 << "\n";
801 close(file);
James Feistddb78302018-09-06 11:45:42 -0700802 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700803 return false;
804 }
805 }
806 else
807 {
808 retries = RETRY_MAX;
809 index++;
810 }
811 // most eeproms require 5-10ms between writes
812 std::this_thread::sleep_for(std::chrono::milliseconds(10));
813 }
814 close(file);
815 return true;
816 }
817}
818
James Feist9eb0b582018-04-27 12:15:46 -0700819void rescanBusses(
James Feist98132792019-07-09 13:29:09 -0700820 boost::asio::io_service& io, BusMap& busmap,
James Feista465ccc2019-02-08 12:51:01 -0800821 boost::container::flat_map<
822 std::pair<size_t, size_t>,
823 std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
James Feista465ccc2019-02-08 12:51:01 -0800824 sdbusplus::asio::object_server& objServer)
James Feist918e18c2018-02-13 15:51:07 -0800825{
James Feist6ebf9de2018-05-15 15:01:17 -0700826 static boost::asio::deadline_timer timer(io);
827 timer.expires_from_now(boost::posix_time::seconds(1));
James Feist918e18c2018-02-13 15:51:07 -0800828
Gunnar Mills6f0ae942018-08-31 12:38:03 -0500829 // setup an async wait in case we get flooded with requests
James Feist98132792019-07-09 13:29:09 -0700830 timer.async_wait([&](const boost::system::error_code&) {
James Feist4131aea2018-03-09 09:47:30 -0800831 auto devDir = fs::path("/dev/");
James Feist4131aea2018-03-09 09:47:30 -0800832 std::vector<fs::path> i2cBuses;
James Feist918e18c2018-02-13 15:51:07 -0800833
Nikhil Potaded8884f12019-03-27 13:27:13 -0700834 boost::container::flat_map<size_t, fs::path> busPaths;
835 if (!getI2cDevicePaths(devDir, busPaths))
James Feist918e18c2018-02-13 15:51:07 -0800836 {
James Feist4131aea2018-03-09 09:47:30 -0800837 std::cerr << "unable to find i2c devices\n";
838 return;
James Feist918e18c2018-02-13 15:51:07 -0800839 }
Nikhil Potaded8884f12019-03-27 13:27:13 -0700840
841 for (auto busPath : busPaths)
842 {
843 i2cBuses.emplace_back(busPath.second);
844 }
James Feist4131aea2018-03-09 09:47:30 -0800845
James Feist98132792019-07-09 13:29:09 -0700846 busmap.clear();
James Feist6ebf9de2018-05-15 15:01:17 -0700847 auto scan = std::make_shared<FindDevicesWithCallback>(
James Feist98132792019-07-09 13:29:09 -0700848 i2cBuses, io, busmap, [&]() {
James Feista465ccc2019-02-08 12:51:01 -0800849 for (auto& busIface : dbusInterfaceMap)
James Feist6ebf9de2018-05-15 15:01:17 -0700850 {
851 objServer.remove_interface(busIface.second);
852 }
James Feist4131aea2018-03-09 09:47:30 -0800853
James Feist6ebf9de2018-05-15 15:01:17 -0700854 dbusInterfaceMap.clear();
855 UNKNOWN_BUS_OBJECT_COUNT = 0;
James Feist4131aea2018-03-09 09:47:30 -0800856
James Feist6ebf9de2018-05-15 15:01:17 -0700857 // todo, get this from a more sensable place
858 std::vector<char> baseboardFru;
859 if (readBaseboardFru(baseboardFru))
860 {
861 boost::container::flat_map<int, std::vector<char>>
862 baseboardDev;
863 baseboardDev.emplace(0, baseboardFru);
James Feist98132792019-07-09 13:29:09 -0700864 busmap[0] = std::make_shared<DeviceMap>(baseboardDev);
James Feist6ebf9de2018-05-15 15:01:17 -0700865 }
James Feist98132792019-07-09 13:29:09 -0700866 for (auto& devicemap : busmap)
James Feist6ebf9de2018-05-15 15:01:17 -0700867 {
James Feista465ccc2019-02-08 12:51:01 -0800868 for (auto& device : *devicemap.second)
James Feist6ebf9de2018-05-15 15:01:17 -0700869 {
James Feist98132792019-07-09 13:29:09 -0700870 AddFruObjectToDbus(device.second, objServer,
James Feist6ebf9de2018-05-15 15:01:17 -0700871 dbusInterfaceMap, devicemap.first,
872 device.first);
873 }
874 }
875 });
876 scan->run();
877 });
James Feist918e18c2018-02-13 15:51:07 -0800878}
879
James Feist98132792019-07-09 13:29:09 -0700880int main()
James Feist3cb5fec2018-01-23 14:41:51 -0800881{
882 auto devDir = fs::path("/dev/");
James Feistc9dff1b2019-02-13 13:33:13 -0800883 auto matchString = std::string(R"(i2c-\d+$)");
James Feist3cb5fec2018-01-23 14:41:51 -0800884 std::vector<fs::path> i2cBuses;
885
James Feista3c180a2018-08-09 16:06:04 -0700886 if (!findFiles(devDir, matchString, i2cBuses))
James Feist3cb5fec2018-01-23 14:41:51 -0800887 {
888 std::cerr << "unable to find i2c devices\n";
889 return 1;
890 }
James Feist3cb5fec2018-01-23 14:41:51 -0800891
Patrick Venture11f1ff42019-08-01 10:42:12 -0700892 // check for and load blacklist with initial buses.
893 loadBlacklist(blacklistPath);
894
James Feist3cb5fec2018-01-23 14:41:51 -0800895 boost::asio::io_service io;
James Feist9eb0b582018-04-27 12:15:46 -0700896 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
897 auto objServer = sdbusplus::asio::object_server(systemBus);
Vijay Khemka065f6d92018-12-18 10:37:47 -0800898 systemBus->request_name("xyz.openbmc_project.FruDevice");
James Feist3cb5fec2018-01-23 14:41:51 -0800899
James Feist6ebf9de2018-05-15 15:01:17 -0700900 // this is a map with keys of pair(bus number, address) and values of
901 // the object on dbus
James Feist3cb5fec2018-01-23 14:41:51 -0800902 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700903 std::shared_ptr<sdbusplus::asio::dbus_interface>>
904 dbusInterfaceMap;
James Feist3cb5fec2018-01-23 14:41:51 -0800905
James Feist9eb0b582018-04-27 12:15:46 -0700906 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
907 objServer.add_interface("/xyz/openbmc_project/FruDevice",
908 "xyz.openbmc_project.FruDeviceManager");
James Feist3cb5fec2018-01-23 14:41:51 -0800909
910 iface->register_method("ReScan", [&]() {
James Feist98132792019-07-09 13:29:09 -0700911 rescanBusses(io, busMap, dbusInterfaceMap, objServer);
James Feist3cb5fec2018-01-23 14:41:51 -0800912 });
James Feist2a9d6db2018-04-27 15:48:28 -0700913
Nikhil Potaded8884f12019-03-27 13:27:13 -0700914 iface->register_method("GetRawFru", getFruInfo);
James Feistb49ffc32018-05-02 11:10:43 -0700915
916 iface->register_method("WriteFru", [&](const uint8_t bus,
917 const uint8_t address,
James Feista465ccc2019-02-08 12:51:01 -0800918 const std::vector<uint8_t>& data) {
James Feistb49ffc32018-05-02 11:10:43 -0700919 if (!writeFru(bus, address, data))
920 {
James Feistddb78302018-09-06 11:45:42 -0700921 throw std::invalid_argument("Invalid Arguments.");
James Feistb49ffc32018-05-02 11:10:43 -0700922 return;
923 }
924 // schedule rescan on success
James Feist98132792019-07-09 13:29:09 -0700925 rescanBusses(io, busMap, dbusInterfaceMap, objServer);
James Feistb49ffc32018-05-02 11:10:43 -0700926 });
James Feist9eb0b582018-04-27 12:15:46 -0700927 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800928
James Feist9eb0b582018-04-27 12:15:46 -0700929 std::function<void(sdbusplus::message::message & message)> eventHandler =
James Feista465ccc2019-02-08 12:51:01 -0800930 [&](sdbusplus::message::message& message) {
James Feist918e18c2018-02-13 15:51:07 -0800931 std::string objectName;
James Feist9eb0b582018-04-27 12:15:46 -0700932 boost::container::flat_map<
James Feista465ccc2019-02-08 12:51:01 -0800933 std::string,
934 std::variant<std::string, bool, int64_t, uint64_t, double>>
James Feist9eb0b582018-04-27 12:15:46 -0700935 values;
936 message.read(objectName, values);
James Feist918e18c2018-02-13 15:51:07 -0800937 auto findPgood = values.find("pgood");
938 if (findPgood != values.end())
939 {
James Feist6ebf9de2018-05-15 15:01:17 -0700940
James Feist98132792019-07-09 13:29:09 -0700941 rescanBusses(io, busMap, dbusInterfaceMap, objServer);
James Feist918e18c2018-02-13 15:51:07 -0800942 }
James Feist918e18c2018-02-13 15:51:07 -0800943 };
James Feist9eb0b582018-04-27 12:15:46 -0700944
945 sdbusplus::bus::match::match powerMatch = sdbusplus::bus::match::match(
James Feista465ccc2019-02-08 12:51:01 -0800946 static_cast<sdbusplus::bus::bus&>(*systemBus),
James Feist7bcd3f22019-03-18 16:04:04 -0700947 "type='signal',interface='org.freedesktop.DBus.Properties',path='/xyz/"
948 "openbmc_project/Chassis/Control/"
949 "Power0',arg0='xyz.openbmc_project.Chassis.Control.Power'",
James Feist9eb0b582018-04-27 12:15:46 -0700950 eventHandler);
951
James Feist4131aea2018-03-09 09:47:30 -0800952 int fd = inotify_init();
James Feist0eb40352019-04-09 14:44:04 -0700953 inotify_add_watch(fd, I2C_DEV_LOCATION,
954 IN_CREATE | IN_MOVED_TO | IN_DELETE);
James Feist4131aea2018-03-09 09:47:30 -0800955 std::array<char, 4096> readBuffer;
956 std::string pendingBuffer;
957 // monitor for new i2c devices
958 boost::asio::posix::stream_descriptor dirWatch(io, fd);
959 std::function<void(const boost::system::error_code, std::size_t)>
James Feista465ccc2019-02-08 12:51:01 -0800960 watchI2cBusses = [&](const boost::system::error_code& ec,
James Feist4131aea2018-03-09 09:47:30 -0800961 std::size_t bytes_transferred) {
962 if (ec)
963 {
964 std::cout << "Callback Error " << ec << "\n";
965 return;
966 }
967 pendingBuffer += std::string(readBuffer.data(), bytes_transferred);
968 bool devChange = false;
969 while (pendingBuffer.size() > sizeof(inotify_event))
970 {
James Feista465ccc2019-02-08 12:51:01 -0800971 const inotify_event* iEvent =
972 reinterpret_cast<const inotify_event*>(
James Feist4131aea2018-03-09 09:47:30 -0800973 pendingBuffer.data());
974 switch (iEvent->mask)
975 {
James Feist9eb0b582018-04-27 12:15:46 -0700976 case IN_CREATE:
977 case IN_MOVED_TO:
978 case IN_DELETE:
979 if (boost::starts_with(std::string(iEvent->name),
980 "i2c"))
981 {
982 devChange = true;
983 }
James Feist4131aea2018-03-09 09:47:30 -0800984 }
985
986 pendingBuffer.erase(0, sizeof(inotify_event) + iEvent->len);
987 }
James Feist6ebf9de2018-05-15 15:01:17 -0700988 if (devChange)
James Feist4131aea2018-03-09 09:47:30 -0800989 {
James Feist98132792019-07-09 13:29:09 -0700990 rescanBusses(io, busMap, dbusInterfaceMap, objServer);
James Feist4131aea2018-03-09 09:47:30 -0800991 }
James Feist6ebf9de2018-05-15 15:01:17 -0700992
James Feist4131aea2018-03-09 09:47:30 -0800993 dirWatch.async_read_some(boost::asio::buffer(readBuffer),
994 watchI2cBusses);
995 };
996
997 dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses);
Gunnar Millsb3e42fe2018-06-13 15:48:27 -0500998 // run the initial scan
James Feist98132792019-07-09 13:29:09 -0700999 rescanBusses(io, busMap, dbusInterfaceMap, objServer);
James Feist918e18c2018-02-13 15:51:07 -08001000
James Feist3cb5fec2018-01-23 14:41:51 -08001001 io.run();
1002 return 0;
1003}