blob: f879afd65329ed84ab4a086899d404638a5181dd [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>
James Feist3cb5fec2018-01-23 14:41:51 -080027#include <fstream>
28#include <future>
James Feist3cb5fec2018-01-23 14:41:51 -080029#include <iostream>
James Feist3f8a2782018-02-12 09:24:42 -080030#include <regex>
James Feist3b860982018-10-02 14:34:07 -070031#include <sdbusplus/asio/connection.hpp>
32#include <sdbusplus/asio/object_server.hpp>
33#include <thread>
34
35extern "C" {
36#include <i2c/smbus.h>
37#include <linux/i2c-dev.h>
38}
James Feist3cb5fec2018-01-23 14:41:51 -080039
40namespace fs = std::experimental::filesystem;
41static constexpr bool DEBUG = false;
42static size_t UNKNOWN_BUS_OBJECT_COUNT = 0;
James Feistb49ffc32018-05-02 11:10:43 -070043constexpr size_t MAX_FRU_SIZE = 512;
44constexpr size_t MAX_EEPROM_PAGE_INDEX = 255;
James Feist26c27ad2018-07-25 15:09:40 -070045constexpr size_t busTimeoutSeconds = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080046
47const static constexpr char *BASEBOARD_FRU_LOCATION =
48 "/etc/fru/baseboard.fru.bin";
49
James Feist4131aea2018-03-09 09:47:30 -080050const static constexpr char *I2C_DEV_LOCATION = "/dev";
51
James Feist3cb5fec2018-01-23 14:41:51 -080052static constexpr std::array<const char *, 5> FRU_AREAS = {
53 "INTERNAL", "CHASSIS", "BOARD", "PRODUCT", "MULTIRECORD"};
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -070054const static std::regex NON_ASCII_REGEX("[^\x01-\x7f]");
James Feist3cb5fec2018-01-23 14:41:51 -080055using DeviceMap = boost::container::flat_map<int, std::vector<char>>;
56using BusMap = boost::container::flat_map<int, std::shared_ptr<DeviceMap>>;
57
James Feist6ebf9de2018-05-15 15:01:17 -070058struct FindDevicesWithCallback;
59
James Feistc95cb142018-02-26 10:41:42 -080060static bool isMuxBus(size_t bus)
61{
62 return is_symlink(std::experimental::filesystem::path(
63 "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device"));
64}
65
James Feist3cb5fec2018-01-23 14:41:51 -080066int get_bus_frus(int file, int first, int last, int bus,
67 std::shared_ptr<DeviceMap> devices)
68{
James Feist3cb5fec2018-01-23 14:41:51 -080069
James Feist26c27ad2018-07-25 15:09:40 -070070 std::future<int> future = std::async(std::launch::async, [&]() {
71 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> block_data;
72 for (int ii = first; ii <= last; ii++)
James Feist3cb5fec2018-01-23 14:41:51 -080073 {
James Feist3cb5fec2018-01-23 14:41:51 -080074
James Feist26c27ad2018-07-25 15:09:40 -070075 // Set slave address
76 if (ioctl(file, I2C_SLAVE_FORCE, ii) < 0)
James Feist3cb5fec2018-01-23 14:41:51 -080077 {
James Feist26c27ad2018-07-25 15:09:40 -070078 std::cerr << "device at bus " << bus << "register " << ii
79 << "busy\n";
80 continue;
81 }
82 // probe
83 else if (i2c_smbus_read_byte(file) < 0)
84 {
85 continue;
86 }
James Feist3cb5fec2018-01-23 14:41:51 -080087
James Feist26c27ad2018-07-25 15:09:40 -070088 if (DEBUG)
89 {
90 std::cout << "something at bus " << bus << "addr " << ii
91 << "\n";
92 }
93 if (i2c_smbus_read_i2c_block_data(file, 0x0, 0x8,
94 block_data.data()) < 0)
95 {
96 std::cerr << "failed to read bus " << bus << " address " << ii
97 << "\n";
98 continue;
99 }
100 size_t sum = 0;
101 for (int jj = 0; jj < 7; jj++)
102 {
103 sum += block_data[jj];
104 }
105 sum = (256 - sum) & 0xFF;
106
107 // check the header checksum
108 if (sum == block_data[7])
109 {
110 std::vector<char> device;
111 device.insert(device.end(), block_data.begin(),
112 block_data.begin() + 8);
113
114 for (int jj = 1; jj <= FRU_AREAS.size(); jj++)
115 {
116 auto area_offset = device[jj];
117 if (area_offset != 0)
James Feist3cb5fec2018-01-23 14:41:51 -0800118 {
James Feist26c27ad2018-07-25 15:09:40 -0700119 area_offset *= 8;
James Feist3cb5fec2018-01-23 14:41:51 -0800120 if (i2c_smbus_read_i2c_block_data(
James Feist26c27ad2018-07-25 15:09:40 -0700121 file, area_offset, 0x8, block_data.data()) < 0)
James Feist3cb5fec2018-01-23 14:41:51 -0800122 {
123 std::cerr << "failed to read bus " << bus
124 << " address " << ii << "\n";
125 return -1;
126 }
James Feist26c27ad2018-07-25 15:09:40 -0700127 int length = block_data[1] * 8;
James Feist3cb5fec2018-01-23 14:41:51 -0800128 device.insert(device.end(), block_data.begin(),
James Feist26c27ad2018-07-25 15:09:40 -0700129 block_data.begin() + 8);
130 length -= 8;
131 area_offset += 8;
132
133 while (length > 0)
134 {
135 auto to_get = std::min(0x20, length);
136 if (i2c_smbus_read_i2c_block_data(
137 file, area_offset, to_get,
138 block_data.data()) < 0)
139 {
140 std::cerr << "failed to read bus " << bus
141 << " address " << ii << "\n";
142 return -1;
143 }
144 device.insert(device.end(), block_data.begin(),
145 block_data.begin() + to_get);
146 area_offset += to_get;
147 length -= to_get;
148 }
James Feist3cb5fec2018-01-23 14:41:51 -0800149 }
150 }
James Feist26c27ad2018-07-25 15:09:40 -0700151 devices->emplace(ii, device);
James Feist3cb5fec2018-01-23 14:41:51 -0800152 }
James Feist3cb5fec2018-01-23 14:41:51 -0800153 }
James Feist26c27ad2018-07-25 15:09:40 -0700154 return 1;
155 });
156 std::future_status status =
157 future.wait_for(std::chrono::seconds(busTimeoutSeconds));
158 if (status == std::future_status::timeout)
159 {
160 std::cerr << "Error reading bus " << bus << "\n";
161 return -1;
James Feist3cb5fec2018-01-23 14:41:51 -0800162 }
163
James Feist26c27ad2018-07-25 15:09:40 -0700164 return future.get();
James Feist3cb5fec2018-01-23 14:41:51 -0800165}
166
James Feist6ebf9de2018-05-15 15:01:17 -0700167static void FindI2CDevices(const std::vector<fs::path> &i2cBuses,
168 std::shared_ptr<FindDevicesWithCallback> context,
169 boost::asio::io_service &io, BusMap &busMap)
James Feist3cb5fec2018-01-23 14:41:51 -0800170{
James Feist3cb5fec2018-01-23 14:41:51 -0800171 for (auto &i2cBus : i2cBuses)
172 {
173 auto busnum = i2cBus.string();
174 auto lastDash = busnum.rfind(std::string("-"));
175 // delete everything before dash inclusive
176 if (lastDash != std::string::npos)
177 {
178 busnum.erase(0, lastDash + 1);
179 }
180 auto bus = std::stoi(busnum);
181
182 auto file = open(i2cBus.c_str(), O_RDWR);
183 if (file < 0)
184 {
185 std::cerr << "unable to open i2c device " << i2cBus.string()
186 << "\n";
187 continue;
188 }
189 unsigned long funcs = 0;
190
191 if (ioctl(file, I2C_FUNCS, &funcs) < 0)
192 {
193 std::cerr
194 << "Error: Could not get the adapter functionality matrix bus"
195 << bus << "\n";
196 continue;
197 }
198 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE) ||
199 !(I2C_FUNC_SMBUS_READ_I2C_BLOCK))
200 {
201 std::cerr << "Error: Can't use SMBus Receive Byte command bus "
202 << bus << "\n";
203 continue;
204 }
205 auto &device = busMap[bus];
206 device = std::make_shared<DeviceMap>();
207
James Feistc95cb142018-02-26 10:41:42 -0800208 // don't scan muxed buses async as don't want to confuse the mux
209 if (isMuxBus(bus))
210 {
211 get_bus_frus(file, 0x03, 0x77, bus, device);
212 close(file);
213 }
214 else
215 {
James Feist6ebf9de2018-05-15 15:01:17 -0700216 io.post([file, device, bus, context] {
217 // i2cdetect by default uses the range 0x03 to 0x77, as
218 // this is
219 // what we
220 // have tested with, use this range. Could be changed in
221 // future.
222 get_bus_frus(file, 0x03, 0x77, bus, device);
223 close(file);
224 });
James Feistc95cb142018-02-26 10:41:42 -0800225 }
James Feist3cb5fec2018-01-23 14:41:51 -0800226 }
James Feist3cb5fec2018-01-23 14:41:51 -0800227}
228
James Feist6ebf9de2018-05-15 15:01:17 -0700229// this class allows an async response after all i2c devices are discovered
230struct FindDevicesWithCallback
231 : std::enable_shared_from_this<FindDevicesWithCallback>
232{
233 FindDevicesWithCallback(const std::vector<fs::path> &i2cBuses,
234 boost::asio::io_service &io, BusMap &busMap,
235 std::function<void(void)> &&callback) :
236 _i2cBuses(i2cBuses),
237 _io(io), _busMap(busMap), _callback(std::move(callback))
238 {
239 }
240 ~FindDevicesWithCallback()
241 {
242 _callback();
243 }
244 void run()
245 {
246 FindI2CDevices(_i2cBuses, shared_from_this(), _io, _busMap);
247 }
248
249 const std::vector<fs::path> &_i2cBuses;
250 boost::asio::io_service &_io;
251 BusMap &_busMap;
252 std::function<void(void)> _callback;
253};
254
James Feist3cb5fec2018-01-23 14:41:51 -0800255static const std::tm intelEpoch(void)
256{
257 std::tm val = {0};
258 val.tm_year = 1996 - 1900;
259 return val;
260}
261
262bool formatFru(const std::vector<char> &fruBytes,
263 boost::container::flat_map<std::string, std::string> &result)
264{
265 static const std::vector<const char *> CHASSIS_FRU_AREAS = {
Vijay Khemka5d5de442018-11-07 10:51:25 -0800266 "PART_NUMBER", "SERIAL_NUMBER", "INFO_AM1", "INFO_AM2"};
James Feist3cb5fec2018-01-23 14:41:51 -0800267
268 static const std::vector<const char *> BOARD_FRU_AREAS = {
Vijay Khemka5d5de442018-11-07 10:51:25 -0800269 "MANUFACTURER", "PRODUCT_NAME", "SERIAL_NUMBER", "PART_NUMBER",
270 "FRU_VERSION_ID", "INFO_AM1", "INFO_AM2"};
James Feist3cb5fec2018-01-23 14:41:51 -0800271
272 static const std::vector<const char *> PRODUCT_FRU_AREAS = {
Vijay Khemka5d5de442018-11-07 10:51:25 -0800273 "MANUFACTURER", "PRODUCT_NAME", "PART_NUMBER",
274 "VERSION", "SERIAL_NUMBER", "ASSET_TAG",
275 "FRU_VERSION_ID", "INFO_AM1", "INFO_AM2"};
James Feist3cb5fec2018-01-23 14:41:51 -0800276
277 size_t sum = 0;
278
James Feistd068e932018-09-20 10:53:07 -0700279 if (fruBytes.size() <= 8)
James Feist3cb5fec2018-01-23 14:41:51 -0800280 {
281 return false;
282 }
283 std::vector<char>::const_iterator fruAreaOffsetField = fruBytes.begin();
James Feist9eb0b582018-04-27 12:15:46 -0700284 result["Common_Format_Version"] =
James Feist3cb5fec2018-01-23 14:41:51 -0800285 std::to_string(static_cast<int>(*fruAreaOffsetField));
286
287 const std::vector<const char *> *fieldData;
288
289 for (auto &area : FRU_AREAS)
290 {
291 fruAreaOffsetField++;
292 if (fruAreaOffsetField >= fruBytes.end())
293 {
294 return false;
295 }
296 size_t offset = (*fruAreaOffsetField) * 8;
297
298 if (offset > 1)
299 {
300 // +2 to skip format and length
301 std::vector<char>::const_iterator fruBytesIter =
302 fruBytes.begin() + offset + 2;
303
304 if (fruBytesIter >= fruBytes.end())
305 {
306 return false;
307 }
308
309 if (area == "CHASSIS")
310 {
311 result["CHASSIS_TYPE"] =
312 std::to_string(static_cast<int>(*fruBytesIter));
313 fruBytesIter += 1;
314 fieldData = &CHASSIS_FRU_AREAS;
315 }
316 else if (area == "BOARD")
317 {
318 result["BOARD_LANGUAGE_CODE"] =
319 std::to_string(static_cast<int>(*fruBytesIter));
320 fruBytesIter += 1;
321 if (fruBytesIter >= fruBytes.end())
322 {
323 return false;
324 }
325
326 unsigned int minutes = *fruBytesIter |
327 *(fruBytesIter + 1) << 8 |
328 *(fruBytesIter + 2) << 16;
329 std::tm fruTime = intelEpoch();
330 time_t timeValue = mktime(&fruTime);
331 timeValue += minutes * 60;
332 fruTime = *gmtime(&timeValue);
333
334 result["BOARD_MANUFACTURE_DATE"] = asctime(&fruTime);
335 result["BOARD_MANUFACTURE_DATE"]
336 .pop_back(); // remove trailing null
337 fruBytesIter += 3;
338 fieldData = &BOARD_FRU_AREAS;
339 }
340 else if (area == "PRODUCT")
341 {
342 result["PRODUCT_LANGUAGE_CODE"] =
343 std::to_string(static_cast<int>(*fruBytesIter));
344 fruBytesIter += 1;
345 fieldData = &PRODUCT_FRU_AREAS;
346 }
347 else
348 {
349 continue;
350 }
351 for (auto &field : *fieldData)
352 {
353 if (fruBytesIter >= fruBytes.end())
354 {
355 return false;
356 }
357
358 size_t length = *fruBytesIter & 0x3f;
359 fruBytesIter += 1;
360
Vijay Khemka5d5de442018-11-07 10:51:25 -0800361 /* Checking for length if field present */
362 if (length == 0)
363 {
364 result[std::string(area) + "_" + field] =
365 std::string("Null");
366 continue;
367 }
368
369 /* Checking for last byte C1 to indicate that no more
370 * field to be read */
371 if (length == 1)
372 {
373 break;
374 }
375
James Feist3cb5fec2018-01-23 14:41:51 -0800376 if (fruBytesIter >= fruBytes.end())
377 {
378 return false;
379 }
380
381 result[std::string(area) + "_" + field] =
382 std::string(fruBytesIter, fruBytesIter + length);
383 fruBytesIter += length;
384 if (fruBytesIter >= fruBytes.end())
385 {
386 std::cerr << "Warning Fru Length Mismatch:\n ";
387 for (auto &c : fruBytes)
388 {
389 std::cerr << c;
390 }
391 std::cerr << "\n";
392 if (DEBUG)
393 {
394 for (auto &keyPair : result)
395 {
396 std::cerr << keyPair.first << " : "
397 << keyPair.second << "\n";
398 }
399 }
400 return false;
401 }
402 }
403 }
404 }
405
406 return true;
407}
408
409void AddFruObjectToDbus(
James Feist9eb0b582018-04-27 12:15:46 -0700410 std::shared_ptr<sdbusplus::asio::connection> dbusConn,
411 std::vector<char> &device, sdbusplus::asio::object_server &objServer,
James Feist3cb5fec2018-01-23 14:41:51 -0800412 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700413 std::shared_ptr<sdbusplus::asio::dbus_interface>>
414 &dbusInterfaceMap,
James Feist13b86d62018-05-29 11:24:35 -0700415 uint32_t bus, uint32_t address)
James Feist3cb5fec2018-01-23 14:41:51 -0800416{
417 boost::container::flat_map<std::string, std::string> formattedFru;
418 if (!formatFru(device, formattedFru))
419 {
420 std::cerr << "failed to format fru for device at bus " << std::hex
421 << bus << "address " << address << "\n";
422 return;
423 }
424 auto productNameFind = formattedFru.find("BOARD_PRODUCT_NAME");
425 std::string productName;
426 if (productNameFind == formattedFru.end())
427 {
428 productNameFind = formattedFru.find("PRODUCT_PRODUCT_NAME");
429 }
430 if (productNameFind != formattedFru.end())
431 {
432 productName = productNameFind->second;
James Feist3f8a2782018-02-12 09:24:42 -0800433 std::regex illegalObject("[^A-Za-z0-9_]");
434 productName = std::regex_replace(productName, illegalObject, "_");
James Feist3cb5fec2018-01-23 14:41:51 -0800435 }
436 else
437 {
438 productName = "UNKNOWN" + std::to_string(UNKNOWN_BUS_OBJECT_COUNT);
439 UNKNOWN_BUS_OBJECT_COUNT++;
440 }
441
James Feist918e18c2018-02-13 15:51:07 -0800442 productName = "/xyz/openbmc_project/FruDevice/" + productName;
James Feist918e18c2018-02-13 15:51:07 -0800443 // avoid duplicates by checking to see if on a mux
James Feist79e9c0b2018-03-15 15:21:17 -0700444 if (bus > 0)
James Feist918e18c2018-02-13 15:51:07 -0800445 {
James Feist79e9c0b2018-03-15 15:21:17 -0700446 size_t index = 0;
James Feist9eb0b582018-04-27 12:15:46 -0700447 for (auto const &busIface : dbusInterfaceMap)
James Feist918e18c2018-02-13 15:51:07 -0800448 {
James Feist9eb0b582018-04-27 12:15:46 -0700449 if ((busIface.second->get_object_path() == productName))
James Feist918e18c2018-02-13 15:51:07 -0800450 {
James Feist9eb0b582018-04-27 12:15:46 -0700451 if (isMuxBus(bus) && address == busIface.first.second)
James Feist79e9c0b2018-03-15 15:21:17 -0700452 {
453 continue;
454 }
455 // add underscore _index for the same object path on dbus
456 std::string strIndex = std::to_string(index);
457 if (index > 0)
458 {
459 productName.substr(0, productName.size() - strIndex.size());
460 }
461 else
462 {
463 productName += "_";
464 }
465 productName += std::to_string(index++);
James Feist918e18c2018-02-13 15:51:07 -0800466 }
467 }
468 }
James Feist3cb5fec2018-01-23 14:41:51 -0800469
James Feist9eb0b582018-04-27 12:15:46 -0700470 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
471 objServer.add_interface(productName, "xyz.openbmc_project.FruDevice");
472 dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface;
473
James Feist3cb5fec2018-01-23 14:41:51 -0800474 for (auto &property : formattedFru)
475 {
James Feist9eb0b582018-04-27 12:15:46 -0700476
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700477 std::regex_replace(property.second.begin(), property.second.begin(),
478 property.second.end(), NON_ASCII_REGEX, "_");
James Feist9eb0b582018-04-27 12:15:46 -0700479 if (property.second.empty())
480 {
481 continue;
482 }
483 std::string key =
484 std::regex_replace(property.first, NON_ASCII_REGEX, "_");
485 if (!iface->register_property(key, property.second + '\0'))
486 {
487 std::cerr << "illegal key: " << key << "\n";
488 }
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700489 if (DEBUG)
490 {
491 std::cout << property.first << ": " << property.second << "\n";
492 }
James Feist3cb5fec2018-01-23 14:41:51 -0800493 }
James Feist2a9d6db2018-04-27 15:48:28 -0700494
495 // baseboard will be 0, 0
James Feist13b86d62018-05-29 11:24:35 -0700496 iface->register_property("BUS", bus);
497 iface->register_property("ADDRESS", address);
James Feist2a9d6db2018-04-27 15:48:28 -0700498
James Feist9eb0b582018-04-27 12:15:46 -0700499 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800500}
501
502static bool readBaseboardFru(std::vector<char> &baseboardFru)
503{
504 // try to read baseboard fru from file
505 std::ifstream baseboardFruFile(BASEBOARD_FRU_LOCATION, std::ios::binary);
506 if (baseboardFruFile.good())
507 {
508 baseboardFruFile.seekg(0, std::ios_base::end);
509 std::streampos fileSize = baseboardFruFile.tellg();
510 baseboardFru.resize(fileSize);
511 baseboardFruFile.seekg(0, std::ios_base::beg);
512 baseboardFruFile.read(baseboardFru.data(), fileSize);
513 }
514 else
515 {
516 return false;
517 }
518 return true;
519}
520
James Feistb49ffc32018-05-02 11:10:43 -0700521bool writeFru(uint8_t bus, uint8_t address, const std::vector<uint8_t> &fru)
522{
523 boost::container::flat_map<std::string, std::string> tmp;
524 if (fru.size() > MAX_FRU_SIZE)
525 {
526 std::cerr << "Invalid fru.size() during writeFru\n";
527 return false;
528 }
529 // verify legal fru by running it through fru parsing logic
530 if (!formatFru(reinterpret_cast<const std::vector<char> &>(fru), tmp))
531 {
532 std::cerr << "Invalid fru format during writeFru\n";
533 return false;
534 }
535 // baseboard fru
536 if (bus == 0 && address == 0)
537 {
538 std::ofstream file(BASEBOARD_FRU_LOCATION, std::ios_base::binary);
539 if (!file.good())
540 {
541 std::cerr << "Error opening file " << BASEBOARD_FRU_LOCATION
542 << "\n";
James Feistddb78302018-09-06 11:45:42 -0700543 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700544 return false;
545 }
546 file.write(reinterpret_cast<const char *>(fru.data()), fru.size());
547 return file.good();
548 }
549 else
550 {
551 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
552
553 int file = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
554 if (file < 0)
555 {
556 std::cerr << "unable to open i2c device " << i2cBus << "\n";
James Feistddb78302018-09-06 11:45:42 -0700557 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700558 return false;
559 }
560 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
561 {
562 std::cerr << "unable to set device address\n";
563 close(file);
James Feistddb78302018-09-06 11:45:42 -0700564 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700565 return false;
566 }
567
568 constexpr const size_t RETRY_MAX = 2;
569 uint16_t index = 0;
570 size_t retries = RETRY_MAX;
571 while (index < fru.size())
572 {
573 if ((index && ((index % (MAX_EEPROM_PAGE_INDEX + 1)) == 0)) &&
574 (retries == RETRY_MAX))
575 {
576 // The 4K EEPROM only uses the A2 and A1 device address bits
577 // with the third bit being a memory page address bit.
578 if (ioctl(file, I2C_SLAVE_FORCE, ++address) < 0)
579 {
580 std::cerr << "unable to set device address\n";
581 close(file);
James Feistddb78302018-09-06 11:45:42 -0700582 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700583 return false;
584 }
585 }
586
587 if (i2c_smbus_write_byte_data(file, index & 0xFF, fru[index]) < 0)
588 {
589 if (!retries--)
590 {
591 std::cerr << "error writing fru: " << strerror(errno)
592 << "\n";
593 close(file);
James Feistddb78302018-09-06 11:45:42 -0700594 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700595 return false;
596 }
597 }
598 else
599 {
600 retries = RETRY_MAX;
601 index++;
602 }
603 // most eeproms require 5-10ms between writes
604 std::this_thread::sleep_for(std::chrono::milliseconds(10));
605 }
606 close(file);
607 return true;
608 }
609}
610
James Feist9eb0b582018-04-27 12:15:46 -0700611void rescanBusses(
James Feist6ebf9de2018-05-15 15:01:17 -0700612 boost::asio::io_service &io, BusMap &busMap,
James Feist9eb0b582018-04-27 12:15:46 -0700613 boost::container::flat_map<std::pair<size_t, size_t>,
614 std::shared_ptr<sdbusplus::asio::dbus_interface>>
615 &dbusInterfaceMap,
James Feist6ebf9de2018-05-15 15:01:17 -0700616 std::shared_ptr<sdbusplus::asio::connection> &systemBus,
617 sdbusplus::asio::object_server &objServer)
James Feist918e18c2018-02-13 15:51:07 -0800618{
James Feist6ebf9de2018-05-15 15:01:17 -0700619 static boost::asio::deadline_timer timer(io);
620 timer.expires_from_now(boost::posix_time::seconds(1));
James Feist918e18c2018-02-13 15:51:07 -0800621
Gunnar Mills6f0ae942018-08-31 12:38:03 -0500622 // setup an async wait in case we get flooded with requests
James Feist6ebf9de2018-05-15 15:01:17 -0700623 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist4131aea2018-03-09 09:47:30 -0800624 auto devDir = fs::path("/dev/");
625 auto matchString = std::string("i2c*");
626 std::vector<fs::path> i2cBuses;
James Feist918e18c2018-02-13 15:51:07 -0800627
James Feista3c180a2018-08-09 16:06:04 -0700628 if (!findFiles(devDir, matchString, i2cBuses))
James Feist918e18c2018-02-13 15:51:07 -0800629 {
James Feist4131aea2018-03-09 09:47:30 -0800630 std::cerr << "unable to find i2c devices\n";
631 return;
James Feist918e18c2018-02-13 15:51:07 -0800632 }
James Feist4131aea2018-03-09 09:47:30 -0800633 // scanning muxes in reverse order seems to have adverse effects
634 // sorting this list seems to be a fix for that
635 std::sort(i2cBuses.begin(), i2cBuses.end());
James Feist4131aea2018-03-09 09:47:30 -0800636
James Feist6ebf9de2018-05-15 15:01:17 -0700637 busMap.clear();
638 auto scan = std::make_shared<FindDevicesWithCallback>(
639 i2cBuses, io, busMap, [&]() {
640 for (auto &busIface : dbusInterfaceMap)
641 {
642 objServer.remove_interface(busIface.second);
643 }
James Feist4131aea2018-03-09 09:47:30 -0800644
James Feist6ebf9de2018-05-15 15:01:17 -0700645 dbusInterfaceMap.clear();
646 UNKNOWN_BUS_OBJECT_COUNT = 0;
James Feist4131aea2018-03-09 09:47:30 -0800647
James Feist6ebf9de2018-05-15 15:01:17 -0700648 // todo, get this from a more sensable place
649 std::vector<char> baseboardFru;
650 if (readBaseboardFru(baseboardFru))
651 {
652 boost::container::flat_map<int, std::vector<char>>
653 baseboardDev;
654 baseboardDev.emplace(0, baseboardFru);
655 busMap[0] = std::make_shared<DeviceMap>(baseboardDev);
656 }
657 for (auto &devicemap : busMap)
658 {
659 for (auto &device : *devicemap.second)
660 {
661 AddFruObjectToDbus(systemBus, device.second, objServer,
662 dbusInterfaceMap, devicemap.first,
663 device.first);
664 }
665 }
666 });
667 scan->run();
668 });
James Feist918e18c2018-02-13 15:51:07 -0800669}
670
James Feist3cb5fec2018-01-23 14:41:51 -0800671int main(int argc, char **argv)
672{
673 auto devDir = fs::path("/dev/");
674 auto matchString = std::string("i2c*");
675 std::vector<fs::path> i2cBuses;
676
James Feista3c180a2018-08-09 16:06:04 -0700677 if (!findFiles(devDir, matchString, i2cBuses))
James Feist3cb5fec2018-01-23 14:41:51 -0800678 {
679 std::cerr << "unable to find i2c devices\n";
680 return 1;
681 }
James Feist3cb5fec2018-01-23 14:41:51 -0800682
683 boost::asio::io_service io;
James Feist9eb0b582018-04-27 12:15:46 -0700684 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
685 auto objServer = sdbusplus::asio::object_server(systemBus);
James Feist3cb5fec2018-01-23 14:41:51 -0800686 systemBus->request_name("com.intel.FruDevice");
687
James Feist6ebf9de2018-05-15 15:01:17 -0700688 // this is a map with keys of pair(bus number, address) and values of
689 // the object on dbus
James Feist3cb5fec2018-01-23 14:41:51 -0800690 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700691 std::shared_ptr<sdbusplus::asio::dbus_interface>>
692 dbusInterfaceMap;
James Feist2a9d6db2018-04-27 15:48:28 -0700693 BusMap busmap;
James Feist3cb5fec2018-01-23 14:41:51 -0800694
James Feist9eb0b582018-04-27 12:15:46 -0700695 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
696 objServer.add_interface("/xyz/openbmc_project/FruDevice",
697 "xyz.openbmc_project.FruDeviceManager");
James Feist3cb5fec2018-01-23 14:41:51 -0800698
699 iface->register_method("ReScan", [&]() {
James Feist6ebf9de2018-05-15 15:01:17 -0700700 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
James Feist3cb5fec2018-01-23 14:41:51 -0800701 });
James Feist2a9d6db2018-04-27 15:48:28 -0700702
703 iface->register_method(
704 "GetRawFru", [&](const uint8_t &bus, const uint8_t &address) {
705 auto deviceMap = busmap.find(bus);
706 if (deviceMap == busmap.end())
707 {
James Feistddb78302018-09-06 11:45:42 -0700708 throw std::invalid_argument("Invalid Bus.");
James Feist2a9d6db2018-04-27 15:48:28 -0700709 }
710 auto device = deviceMap->second->find(address);
711 if (device == deviceMap->second->end())
712 {
James Feistddb78302018-09-06 11:45:42 -0700713 throw std::invalid_argument("Invalid Address.");
James Feist2a9d6db2018-04-27 15:48:28 -0700714 }
715 std::vector<uint8_t> &ret =
716 reinterpret_cast<std::vector<uint8_t> &>(device->second);
717 return ret;
718 });
James Feistb49ffc32018-05-02 11:10:43 -0700719
720 iface->register_method("WriteFru", [&](const uint8_t bus,
721 const uint8_t address,
722 const std::vector<uint8_t> &data) {
723 if (!writeFru(bus, address, data))
724 {
James Feistddb78302018-09-06 11:45:42 -0700725 throw std::invalid_argument("Invalid Arguments.");
James Feistb49ffc32018-05-02 11:10:43 -0700726 return;
727 }
728 // schedule rescan on success
729 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
James Feistb49ffc32018-05-02 11:10:43 -0700730 });
James Feist9eb0b582018-04-27 12:15:46 -0700731 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800732
James Feist9eb0b582018-04-27 12:15:46 -0700733 std::function<void(sdbusplus::message::message & message)> eventHandler =
734 [&](sdbusplus::message::message &message) {
James Feist918e18c2018-02-13 15:51:07 -0800735 std::string objectName;
James Feist9eb0b582018-04-27 12:15:46 -0700736 boost::container::flat_map<
737 std::string, sdbusplus::message::variant<
738 std::string, bool, int64_t, uint64_t, double>>
739 values;
740 message.read(objectName, values);
James Feist918e18c2018-02-13 15:51:07 -0800741 auto findPgood = values.find("pgood");
742 if (findPgood != values.end())
743 {
James Feist6ebf9de2018-05-15 15:01:17 -0700744
745 rescanBusses(io, busmap, dbusInterfaceMap, systemBus,
746 objServer);
James Feist918e18c2018-02-13 15:51:07 -0800747 }
James Feist918e18c2018-02-13 15:51:07 -0800748 };
James Feist9eb0b582018-04-27 12:15:46 -0700749
750 sdbusplus::bus::match::match powerMatch = sdbusplus::bus::match::match(
751 static_cast<sdbusplus::bus::bus &>(*systemBus),
752 "type='signal',interface='org.freedesktop.DBus.Properties',path_"
Kuiying Wang1dc43102018-05-09 15:09:29 +0800753 "namespace='/xyz/openbmc_project/Chassis/Control/"
754 "power0',arg0='xyz.openbmc_project.Chassis.Control.Power'",
James Feist9eb0b582018-04-27 12:15:46 -0700755 eventHandler);
756
James Feist4131aea2018-03-09 09:47:30 -0800757 int fd = inotify_init();
758 int wd = inotify_add_watch(fd, I2C_DEV_LOCATION,
759 IN_CREATE | IN_MOVED_TO | IN_DELETE);
760 std::array<char, 4096> readBuffer;
761 std::string pendingBuffer;
762 // monitor for new i2c devices
763 boost::asio::posix::stream_descriptor dirWatch(io, fd);
764 std::function<void(const boost::system::error_code, std::size_t)>
765 watchI2cBusses = [&](const boost::system::error_code &ec,
766 std::size_t bytes_transferred) {
767 if (ec)
768 {
769 std::cout << "Callback Error " << ec << "\n";
770 return;
771 }
772 pendingBuffer += std::string(readBuffer.data(), bytes_transferred);
773 bool devChange = false;
774 while (pendingBuffer.size() > sizeof(inotify_event))
775 {
776 const inotify_event *iEvent =
777 reinterpret_cast<const inotify_event *>(
778 pendingBuffer.data());
779 switch (iEvent->mask)
780 {
James Feist9eb0b582018-04-27 12:15:46 -0700781 case IN_CREATE:
782 case IN_MOVED_TO:
783 case IN_DELETE:
784 if (boost::starts_with(std::string(iEvent->name),
785 "i2c"))
786 {
787 devChange = true;
788 }
James Feist4131aea2018-03-09 09:47:30 -0800789 }
790
791 pendingBuffer.erase(0, sizeof(inotify_event) + iEvent->len);
792 }
James Feist6ebf9de2018-05-15 15:01:17 -0700793 if (devChange)
James Feist4131aea2018-03-09 09:47:30 -0800794 {
James Feist6ebf9de2018-05-15 15:01:17 -0700795 rescanBusses(io, busmap, dbusInterfaceMap, systemBus,
796 objServer);
James Feist4131aea2018-03-09 09:47:30 -0800797 }
James Feist6ebf9de2018-05-15 15:01:17 -0700798
James Feist4131aea2018-03-09 09:47:30 -0800799 dirWatch.async_read_some(boost::asio::buffer(readBuffer),
800 watchI2cBusses);
801 };
802
803 dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses);
Gunnar Millsb3e42fe2018-06-13 15:48:27 -0500804 // run the initial scan
James Feist6ebf9de2018-05-15 15:01:17 -0700805 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
James Feist918e18c2018-02-13 15:51:07 -0800806
James Feist3cb5fec2018-01-23 14:41:51 -0800807 io.run();
808 return 0;
809}