blob: 956ca1df392959343586e3528e42bbd05bacc3ac [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include <Utils.hpp>
18#include <boost/container/flat_map.hpp>
James Feist8f2710a2018-05-09 17:18:55 -070019#include <boost/algorithm/string/predicate.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080020#include <ctime>
James Feistb49ffc32018-05-02 11:10:43 -070021#include <chrono>
22#include <thread>
James Feist9eb0b582018-04-27 12:15:46 -070023#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
James Feist3cb5fec2018-01-23 14:41:51 -080025#include <fcntl.h>
26#include <fstream>
27#include <future>
James Feistb5320a72018-01-24 12:28:12 -080028#include <linux/i2c-dev-user.h>
James Feist3cb5fec2018-01-23 14:41:51 -080029#include <iostream>
30#include <sys/ioctl.h>
James Feist3f8a2782018-02-12 09:24:42 -080031#include <regex>
James Feist4131aea2018-03-09 09:47:30 -080032#include <sys/inotify.h>
James Feistb49ffc32018-05-02 11:10:43 -070033#include <errno.h>
James Feist3cb5fec2018-01-23 14:41:51 -080034
35namespace fs = std::experimental::filesystem;
36static constexpr bool DEBUG = false;
37static size_t UNKNOWN_BUS_OBJECT_COUNT = 0;
James Feistb49ffc32018-05-02 11:10:43 -070038constexpr size_t MAX_FRU_SIZE = 512;
39constexpr size_t MAX_EEPROM_PAGE_INDEX = 255;
James Feist26c27ad2018-07-25 15:09:40 -070040constexpr size_t busTimeoutSeconds = 5;
James Feist3cb5fec2018-01-23 14:41:51 -080041
42const static constexpr char *BASEBOARD_FRU_LOCATION =
43 "/etc/fru/baseboard.fru.bin";
44
James Feist4131aea2018-03-09 09:47:30 -080045const static constexpr char *I2C_DEV_LOCATION = "/dev";
46
James Feist3cb5fec2018-01-23 14:41:51 -080047static constexpr std::array<const char *, 5> FRU_AREAS = {
48 "INTERNAL", "CHASSIS", "BOARD", "PRODUCT", "MULTIRECORD"};
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -070049const static std::regex NON_ASCII_REGEX("[^\x01-\x7f]");
James Feist3cb5fec2018-01-23 14:41:51 -080050using DeviceMap = boost::container::flat_map<int, std::vector<char>>;
51using BusMap = boost::container::flat_map<int, std::shared_ptr<DeviceMap>>;
52
James Feist6ebf9de2018-05-15 15:01:17 -070053struct FindDevicesWithCallback;
54
James Feistc95cb142018-02-26 10:41:42 -080055static bool isMuxBus(size_t bus)
56{
57 return is_symlink(std::experimental::filesystem::path(
58 "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device"));
59}
60
James Feist3cb5fec2018-01-23 14:41:51 -080061int get_bus_frus(int file, int first, int last, int bus,
62 std::shared_ptr<DeviceMap> devices)
63{
James Feist3cb5fec2018-01-23 14:41:51 -080064
James Feist26c27ad2018-07-25 15:09:40 -070065 std::future<int> future = std::async(std::launch::async, [&]() {
66 std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> block_data;
67 for (int ii = first; ii <= last; ii++)
James Feist3cb5fec2018-01-23 14:41:51 -080068 {
James Feist3cb5fec2018-01-23 14:41:51 -080069
James Feist26c27ad2018-07-25 15:09:40 -070070 // Set slave address
71 if (ioctl(file, I2C_SLAVE_FORCE, ii) < 0)
James Feist3cb5fec2018-01-23 14:41:51 -080072 {
James Feist26c27ad2018-07-25 15:09:40 -070073 std::cerr << "device at bus " << bus << "register " << ii
74 << "busy\n";
75 continue;
76 }
77 // probe
78 else if (i2c_smbus_read_byte(file) < 0)
79 {
80 continue;
81 }
James Feist3cb5fec2018-01-23 14:41:51 -080082
James Feist26c27ad2018-07-25 15:09:40 -070083 if (DEBUG)
84 {
85 std::cout << "something at bus " << bus << "addr " << ii
86 << "\n";
87 }
88 if (i2c_smbus_read_i2c_block_data(file, 0x0, 0x8,
89 block_data.data()) < 0)
90 {
91 std::cerr << "failed to read bus " << bus << " address " << ii
92 << "\n";
93 continue;
94 }
95 size_t sum = 0;
96 for (int jj = 0; jj < 7; jj++)
97 {
98 sum += block_data[jj];
99 }
100 sum = (256 - sum) & 0xFF;
101
102 // check the header checksum
103 if (sum == block_data[7])
104 {
105 std::vector<char> device;
106 device.insert(device.end(), block_data.begin(),
107 block_data.begin() + 8);
108
109 for (int jj = 1; jj <= FRU_AREAS.size(); jj++)
110 {
111 auto area_offset = device[jj];
112 if (area_offset != 0)
James Feist3cb5fec2018-01-23 14:41:51 -0800113 {
James Feist26c27ad2018-07-25 15:09:40 -0700114 area_offset *= 8;
James Feist3cb5fec2018-01-23 14:41:51 -0800115 if (i2c_smbus_read_i2c_block_data(
James Feist26c27ad2018-07-25 15:09:40 -0700116 file, area_offset, 0x8, block_data.data()) < 0)
James Feist3cb5fec2018-01-23 14:41:51 -0800117 {
118 std::cerr << "failed to read bus " << bus
119 << " address " << ii << "\n";
120 return -1;
121 }
James Feist26c27ad2018-07-25 15:09:40 -0700122 int length = block_data[1] * 8;
James Feist3cb5fec2018-01-23 14:41:51 -0800123 device.insert(device.end(), block_data.begin(),
James Feist26c27ad2018-07-25 15:09:40 -0700124 block_data.begin() + 8);
125 length -= 8;
126 area_offset += 8;
127
128 while (length > 0)
129 {
130 auto to_get = std::min(0x20, length);
131 if (i2c_smbus_read_i2c_block_data(
132 file, area_offset, to_get,
133 block_data.data()) < 0)
134 {
135 std::cerr << "failed to read bus " << bus
136 << " address " << ii << "\n";
137 return -1;
138 }
139 device.insert(device.end(), block_data.begin(),
140 block_data.begin() + to_get);
141 area_offset += to_get;
142 length -= to_get;
143 }
James Feist3cb5fec2018-01-23 14:41:51 -0800144 }
145 }
James Feist26c27ad2018-07-25 15:09:40 -0700146 devices->emplace(ii, device);
James Feist3cb5fec2018-01-23 14:41:51 -0800147 }
James Feist3cb5fec2018-01-23 14:41:51 -0800148 }
James Feist26c27ad2018-07-25 15:09:40 -0700149 return 1;
150 });
151 std::future_status status =
152 future.wait_for(std::chrono::seconds(busTimeoutSeconds));
153 if (status == std::future_status::timeout)
154 {
155 std::cerr << "Error reading bus " << bus << "\n";
156 return -1;
James Feist3cb5fec2018-01-23 14:41:51 -0800157 }
158
James Feist26c27ad2018-07-25 15:09:40 -0700159 return future.get();
James Feist3cb5fec2018-01-23 14:41:51 -0800160}
161
James Feist6ebf9de2018-05-15 15:01:17 -0700162static void FindI2CDevices(const std::vector<fs::path> &i2cBuses,
163 std::shared_ptr<FindDevicesWithCallback> context,
164 boost::asio::io_service &io, BusMap &busMap)
James Feist3cb5fec2018-01-23 14:41:51 -0800165{
James Feist3cb5fec2018-01-23 14:41:51 -0800166 for (auto &i2cBus : i2cBuses)
167 {
168 auto busnum = i2cBus.string();
169 auto lastDash = busnum.rfind(std::string("-"));
170 // delete everything before dash inclusive
171 if (lastDash != std::string::npos)
172 {
173 busnum.erase(0, lastDash + 1);
174 }
175 auto bus = std::stoi(busnum);
176
177 auto file = open(i2cBus.c_str(), O_RDWR);
178 if (file < 0)
179 {
180 std::cerr << "unable to open i2c device " << i2cBus.string()
181 << "\n";
182 continue;
183 }
184 unsigned long funcs = 0;
185
186 if (ioctl(file, I2C_FUNCS, &funcs) < 0)
187 {
188 std::cerr
189 << "Error: Could not get the adapter functionality matrix bus"
190 << bus << "\n";
191 continue;
192 }
193 if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE) ||
194 !(I2C_FUNC_SMBUS_READ_I2C_BLOCK))
195 {
196 std::cerr << "Error: Can't use SMBus Receive Byte command bus "
197 << bus << "\n";
198 continue;
199 }
200 auto &device = busMap[bus];
201 device = std::make_shared<DeviceMap>();
202
James Feistc95cb142018-02-26 10:41:42 -0800203 // don't scan muxed buses async as don't want to confuse the mux
204 if (isMuxBus(bus))
205 {
206 get_bus_frus(file, 0x03, 0x77, bus, device);
207 close(file);
208 }
209 else
210 {
James Feist6ebf9de2018-05-15 15:01:17 -0700211 io.post([file, device, bus, context] {
212 // i2cdetect by default uses the range 0x03 to 0x77, as
213 // this is
214 // what we
215 // have tested with, use this range. Could be changed in
216 // future.
217 get_bus_frus(file, 0x03, 0x77, bus, device);
218 close(file);
219 });
James Feistc95cb142018-02-26 10:41:42 -0800220 }
James Feist3cb5fec2018-01-23 14:41:51 -0800221 }
James Feist3cb5fec2018-01-23 14:41:51 -0800222}
223
James Feist6ebf9de2018-05-15 15:01:17 -0700224// this class allows an async response after all i2c devices are discovered
225struct FindDevicesWithCallback
226 : std::enable_shared_from_this<FindDevicesWithCallback>
227{
228 FindDevicesWithCallback(const std::vector<fs::path> &i2cBuses,
229 boost::asio::io_service &io, BusMap &busMap,
230 std::function<void(void)> &&callback) :
231 _i2cBuses(i2cBuses),
232 _io(io), _busMap(busMap), _callback(std::move(callback))
233 {
234 }
235 ~FindDevicesWithCallback()
236 {
237 _callback();
238 }
239 void run()
240 {
241 FindI2CDevices(_i2cBuses, shared_from_this(), _io, _busMap);
242 }
243
244 const std::vector<fs::path> &_i2cBuses;
245 boost::asio::io_service &_io;
246 BusMap &_busMap;
247 std::function<void(void)> _callback;
248};
249
James Feist3cb5fec2018-01-23 14:41:51 -0800250static const std::tm intelEpoch(void)
251{
252 std::tm val = {0};
253 val.tm_year = 1996 - 1900;
254 return val;
255}
256
257bool formatFru(const std::vector<char> &fruBytes,
258 boost::container::flat_map<std::string, std::string> &result)
259{
260 static const std::vector<const char *> CHASSIS_FRU_AREAS = {
261 "PART_NUMBER", "SERIAL_NUMBER", "CHASSIS_INFO_AM1", "CHASSIS_INFO_AM2"};
262
263 static const std::vector<const char *> BOARD_FRU_AREAS = {
264 "MANUFACTURER", "PRODUCT_NAME", "SERIAL_NUMBER", "PART_NUMBER",
265 "VERSION_ID"};
266
267 static const std::vector<const char *> PRODUCT_FRU_AREAS = {
268 "MANUFACTURER", "PRODUCT_NAME", "PART_NUMBER",
269 "PRODUCT_VERSION", "PRODUCT_SERIAL_NUMBER", "ASSET_TAG"};
270
271 size_t sum = 0;
272
James Feistd068e932018-09-20 10:53:07 -0700273 if (fruBytes.size() <= 8)
James Feist3cb5fec2018-01-23 14:41:51 -0800274 {
275 return false;
276 }
277 std::vector<char>::const_iterator fruAreaOffsetField = fruBytes.begin();
James Feist9eb0b582018-04-27 12:15:46 -0700278 result["Common_Format_Version"] =
James Feist3cb5fec2018-01-23 14:41:51 -0800279 std::to_string(static_cast<int>(*fruAreaOffsetField));
280
281 const std::vector<const char *> *fieldData;
282
283 for (auto &area : FRU_AREAS)
284 {
285 fruAreaOffsetField++;
286 if (fruAreaOffsetField >= fruBytes.end())
287 {
288 return false;
289 }
290 size_t offset = (*fruAreaOffsetField) * 8;
291
292 if (offset > 1)
293 {
294 // +2 to skip format and length
295 std::vector<char>::const_iterator fruBytesIter =
296 fruBytes.begin() + offset + 2;
297
298 if (fruBytesIter >= fruBytes.end())
299 {
300 return false;
301 }
302
303 if (area == "CHASSIS")
304 {
305 result["CHASSIS_TYPE"] =
306 std::to_string(static_cast<int>(*fruBytesIter));
307 fruBytesIter += 1;
308 fieldData = &CHASSIS_FRU_AREAS;
309 }
310 else if (area == "BOARD")
311 {
312 result["BOARD_LANGUAGE_CODE"] =
313 std::to_string(static_cast<int>(*fruBytesIter));
314 fruBytesIter += 1;
315 if (fruBytesIter >= fruBytes.end())
316 {
317 return false;
318 }
319
320 unsigned int minutes = *fruBytesIter |
321 *(fruBytesIter + 1) << 8 |
322 *(fruBytesIter + 2) << 16;
323 std::tm fruTime = intelEpoch();
324 time_t timeValue = mktime(&fruTime);
325 timeValue += minutes * 60;
326 fruTime = *gmtime(&timeValue);
327
328 result["BOARD_MANUFACTURE_DATE"] = asctime(&fruTime);
329 result["BOARD_MANUFACTURE_DATE"]
330 .pop_back(); // remove trailing null
331 fruBytesIter += 3;
332 fieldData = &BOARD_FRU_AREAS;
333 }
334 else if (area == "PRODUCT")
335 {
336 result["PRODUCT_LANGUAGE_CODE"] =
337 std::to_string(static_cast<int>(*fruBytesIter));
338 fruBytesIter += 1;
339 fieldData = &PRODUCT_FRU_AREAS;
340 }
341 else
342 {
343 continue;
344 }
345 for (auto &field : *fieldData)
346 {
347 if (fruBytesIter >= fruBytes.end())
348 {
349 return false;
350 }
351
352 size_t length = *fruBytesIter & 0x3f;
353 fruBytesIter += 1;
354
355 if (fruBytesIter >= fruBytes.end())
356 {
357 return false;
358 }
359
360 result[std::string(area) + "_" + field] =
361 std::string(fruBytesIter, fruBytesIter + length);
362 fruBytesIter += length;
363 if (fruBytesIter >= fruBytes.end())
364 {
365 std::cerr << "Warning Fru Length Mismatch:\n ";
366 for (auto &c : fruBytes)
367 {
368 std::cerr << c;
369 }
370 std::cerr << "\n";
371 if (DEBUG)
372 {
373 for (auto &keyPair : result)
374 {
375 std::cerr << keyPair.first << " : "
376 << keyPair.second << "\n";
377 }
378 }
379 return false;
380 }
381 }
382 }
383 }
384
385 return true;
386}
387
388void AddFruObjectToDbus(
James Feist9eb0b582018-04-27 12:15:46 -0700389 std::shared_ptr<sdbusplus::asio::connection> dbusConn,
390 std::vector<char> &device, sdbusplus::asio::object_server &objServer,
James Feist3cb5fec2018-01-23 14:41:51 -0800391 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700392 std::shared_ptr<sdbusplus::asio::dbus_interface>>
393 &dbusInterfaceMap,
James Feist13b86d62018-05-29 11:24:35 -0700394 uint32_t bus, uint32_t address)
James Feist3cb5fec2018-01-23 14:41:51 -0800395{
396 boost::container::flat_map<std::string, std::string> formattedFru;
397 if (!formatFru(device, formattedFru))
398 {
399 std::cerr << "failed to format fru for device at bus " << std::hex
400 << bus << "address " << address << "\n";
401 return;
402 }
403 auto productNameFind = formattedFru.find("BOARD_PRODUCT_NAME");
404 std::string productName;
405 if (productNameFind == formattedFru.end())
406 {
407 productNameFind = formattedFru.find("PRODUCT_PRODUCT_NAME");
408 }
409 if (productNameFind != formattedFru.end())
410 {
411 productName = productNameFind->second;
James Feist3f8a2782018-02-12 09:24:42 -0800412 std::regex illegalObject("[^A-Za-z0-9_]");
413 productName = std::regex_replace(productName, illegalObject, "_");
James Feist3cb5fec2018-01-23 14:41:51 -0800414 }
415 else
416 {
417 productName = "UNKNOWN" + std::to_string(UNKNOWN_BUS_OBJECT_COUNT);
418 UNKNOWN_BUS_OBJECT_COUNT++;
419 }
420
James Feist918e18c2018-02-13 15:51:07 -0800421 productName = "/xyz/openbmc_project/FruDevice/" + productName;
James Feist918e18c2018-02-13 15:51:07 -0800422 // avoid duplicates by checking to see if on a mux
James Feist79e9c0b2018-03-15 15:21:17 -0700423 if (bus > 0)
James Feist918e18c2018-02-13 15:51:07 -0800424 {
James Feist79e9c0b2018-03-15 15:21:17 -0700425 size_t index = 0;
James Feist9eb0b582018-04-27 12:15:46 -0700426 for (auto const &busIface : dbusInterfaceMap)
James Feist918e18c2018-02-13 15:51:07 -0800427 {
James Feist9eb0b582018-04-27 12:15:46 -0700428 if ((busIface.second->get_object_path() == productName))
James Feist918e18c2018-02-13 15:51:07 -0800429 {
James Feist9eb0b582018-04-27 12:15:46 -0700430 if (isMuxBus(bus) && address == busIface.first.second)
James Feist79e9c0b2018-03-15 15:21:17 -0700431 {
432 continue;
433 }
434 // add underscore _index for the same object path on dbus
435 std::string strIndex = std::to_string(index);
436 if (index > 0)
437 {
438 productName.substr(0, productName.size() - strIndex.size());
439 }
440 else
441 {
442 productName += "_";
443 }
444 productName += std::to_string(index++);
James Feist918e18c2018-02-13 15:51:07 -0800445 }
446 }
447 }
James Feist3cb5fec2018-01-23 14:41:51 -0800448
James Feist9eb0b582018-04-27 12:15:46 -0700449 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
450 objServer.add_interface(productName, "xyz.openbmc_project.FruDevice");
451 dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface;
452
James Feist3cb5fec2018-01-23 14:41:51 -0800453 for (auto &property : formattedFru)
454 {
James Feist9eb0b582018-04-27 12:15:46 -0700455
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700456 std::regex_replace(property.second.begin(), property.second.begin(),
457 property.second.end(), NON_ASCII_REGEX, "_");
James Feist9eb0b582018-04-27 12:15:46 -0700458 if (property.second.empty())
459 {
460 continue;
461 }
462 std::string key =
463 std::regex_replace(property.first, NON_ASCII_REGEX, "_");
464 if (!iface->register_property(key, property.second + '\0'))
465 {
466 std::cerr << "illegal key: " << key << "\n";
467 }
Jae Hyun Yoo3936e7a2018-03-23 17:26:16 -0700468 if (DEBUG)
469 {
470 std::cout << property.first << ": " << property.second << "\n";
471 }
James Feist3cb5fec2018-01-23 14:41:51 -0800472 }
James Feist2a9d6db2018-04-27 15:48:28 -0700473
474 // baseboard will be 0, 0
James Feist13b86d62018-05-29 11:24:35 -0700475 iface->register_property("BUS", bus);
476 iface->register_property("ADDRESS", address);
James Feist2a9d6db2018-04-27 15:48:28 -0700477
James Feist9eb0b582018-04-27 12:15:46 -0700478 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800479}
480
481static bool readBaseboardFru(std::vector<char> &baseboardFru)
482{
483 // try to read baseboard fru from file
484 std::ifstream baseboardFruFile(BASEBOARD_FRU_LOCATION, std::ios::binary);
485 if (baseboardFruFile.good())
486 {
487 baseboardFruFile.seekg(0, std::ios_base::end);
488 std::streampos fileSize = baseboardFruFile.tellg();
489 baseboardFru.resize(fileSize);
490 baseboardFruFile.seekg(0, std::ios_base::beg);
491 baseboardFruFile.read(baseboardFru.data(), fileSize);
492 }
493 else
494 {
495 return false;
496 }
497 return true;
498}
499
James Feistb49ffc32018-05-02 11:10:43 -0700500bool writeFru(uint8_t bus, uint8_t address, const std::vector<uint8_t> &fru)
501{
502 boost::container::flat_map<std::string, std::string> tmp;
503 if (fru.size() > MAX_FRU_SIZE)
504 {
505 std::cerr << "Invalid fru.size() during writeFru\n";
506 return false;
507 }
508 // verify legal fru by running it through fru parsing logic
509 if (!formatFru(reinterpret_cast<const std::vector<char> &>(fru), tmp))
510 {
511 std::cerr << "Invalid fru format during writeFru\n";
512 return false;
513 }
514 // baseboard fru
515 if (bus == 0 && address == 0)
516 {
517 std::ofstream file(BASEBOARD_FRU_LOCATION, std::ios_base::binary);
518 if (!file.good())
519 {
520 std::cerr << "Error opening file " << BASEBOARD_FRU_LOCATION
521 << "\n";
James Feistddb78302018-09-06 11:45:42 -0700522 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700523 return false;
524 }
525 file.write(reinterpret_cast<const char *>(fru.data()), fru.size());
526 return file.good();
527 }
528 else
529 {
530 std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
531
532 int file = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
533 if (file < 0)
534 {
535 std::cerr << "unable to open i2c device " << i2cBus << "\n";
James Feistddb78302018-09-06 11:45:42 -0700536 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700537 return false;
538 }
539 if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
540 {
541 std::cerr << "unable to set device address\n";
542 close(file);
James Feistddb78302018-09-06 11:45:42 -0700543 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700544 return false;
545 }
546
547 constexpr const size_t RETRY_MAX = 2;
548 uint16_t index = 0;
549 size_t retries = RETRY_MAX;
550 while (index < fru.size())
551 {
552 if ((index && ((index % (MAX_EEPROM_PAGE_INDEX + 1)) == 0)) &&
553 (retries == RETRY_MAX))
554 {
555 // The 4K EEPROM only uses the A2 and A1 device address bits
556 // with the third bit being a memory page address bit.
557 if (ioctl(file, I2C_SLAVE_FORCE, ++address) < 0)
558 {
559 std::cerr << "unable to set device address\n";
560 close(file);
James Feistddb78302018-09-06 11:45:42 -0700561 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700562 return false;
563 }
564 }
565
566 if (i2c_smbus_write_byte_data(file, index & 0xFF, fru[index]) < 0)
567 {
568 if (!retries--)
569 {
570 std::cerr << "error writing fru: " << strerror(errno)
571 << "\n";
572 close(file);
James Feistddb78302018-09-06 11:45:42 -0700573 throw DBusInternalError();
James Feistb49ffc32018-05-02 11:10:43 -0700574 return false;
575 }
576 }
577 else
578 {
579 retries = RETRY_MAX;
580 index++;
581 }
582 // most eeproms require 5-10ms between writes
583 std::this_thread::sleep_for(std::chrono::milliseconds(10));
584 }
585 close(file);
586 return true;
587 }
588}
589
James Feist9eb0b582018-04-27 12:15:46 -0700590void rescanBusses(
James Feist6ebf9de2018-05-15 15:01:17 -0700591 boost::asio::io_service &io, BusMap &busMap,
James Feist9eb0b582018-04-27 12:15:46 -0700592 boost::container::flat_map<std::pair<size_t, size_t>,
593 std::shared_ptr<sdbusplus::asio::dbus_interface>>
594 &dbusInterfaceMap,
James Feist6ebf9de2018-05-15 15:01:17 -0700595 std::shared_ptr<sdbusplus::asio::connection> &systemBus,
596 sdbusplus::asio::object_server &objServer)
James Feist918e18c2018-02-13 15:51:07 -0800597{
James Feist6ebf9de2018-05-15 15:01:17 -0700598 static boost::asio::deadline_timer timer(io);
599 timer.expires_from_now(boost::posix_time::seconds(1));
James Feist918e18c2018-02-13 15:51:07 -0800600
Gunnar Mills6f0ae942018-08-31 12:38:03 -0500601 // setup an async wait in case we get flooded with requests
James Feist6ebf9de2018-05-15 15:01:17 -0700602 timer.async_wait([&](const boost::system::error_code &ec) {
James Feist4131aea2018-03-09 09:47:30 -0800603 auto devDir = fs::path("/dev/");
604 auto matchString = std::string("i2c*");
605 std::vector<fs::path> i2cBuses;
James Feist918e18c2018-02-13 15:51:07 -0800606
James Feista3c180a2018-08-09 16:06:04 -0700607 if (!findFiles(devDir, matchString, i2cBuses))
James Feist918e18c2018-02-13 15:51:07 -0800608 {
James Feist4131aea2018-03-09 09:47:30 -0800609 std::cerr << "unable to find i2c devices\n";
610 return;
James Feist918e18c2018-02-13 15:51:07 -0800611 }
James Feist4131aea2018-03-09 09:47:30 -0800612 // scanning muxes in reverse order seems to have adverse effects
613 // sorting this list seems to be a fix for that
614 std::sort(i2cBuses.begin(), i2cBuses.end());
James Feist4131aea2018-03-09 09:47:30 -0800615
James Feist6ebf9de2018-05-15 15:01:17 -0700616 busMap.clear();
617 auto scan = std::make_shared<FindDevicesWithCallback>(
618 i2cBuses, io, busMap, [&]() {
619 for (auto &busIface : dbusInterfaceMap)
620 {
621 objServer.remove_interface(busIface.second);
622 }
James Feist4131aea2018-03-09 09:47:30 -0800623
James Feist6ebf9de2018-05-15 15:01:17 -0700624 dbusInterfaceMap.clear();
625 UNKNOWN_BUS_OBJECT_COUNT = 0;
James Feist4131aea2018-03-09 09:47:30 -0800626
James Feist6ebf9de2018-05-15 15:01:17 -0700627 // todo, get this from a more sensable place
628 std::vector<char> baseboardFru;
629 if (readBaseboardFru(baseboardFru))
630 {
631 boost::container::flat_map<int, std::vector<char>>
632 baseboardDev;
633 baseboardDev.emplace(0, baseboardFru);
634 busMap[0] = std::make_shared<DeviceMap>(baseboardDev);
635 }
636 for (auto &devicemap : busMap)
637 {
638 for (auto &device : *devicemap.second)
639 {
640 AddFruObjectToDbus(systemBus, device.second, objServer,
641 dbusInterfaceMap, devicemap.first,
642 device.first);
643 }
644 }
645 });
646 scan->run();
647 });
James Feist918e18c2018-02-13 15:51:07 -0800648}
649
James Feist3cb5fec2018-01-23 14:41:51 -0800650int main(int argc, char **argv)
651{
652 auto devDir = fs::path("/dev/");
653 auto matchString = std::string("i2c*");
654 std::vector<fs::path> i2cBuses;
655
James Feista3c180a2018-08-09 16:06:04 -0700656 if (!findFiles(devDir, matchString, i2cBuses))
James Feist3cb5fec2018-01-23 14:41:51 -0800657 {
658 std::cerr << "unable to find i2c devices\n";
659 return 1;
660 }
James Feist3cb5fec2018-01-23 14:41:51 -0800661
662 boost::asio::io_service io;
James Feist9eb0b582018-04-27 12:15:46 -0700663 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
664 auto objServer = sdbusplus::asio::object_server(systemBus);
James Feist3cb5fec2018-01-23 14:41:51 -0800665 systemBus->request_name("com.intel.FruDevice");
666
James Feist6ebf9de2018-05-15 15:01:17 -0700667 // this is a map with keys of pair(bus number, address) and values of
668 // the object on dbus
James Feist3cb5fec2018-01-23 14:41:51 -0800669 boost::container::flat_map<std::pair<size_t, size_t>,
James Feist9eb0b582018-04-27 12:15:46 -0700670 std::shared_ptr<sdbusplus::asio::dbus_interface>>
671 dbusInterfaceMap;
James Feist2a9d6db2018-04-27 15:48:28 -0700672 BusMap busmap;
James Feist3cb5fec2018-01-23 14:41:51 -0800673
James Feist9eb0b582018-04-27 12:15:46 -0700674 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
675 objServer.add_interface("/xyz/openbmc_project/FruDevice",
676 "xyz.openbmc_project.FruDeviceManager");
James Feist3cb5fec2018-01-23 14:41:51 -0800677
678 iface->register_method("ReScan", [&]() {
James Feist6ebf9de2018-05-15 15:01:17 -0700679 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
James Feist3cb5fec2018-01-23 14:41:51 -0800680 });
James Feist2a9d6db2018-04-27 15:48:28 -0700681
682 iface->register_method(
683 "GetRawFru", [&](const uint8_t &bus, const uint8_t &address) {
684 auto deviceMap = busmap.find(bus);
685 if (deviceMap == busmap.end())
686 {
James Feistddb78302018-09-06 11:45:42 -0700687 throw std::invalid_argument("Invalid Bus.");
James Feist2a9d6db2018-04-27 15:48:28 -0700688 }
689 auto device = deviceMap->second->find(address);
690 if (device == deviceMap->second->end())
691 {
James Feistddb78302018-09-06 11:45:42 -0700692 throw std::invalid_argument("Invalid Address.");
James Feist2a9d6db2018-04-27 15:48:28 -0700693 }
694 std::vector<uint8_t> &ret =
695 reinterpret_cast<std::vector<uint8_t> &>(device->second);
696 return ret;
697 });
James Feistb49ffc32018-05-02 11:10:43 -0700698
699 iface->register_method("WriteFru", [&](const uint8_t bus,
700 const uint8_t address,
701 const std::vector<uint8_t> &data) {
702 if (!writeFru(bus, address, data))
703 {
James Feistddb78302018-09-06 11:45:42 -0700704 throw std::invalid_argument("Invalid Arguments.");
James Feistb49ffc32018-05-02 11:10:43 -0700705 return;
706 }
707 // schedule rescan on success
708 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
James Feistb49ffc32018-05-02 11:10:43 -0700709 });
James Feist9eb0b582018-04-27 12:15:46 -0700710 iface->initialize();
James Feist3cb5fec2018-01-23 14:41:51 -0800711
James Feist9eb0b582018-04-27 12:15:46 -0700712 std::function<void(sdbusplus::message::message & message)> eventHandler =
713 [&](sdbusplus::message::message &message) {
James Feist918e18c2018-02-13 15:51:07 -0800714 std::string objectName;
James Feist9eb0b582018-04-27 12:15:46 -0700715 boost::container::flat_map<
716 std::string, sdbusplus::message::variant<
717 std::string, bool, int64_t, uint64_t, double>>
718 values;
719 message.read(objectName, values);
James Feist918e18c2018-02-13 15:51:07 -0800720 auto findPgood = values.find("pgood");
721 if (findPgood != values.end())
722 {
James Feist6ebf9de2018-05-15 15:01:17 -0700723
724 rescanBusses(io, busmap, dbusInterfaceMap, systemBus,
725 objServer);
James Feist918e18c2018-02-13 15:51:07 -0800726 }
James Feist918e18c2018-02-13 15:51:07 -0800727 };
James Feist9eb0b582018-04-27 12:15:46 -0700728
729 sdbusplus::bus::match::match powerMatch = sdbusplus::bus::match::match(
730 static_cast<sdbusplus::bus::bus &>(*systemBus),
731 "type='signal',interface='org.freedesktop.DBus.Properties',path_"
Kuiying Wang1dc43102018-05-09 15:09:29 +0800732 "namespace='/xyz/openbmc_project/Chassis/Control/"
733 "power0',arg0='xyz.openbmc_project.Chassis.Control.Power'",
James Feist9eb0b582018-04-27 12:15:46 -0700734 eventHandler);
735
James Feist4131aea2018-03-09 09:47:30 -0800736 int fd = inotify_init();
737 int wd = inotify_add_watch(fd, I2C_DEV_LOCATION,
738 IN_CREATE | IN_MOVED_TO | IN_DELETE);
739 std::array<char, 4096> readBuffer;
740 std::string pendingBuffer;
741 // monitor for new i2c devices
742 boost::asio::posix::stream_descriptor dirWatch(io, fd);
743 std::function<void(const boost::system::error_code, std::size_t)>
744 watchI2cBusses = [&](const boost::system::error_code &ec,
745 std::size_t bytes_transferred) {
746 if (ec)
747 {
748 std::cout << "Callback Error " << ec << "\n";
749 return;
750 }
751 pendingBuffer += std::string(readBuffer.data(), bytes_transferred);
752 bool devChange = false;
753 while (pendingBuffer.size() > sizeof(inotify_event))
754 {
755 const inotify_event *iEvent =
756 reinterpret_cast<const inotify_event *>(
757 pendingBuffer.data());
758 switch (iEvent->mask)
759 {
James Feist9eb0b582018-04-27 12:15:46 -0700760 case IN_CREATE:
761 case IN_MOVED_TO:
762 case IN_DELETE:
763 if (boost::starts_with(std::string(iEvent->name),
764 "i2c"))
765 {
766 devChange = true;
767 }
James Feist4131aea2018-03-09 09:47:30 -0800768 }
769
770 pendingBuffer.erase(0, sizeof(inotify_event) + iEvent->len);
771 }
James Feist6ebf9de2018-05-15 15:01:17 -0700772 if (devChange)
James Feist4131aea2018-03-09 09:47:30 -0800773 {
James Feist6ebf9de2018-05-15 15:01:17 -0700774 rescanBusses(io, busmap, dbusInterfaceMap, systemBus,
775 objServer);
James Feist4131aea2018-03-09 09:47:30 -0800776 }
James Feist6ebf9de2018-05-15 15:01:17 -0700777
James Feist4131aea2018-03-09 09:47:30 -0800778 dirWatch.async_read_some(boost::asio::buffer(readBuffer),
779 watchI2cBusses);
780 };
781
782 dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses);
Gunnar Millsb3e42fe2018-06-13 15:48:27 -0500783 // run the initial scan
James Feist6ebf9de2018-05-15 15:01:17 -0700784 rescanBusses(io, busmap, dbusInterfaceMap, systemBus, objServer);
James Feist918e18c2018-02-13 15:51:07 -0800785
James Feist3cb5fec2018-01-23 14:41:51 -0800786 io.run();
787 return 0;
788}