| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 1 | /* | 
 | 2 | // Copyright (c) 2018 Intel Corporation | 
 | 3 | // | 
 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 | // you may not use this file except in compliance with the License. | 
 | 6 | // You may obtain a copy of the License at | 
 | 7 | // | 
 | 8 | //      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 | // | 
 | 10 | // Unless required by applicable law or agreed to in writing, software | 
 | 11 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 | // See the License for the specific language governing permissions and | 
 | 14 | // limitations under the License. | 
 | 15 | */ | 
 | 16 |  | 
 | 17 | #include <Utils.hpp> | 
 | 18 | #include <boost/container/flat_map.hpp> | 
 | 19 | #include <ctime> | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 20 | #include <sdbusplus/asio/connection.hpp> | 
 | 21 | #include <sdbusplus/asio/object_server.hpp> | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 22 | #include <dbus/properties.hpp> | 
 | 23 | #include <fcntl.h> | 
 | 24 | #include <fstream> | 
 | 25 | #include <future> | 
| James Feist | b5320a7 | 2018-01-24 12:28:12 -0800 | [diff] [blame] | 26 | #include <linux/i2c-dev-user.h> | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 27 | #include <iostream> | 
 | 28 | #include <sys/ioctl.h> | 
| James Feist | 3f8a278 | 2018-02-12 09:24:42 -0800 | [diff] [blame] | 29 | #include <regex> | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 30 | #include <sys/inotify.h> | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 31 | #include <xyz/openbmc_project/Common/error.hpp> | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 32 |  | 
 | 33 | namespace fs = std::experimental::filesystem; | 
 | 34 | static constexpr bool DEBUG = false; | 
 | 35 | static size_t UNKNOWN_BUS_OBJECT_COUNT = 0; | 
 | 36 |  | 
 | 37 | const static constexpr char *BASEBOARD_FRU_LOCATION = | 
 | 38 |     "/etc/fru/baseboard.fru.bin"; | 
 | 39 |  | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 40 | const static constexpr char *I2C_DEV_LOCATION = "/dev"; | 
 | 41 |  | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 42 | static constexpr std::array<const char *, 5> FRU_AREAS = { | 
 | 43 |     "INTERNAL", "CHASSIS", "BOARD", "PRODUCT", "MULTIRECORD"}; | 
| Jae Hyun Yoo | 3936e7a | 2018-03-23 17:26:16 -0700 | [diff] [blame] | 44 | const static std::regex NON_ASCII_REGEX("[^\x01-\x7f]"); | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 45 | using DeviceMap = boost::container::flat_map<int, std::vector<char>>; | 
 | 46 | using BusMap = boost::container::flat_map<int, std::shared_ptr<DeviceMap>>; | 
 | 47 |  | 
| James Feist | c95cb14 | 2018-02-26 10:41:42 -0800 | [diff] [blame] | 48 | static bool isMuxBus(size_t bus) | 
 | 49 | { | 
 | 50 |     return is_symlink(std::experimental::filesystem::path( | 
 | 51 |         "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device")); | 
 | 52 | } | 
 | 53 |  | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 54 | int get_bus_frus(int file, int first, int last, int bus, | 
 | 55 |                  std::shared_ptr<DeviceMap> devices) | 
 | 56 | { | 
 | 57 |     std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> block_data; | 
 | 58 |  | 
 | 59 |     for (int ii = first; ii <= last; ii++) | 
 | 60 |     { | 
 | 61 |         // Set slave address | 
 | 62 |         if (ioctl(file, I2C_SLAVE_FORCE, ii) < 0) | 
 | 63 |         { | 
 | 64 |             std::cerr << "device at bus " << bus << "register " << ii | 
 | 65 |                       << "busy\n"; | 
 | 66 |             continue; | 
 | 67 |         } | 
 | 68 |         // probe | 
 | 69 |         else if (i2c_smbus_read_byte(file) < 0) | 
 | 70 |         { | 
 | 71 |             continue; | 
 | 72 |         } | 
 | 73 |  | 
 | 74 |         if (DEBUG) | 
 | 75 |         { | 
 | 76 |             std::cout << "something at bus " << bus << "addr " << ii << "\n"; | 
 | 77 |         } | 
 | 78 |         if (i2c_smbus_read_i2c_block_data(file, 0x0, 0x8, block_data.data()) < | 
 | 79 |             0) | 
 | 80 |         { | 
 | 81 |             std::cerr << "failed to read bus " << bus << " address " << ii | 
 | 82 |                       << "\n"; | 
 | 83 |             continue; | 
 | 84 |         } | 
 | 85 |         size_t sum = 0; | 
 | 86 |         for (int jj = 0; jj < 7; jj++) | 
 | 87 |         { | 
 | 88 |             sum += block_data[jj]; | 
 | 89 |         } | 
 | 90 |         sum = (256 - sum) & 0xFF; | 
 | 91 |  | 
 | 92 |         // check the header checksum | 
 | 93 |         if (sum == block_data[7]) | 
 | 94 |         { | 
 | 95 |             std::vector<char> device; | 
 | 96 |             device.insert(device.end(), block_data.begin(), | 
 | 97 |                           block_data.begin() + 8); | 
 | 98 |  | 
 | 99 |             for (int jj = 1; jj <= FRU_AREAS.size(); jj++) | 
 | 100 |             { | 
 | 101 |                 auto area_offset = device[jj]; | 
 | 102 |                 if (area_offset != 0) | 
 | 103 |                 { | 
 | 104 |                     area_offset *= 8; | 
 | 105 |                     if (i2c_smbus_read_i2c_block_data(file, area_offset, 0x8, | 
 | 106 |                                                       block_data.data()) < 0) | 
 | 107 |                     { | 
 | 108 |                         std::cerr << "failed to read bus " << bus << " address " | 
 | 109 |                                   << ii << "\n"; | 
 | 110 |                         return -1; | 
 | 111 |                     } | 
 | 112 |                     int length = block_data[1] * 8; | 
 | 113 |                     device.insert(device.end(), block_data.begin(), | 
 | 114 |                                   block_data.begin() + 8); | 
 | 115 |                     length -= 8; | 
 | 116 |                     area_offset += 8; | 
 | 117 |  | 
 | 118 |                     while (length > 0) | 
 | 119 |                     { | 
 | 120 |                         auto to_get = std::min(0x20, length); | 
 | 121 |                         if (i2c_smbus_read_i2c_block_data( | 
 | 122 |                                 file, area_offset, to_get, block_data.data()) < | 
 | 123 |                             0) | 
 | 124 |                         { | 
 | 125 |                             std::cerr << "failed to read bus " << bus | 
 | 126 |                                       << " address " << ii << "\n"; | 
 | 127 |                             return -1; | 
 | 128 |                         } | 
 | 129 |                         device.insert(device.end(), block_data.begin(), | 
 | 130 |                                       block_data.begin() + to_get); | 
 | 131 |                         area_offset += to_get; | 
 | 132 |                         length -= to_get; | 
 | 133 |                     } | 
 | 134 |                 } | 
 | 135 |             } | 
 | 136 |             (*devices).emplace(ii, device); | 
 | 137 |         } | 
 | 138 |     } | 
 | 139 |  | 
 | 140 |     return 0; | 
 | 141 | } | 
 | 142 |  | 
 | 143 | static BusMap FindI2CDevices(const std::vector<fs::path> &i2cBuses) | 
 | 144 | { | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 145 |     std::vector<std::future<void>> futures; | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 146 |     BusMap busMap; | 
 | 147 |     for (auto &i2cBus : i2cBuses) | 
 | 148 |     { | 
 | 149 |         auto busnum = i2cBus.string(); | 
 | 150 |         auto lastDash = busnum.rfind(std::string("-")); | 
 | 151 |         // delete everything before dash inclusive | 
 | 152 |         if (lastDash != std::string::npos) | 
 | 153 |         { | 
 | 154 |             busnum.erase(0, lastDash + 1); | 
 | 155 |         } | 
 | 156 |         auto bus = std::stoi(busnum); | 
 | 157 |  | 
 | 158 |         auto file = open(i2cBus.c_str(), O_RDWR); | 
 | 159 |         if (file < 0) | 
 | 160 |         { | 
 | 161 |             std::cerr << "unable to open i2c device " << i2cBus.string() | 
 | 162 |                       << "\n"; | 
 | 163 |             continue; | 
 | 164 |         } | 
 | 165 |         unsigned long funcs = 0; | 
 | 166 |  | 
 | 167 |         if (ioctl(file, I2C_FUNCS, &funcs) < 0) | 
 | 168 |         { | 
 | 169 |             std::cerr | 
 | 170 |                 << "Error: Could not get the adapter functionality matrix bus" | 
 | 171 |                 << bus << "\n"; | 
 | 172 |             continue; | 
 | 173 |         } | 
 | 174 |         if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE) || | 
 | 175 |             !(I2C_FUNC_SMBUS_READ_I2C_BLOCK)) | 
 | 176 |         { | 
 | 177 |             std::cerr << "Error: Can't use SMBus Receive Byte command bus " | 
 | 178 |                       << bus << "\n"; | 
 | 179 |             continue; | 
 | 180 |         } | 
 | 181 |         auto &device = busMap[bus]; | 
 | 182 |         device = std::make_shared<DeviceMap>(); | 
 | 183 |  | 
| James Feist | c95cb14 | 2018-02-26 10:41:42 -0800 | [diff] [blame] | 184 |         // don't scan muxed buses async as don't want to confuse the mux | 
 | 185 |         if (isMuxBus(bus)) | 
 | 186 |         { | 
 | 187 |             get_bus_frus(file, 0x03, 0x77, bus, device); | 
 | 188 |             close(file); | 
 | 189 |         } | 
 | 190 |         else | 
 | 191 |         { | 
 | 192 |             // todo: call with boost asio? | 
 | 193 |             futures.emplace_back( | 
 | 194 |                 std::async(std::launch::async, [file, device, bus] { | 
 | 195 |                     //  i2cdetect by default uses the range 0x03 to 0x77, as | 
 | 196 |                     //  this is | 
 | 197 |                     //  what we | 
 | 198 |                     //  have tested with, use this range. Could be changed in | 
 | 199 |                     //  future. | 
 | 200 |                     get_bus_frus(file, 0x03, 0x77, bus, device); | 
 | 201 |                     close(file); | 
 | 202 |                 })); | 
 | 203 |         } | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 204 |     } | 
 | 205 |     for (auto &fut : futures) | 
 | 206 |     { | 
 | 207 |         fut.get(); // wait for all scans | 
 | 208 |     } | 
 | 209 |     return busMap; | 
 | 210 | } | 
 | 211 |  | 
 | 212 | static const std::tm intelEpoch(void) | 
 | 213 | { | 
 | 214 |     std::tm val = {0}; | 
 | 215 |     val.tm_year = 1996 - 1900; | 
 | 216 |     return val; | 
 | 217 | } | 
 | 218 |  | 
 | 219 | bool formatFru(const std::vector<char> &fruBytes, | 
 | 220 |                boost::container::flat_map<std::string, std::string> &result) | 
 | 221 | { | 
 | 222 |     static const std::vector<const char *> CHASSIS_FRU_AREAS = { | 
 | 223 |         "PART_NUMBER", "SERIAL_NUMBER", "CHASSIS_INFO_AM1", "CHASSIS_INFO_AM2"}; | 
 | 224 |  | 
 | 225 |     static const std::vector<const char *> BOARD_FRU_AREAS = { | 
 | 226 |         "MANUFACTURER", "PRODUCT_NAME", "SERIAL_NUMBER", "PART_NUMBER", | 
 | 227 |         "VERSION_ID"}; | 
 | 228 |  | 
 | 229 |     static const std::vector<const char *> PRODUCT_FRU_AREAS = { | 
 | 230 |         "MANUFACTURER",    "PRODUCT_NAME",          "PART_NUMBER", | 
 | 231 |         "PRODUCT_VERSION", "PRODUCT_SERIAL_NUMBER", "ASSET_TAG"}; | 
 | 232 |  | 
 | 233 |     size_t sum = 0; | 
 | 234 |  | 
 | 235 |     if (fruBytes.size() < 8) | 
 | 236 |     { | 
 | 237 |         return false; | 
 | 238 |     } | 
 | 239 |     std::vector<char>::const_iterator fruAreaOffsetField = fruBytes.begin(); | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 240 |     result["Common_Format_Version"] = | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 241 |         std::to_string(static_cast<int>(*fruAreaOffsetField)); | 
 | 242 |  | 
 | 243 |     const std::vector<const char *> *fieldData; | 
 | 244 |  | 
 | 245 |     for (auto &area : FRU_AREAS) | 
 | 246 |     { | 
 | 247 |         fruAreaOffsetField++; | 
 | 248 |         if (fruAreaOffsetField >= fruBytes.end()) | 
 | 249 |         { | 
 | 250 |             return false; | 
 | 251 |         } | 
 | 252 |         size_t offset = (*fruAreaOffsetField) * 8; | 
 | 253 |  | 
 | 254 |         if (offset > 1) | 
 | 255 |         { | 
 | 256 |             // +2 to skip format and length | 
 | 257 |             std::vector<char>::const_iterator fruBytesIter = | 
 | 258 |                 fruBytes.begin() + offset + 2; | 
 | 259 |  | 
 | 260 |             if (fruBytesIter >= fruBytes.end()) | 
 | 261 |             { | 
 | 262 |                 return false; | 
 | 263 |             } | 
 | 264 |  | 
 | 265 |             if (area == "CHASSIS") | 
 | 266 |             { | 
 | 267 |                 result["CHASSIS_TYPE"] = | 
 | 268 |                     std::to_string(static_cast<int>(*fruBytesIter)); | 
 | 269 |                 fruBytesIter += 1; | 
 | 270 |                 fieldData = &CHASSIS_FRU_AREAS; | 
 | 271 |             } | 
 | 272 |             else if (area == "BOARD") | 
 | 273 |             { | 
 | 274 |                 result["BOARD_LANGUAGE_CODE"] = | 
 | 275 |                     std::to_string(static_cast<int>(*fruBytesIter)); | 
 | 276 |                 fruBytesIter += 1; | 
 | 277 |                 if (fruBytesIter >= fruBytes.end()) | 
 | 278 |                 { | 
 | 279 |                     return false; | 
 | 280 |                 } | 
 | 281 |  | 
 | 282 |                 unsigned int minutes = *fruBytesIter | | 
 | 283 |                                        *(fruBytesIter + 1) << 8 | | 
 | 284 |                                        *(fruBytesIter + 2) << 16; | 
 | 285 |                 std::tm fruTime = intelEpoch(); | 
 | 286 |                 time_t timeValue = mktime(&fruTime); | 
 | 287 |                 timeValue += minutes * 60; | 
 | 288 |                 fruTime = *gmtime(&timeValue); | 
 | 289 |  | 
 | 290 |                 result["BOARD_MANUFACTURE_DATE"] = asctime(&fruTime); | 
 | 291 |                 result["BOARD_MANUFACTURE_DATE"] | 
 | 292 |                     .pop_back(); // remove trailing null | 
 | 293 |                 fruBytesIter += 3; | 
 | 294 |                 fieldData = &BOARD_FRU_AREAS; | 
 | 295 |             } | 
 | 296 |             else if (area == "PRODUCT") | 
 | 297 |             { | 
 | 298 |                 result["PRODUCT_LANGUAGE_CODE"] = | 
 | 299 |                     std::to_string(static_cast<int>(*fruBytesIter)); | 
 | 300 |                 fruBytesIter += 1; | 
 | 301 |                 fieldData = &PRODUCT_FRU_AREAS; | 
 | 302 |             } | 
 | 303 |             else | 
 | 304 |             { | 
 | 305 |                 continue; | 
 | 306 |             } | 
 | 307 |             for (auto &field : *fieldData) | 
 | 308 |             { | 
 | 309 |                 if (fruBytesIter >= fruBytes.end()) | 
 | 310 |                 { | 
 | 311 |                     return false; | 
 | 312 |                 } | 
 | 313 |  | 
 | 314 |                 size_t length = *fruBytesIter & 0x3f; | 
 | 315 |                 fruBytesIter += 1; | 
 | 316 |  | 
 | 317 |                 if (fruBytesIter >= fruBytes.end()) | 
 | 318 |                 { | 
 | 319 |                     return false; | 
 | 320 |                 } | 
 | 321 |  | 
 | 322 |                 result[std::string(area) + "_" + field] = | 
 | 323 |                     std::string(fruBytesIter, fruBytesIter + length); | 
 | 324 |                 fruBytesIter += length; | 
 | 325 |                 if (fruBytesIter >= fruBytes.end()) | 
 | 326 |                 { | 
 | 327 |                     std::cerr << "Warning Fru Length Mismatch:\n    "; | 
 | 328 |                     for (auto &c : fruBytes) | 
 | 329 |                     { | 
 | 330 |                         std::cerr << c; | 
 | 331 |                     } | 
 | 332 |                     std::cerr << "\n"; | 
 | 333 |                     if (DEBUG) | 
 | 334 |                     { | 
 | 335 |                         for (auto &keyPair : result) | 
 | 336 |                         { | 
 | 337 |                             std::cerr << keyPair.first << " : " | 
 | 338 |                                       << keyPair.second << "\n"; | 
 | 339 |                         } | 
 | 340 |                     } | 
 | 341 |                     return false; | 
 | 342 |                 } | 
 | 343 |             } | 
 | 344 |         } | 
 | 345 |     } | 
 | 346 |  | 
 | 347 |     return true; | 
 | 348 | } | 
 | 349 |  | 
 | 350 | void AddFruObjectToDbus( | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 351 |     std::shared_ptr<sdbusplus::asio::connection> dbusConn, | 
 | 352 |     std::vector<char> &device, sdbusplus::asio::object_server &objServer, | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 353 |     boost::container::flat_map<std::pair<size_t, size_t>, | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 354 |                                std::shared_ptr<sdbusplus::asio::dbus_interface>> | 
 | 355 |         &dbusInterfaceMap, | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 356 |     int bus, size_t address) | 
 | 357 | { | 
 | 358 |     boost::container::flat_map<std::string, std::string> formattedFru; | 
 | 359 |     if (!formatFru(device, formattedFru)) | 
 | 360 |     { | 
 | 361 |         std::cerr << "failed to format fru for device at bus " << std::hex | 
 | 362 |                   << bus << "address " << address << "\n"; | 
 | 363 |         return; | 
 | 364 |     } | 
 | 365 |     auto productNameFind = formattedFru.find("BOARD_PRODUCT_NAME"); | 
 | 366 |     std::string productName; | 
 | 367 |     if (productNameFind == formattedFru.end()) | 
 | 368 |     { | 
 | 369 |         productNameFind = formattedFru.find("PRODUCT_PRODUCT_NAME"); | 
 | 370 |     } | 
 | 371 |     if (productNameFind != formattedFru.end()) | 
 | 372 |     { | 
 | 373 |         productName = productNameFind->second; | 
| James Feist | 3f8a278 | 2018-02-12 09:24:42 -0800 | [diff] [blame] | 374 |         std::regex illegalObject("[^A-Za-z0-9_]"); | 
 | 375 |         productName = std::regex_replace(productName, illegalObject, "_"); | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 376 |     } | 
 | 377 |     else | 
 | 378 |     { | 
 | 379 |         productName = "UNKNOWN" + std::to_string(UNKNOWN_BUS_OBJECT_COUNT); | 
 | 380 |         UNKNOWN_BUS_OBJECT_COUNT++; | 
 | 381 |     } | 
 | 382 |  | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 383 |     productName = "/xyz/openbmc_project/FruDevice/" + productName; | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 384 |     // avoid duplicates by checking to see if on a mux | 
| James Feist | 79e9c0b | 2018-03-15 15:21:17 -0700 | [diff] [blame] | 385 |     if (bus > 0) | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 386 |     { | 
| James Feist | 79e9c0b | 2018-03-15 15:21:17 -0700 | [diff] [blame] | 387 |         size_t index = 0; | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 388 |         for (auto const &busIface : dbusInterfaceMap) | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 389 |         { | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 390 |             if ((busIface.second->get_object_path() == productName)) | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 391 |             { | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 392 |                 if (isMuxBus(bus) && address == busIface.first.second) | 
| James Feist | 79e9c0b | 2018-03-15 15:21:17 -0700 | [diff] [blame] | 393 |                 { | 
 | 394 |                     continue; | 
 | 395 |                 } | 
 | 396 |                 // add underscore _index for the same object path on dbus | 
 | 397 |                 std::string strIndex = std::to_string(index); | 
 | 398 |                 if (index > 0) | 
 | 399 |                 { | 
 | 400 |                     productName.substr(0, productName.size() - strIndex.size()); | 
 | 401 |                 } | 
 | 402 |                 else | 
 | 403 |                 { | 
 | 404 |                     productName += "_"; | 
 | 405 |                 } | 
 | 406 |                 productName += std::to_string(index++); | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 407 |             } | 
 | 408 |         } | 
 | 409 |     } | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 410 |  | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 411 |     std::shared_ptr<sdbusplus::asio::dbus_interface> iface = | 
 | 412 |         objServer.add_interface(productName, "xyz.openbmc_project.FruDevice"); | 
 | 413 |     dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface; | 
 | 414 |  | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 415 |     for (auto &property : formattedFru) | 
 | 416 |     { | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 417 |  | 
| Jae Hyun Yoo | 3936e7a | 2018-03-23 17:26:16 -0700 | [diff] [blame] | 418 |         std::regex_replace(property.second.begin(), property.second.begin(), | 
 | 419 |                            property.second.end(), NON_ASCII_REGEX, "_"); | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 420 |         if (property.second.empty()) | 
 | 421 |         { | 
 | 422 |             continue; | 
 | 423 |         } | 
 | 424 |         std::string key = | 
 | 425 |             std::regex_replace(property.first, NON_ASCII_REGEX, "_"); | 
 | 426 |         if (!iface->register_property(key, property.second + '\0')) | 
 | 427 |         { | 
 | 428 |             std::cerr << "illegal key: " << key << "\n"; | 
 | 429 |         } | 
| Jae Hyun Yoo | 3936e7a | 2018-03-23 17:26:16 -0700 | [diff] [blame] | 430 |         if (DEBUG) | 
 | 431 |         { | 
 | 432 |             std::cout << property.first << ": " << property.second << "\n"; | 
 | 433 |         } | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 434 |     } | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 435 |  | 
 | 436 |     // baseboard will be 0, 0 | 
 | 437 |     std::stringstream data; | 
 | 438 |     data << "0x" << std::hex << bus; | 
 | 439 |     iface->register_property("BUS", data.str()); | 
 | 440 |     data.str(""); | 
 | 441 |     data << "0x" << std::hex << address; | 
 | 442 |     iface->register_property("ADDRESS", data.str()); | 
 | 443 |  | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 444 |     iface->initialize(); | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 445 | } | 
 | 446 |  | 
 | 447 | static bool readBaseboardFru(std::vector<char> &baseboardFru) | 
 | 448 | { | 
 | 449 |     // try to read baseboard fru from file | 
 | 450 |     std::ifstream baseboardFruFile(BASEBOARD_FRU_LOCATION, std::ios::binary); | 
 | 451 |     if (baseboardFruFile.good()) | 
 | 452 |     { | 
 | 453 |         baseboardFruFile.seekg(0, std::ios_base::end); | 
 | 454 |         std::streampos fileSize = baseboardFruFile.tellg(); | 
 | 455 |         baseboardFru.resize(fileSize); | 
 | 456 |         baseboardFruFile.seekg(0, std::ios_base::beg); | 
 | 457 |         baseboardFruFile.read(baseboardFru.data(), fileSize); | 
 | 458 |     } | 
 | 459 |     else | 
 | 460 |     { | 
 | 461 |         return false; | 
 | 462 |     } | 
 | 463 |     return true; | 
 | 464 | } | 
 | 465 |  | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 466 | void rescanBusses( | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 467 |     BusMap &busMap, | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 468 |     boost::container::flat_map<std::pair<size_t, size_t>, | 
 | 469 |                                std::shared_ptr<sdbusplus::asio::dbus_interface>> | 
 | 470 |         &dbusInterfaceMap, | 
 | 471 |     std::shared_ptr<sdbusplus::asio::connection> systemBus, | 
 | 472 |     sdbusplus::asio::object_server &objServer, | 
 | 473 |     std::atomic_bool &pendingCallback) | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 474 | { | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 475 |  | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 476 |     do | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 477 |     { | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 478 |         auto devDir = fs::path("/dev/"); | 
 | 479 |         auto matchString = std::string("i2c*"); | 
 | 480 |         std::vector<fs::path> i2cBuses; | 
 | 481 |         pendingCallback = false; | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 482 |  | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 483 |         if (!find_files(devDir, matchString, i2cBuses, 0)) | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 484 |         { | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 485 |             std::cerr << "unable to find i2c devices\n"; | 
 | 486 |             return; | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 487 |         } | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 488 |         // scanning muxes in reverse order seems to have adverse effects | 
 | 489 |         // sorting this list seems to be a fix for that | 
 | 490 |         std::sort(i2cBuses.begin(), i2cBuses.end()); | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 491 |         busMap = FindI2CDevices(i2cBuses); | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 492 |  | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 493 |         for (auto &busIface : dbusInterfaceMap) | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 494 |         { | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 495 |             objServer.remove_interface(busIface.second); | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 496 |         } | 
 | 497 |  | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 498 |         dbusInterfaceMap.clear(); | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 499 |         UNKNOWN_BUS_OBJECT_COUNT = 0; | 
 | 500 |  | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 501 |         // todo, get this from a more sensable place | 
 | 502 |         std::vector<char> baseboardFru; | 
 | 503 |         if (readBaseboardFru(baseboardFru)) | 
 | 504 |         { | 
 | 505 |             boost::container::flat_map<int, std::vector<char>> baseboardDev; | 
 | 506 |             baseboardDev.emplace(0, baseboardFru); | 
 | 507 |             busMap[0] = std::make_shared<DeviceMap>(baseboardDev); | 
 | 508 |         } | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 509 |         for (auto &devicemap : busMap) | 
 | 510 |         { | 
 | 511 |             for (auto &device : *devicemap.second) | 
 | 512 |             { | 
 | 513 |                 AddFruObjectToDbus(systemBus, device.second, objServer, | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 514 |                                    dbusInterfaceMap, devicemap.first, | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 515 |                                    device.first); | 
 | 516 |             } | 
 | 517 |         } | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 518 |     } while (pendingCallback); | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 519 | } | 
 | 520 |  | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 521 | int main(int argc, char **argv) | 
 | 522 | { | 
 | 523 |     auto devDir = fs::path("/dev/"); | 
 | 524 |     auto matchString = std::string("i2c*"); | 
 | 525 |     std::vector<fs::path> i2cBuses; | 
 | 526 |  | 
 | 527 |     if (!find_files(devDir, matchString, i2cBuses, 0)) | 
 | 528 |     { | 
 | 529 |         std::cerr << "unable to find i2c devices\n"; | 
 | 530 |         return 1; | 
 | 531 |     } | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 532 |  | 
 | 533 |     boost::asio::io_service io; | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 534 |     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); | 
 | 535 |     auto objServer = sdbusplus::asio::object_server(systemBus); | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 536 |     systemBus->request_name("com.intel.FruDevice"); | 
 | 537 |  | 
| Gunnar Mills | b3e42fe | 2018-06-13 15:48:27 -0500 | [diff] [blame] | 538 |     // this is a map with keys of pair(bus number, address) and values of the | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 539 |     // object on dbus | 
 | 540 |     boost::container::flat_map<std::pair<size_t, size_t>, | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 541 |                                std::shared_ptr<sdbusplus::asio::dbus_interface>> | 
 | 542 |         dbusInterfaceMap; | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 543 |     BusMap busmap; | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 544 |  | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 545 |     std::shared_ptr<sdbusplus::asio::dbus_interface> iface = | 
 | 546 |         objServer.add_interface("/xyz/openbmc_project/FruDevice", | 
 | 547 |                                 "xyz.openbmc_project.FruDeviceManager"); | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 548 |  | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 549 |     std::atomic_bool threadRunning(false); | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 550 |     std::atomic_bool pendingCallback(false); | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 551 |     std::future<void> future; | 
 | 552 |  | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 553 |     iface->register_method("ReScan", [&]() { | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 554 |         bool notRunning = false; | 
 | 555 |         if (threadRunning.compare_exchange_strong(notRunning, true)) | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 556 |         { | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 557 |             future = std::async(std::launch::async, [&] { | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 558 |                 rescanBusses(busmap, dbusInterfaceMap, systemBus, objServer, | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 559 |                              pendingCallback); | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 560 |                 threadRunning = false; | 
 | 561 |             }); | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 562 |         } | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 563 |         else | 
 | 564 |         { | 
 | 565 |             pendingCallback = true; | 
 | 566 |         } | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 567 |         return; | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 568 |     }); | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 569 |  | 
 | 570 |     iface->register_method( | 
 | 571 |         "GetRawFru", [&](const uint8_t &bus, const uint8_t &address) { | 
 | 572 |             auto deviceMap = busmap.find(bus); | 
 | 573 |             if (deviceMap == busmap.end()) | 
 | 574 |             { | 
 | 575 |                 throw sdbusplus::xyz::openbmc_project::Common::Error:: | 
 | 576 |                     InvalidArgument(); | 
 | 577 |             } | 
 | 578 |             auto device = deviceMap->second->find(address); | 
 | 579 |             if (device == deviceMap->second->end()) | 
 | 580 |             { | 
 | 581 |                 throw sdbusplus::xyz::openbmc_project::Common::Error:: | 
 | 582 |                     InvalidArgument(); | 
 | 583 |             } | 
 | 584 |             std::vector<uint8_t> &ret = | 
 | 585 |                 reinterpret_cast<std::vector<uint8_t> &>(device->second); | 
 | 586 |             return ret; | 
 | 587 |         }); | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 588 |     iface->initialize(); | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 589 |  | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 590 |     std::function<void(sdbusplus::message::message & message)> eventHandler = | 
 | 591 |         [&](sdbusplus::message::message &message) { | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 592 |             std::string objectName; | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 593 |             boost::container::flat_map< | 
 | 594 |                 std::string, sdbusplus::message::variant< | 
 | 595 |                                  std::string, bool, int64_t, uint64_t, double>> | 
 | 596 |                 values; | 
 | 597 |             message.read(objectName, values); | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 598 |             auto findPgood = values.find("pgood"); | 
 | 599 |             if (findPgood != values.end()) | 
 | 600 |             { | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 601 |                 bool notRunning = false; | 
 | 602 |                 if (threadRunning.compare_exchange_strong(notRunning, true)) | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 603 |                 { | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 604 |                     future = std::async(std::launch::async, [&] { | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 605 |                         rescanBusses(busmap, dbusInterfaceMap, systemBus, | 
 | 606 |                                      objServer, pendingCallback); | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 607 |                         threadRunning = false; | 
 | 608 |                     }); | 
 | 609 |                 } | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 610 |                 else | 
 | 611 |                 { | 
 | 612 |                     pendingCallback = true; | 
 | 613 |                 } | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 614 |             } | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 615 |         }; | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 616 |  | 
 | 617 |     sdbusplus::bus::match::match powerMatch = sdbusplus::bus::match::match( | 
 | 618 |         static_cast<sdbusplus::bus::bus &>(*systemBus), | 
 | 619 |         "type='signal',interface='org.freedesktop.DBus.Properties',path_" | 
| Kuiying Wang | 1dc4310 | 2018-05-09 15:09:29 +0800 | [diff] [blame] | 620 |         "namespace='/xyz/openbmc_project/Chassis/Control/" | 
 | 621 |         "power0',arg0='xyz.openbmc_project.Chassis.Control.Power'", | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 622 |         eventHandler); | 
 | 623 |  | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 624 |     int fd = inotify_init(); | 
 | 625 |     int wd = inotify_add_watch(fd, I2C_DEV_LOCATION, | 
 | 626 |                                IN_CREATE | IN_MOVED_TO | IN_DELETE); | 
 | 627 |     std::array<char, 4096> readBuffer; | 
 | 628 |     std::string pendingBuffer; | 
 | 629 |     // monitor for new i2c devices | 
 | 630 |     boost::asio::posix::stream_descriptor dirWatch(io, fd); | 
 | 631 |     std::function<void(const boost::system::error_code, std::size_t)> | 
 | 632 |         watchI2cBusses = [&](const boost::system::error_code &ec, | 
 | 633 |                              std::size_t bytes_transferred) { | 
 | 634 |             if (ec) | 
 | 635 |             { | 
 | 636 |                 std::cout << "Callback Error " << ec << "\n"; | 
 | 637 |                 return; | 
 | 638 |             } | 
 | 639 |             pendingBuffer += std::string(readBuffer.data(), bytes_transferred); | 
 | 640 |             bool devChange = false; | 
 | 641 |             while (pendingBuffer.size() > sizeof(inotify_event)) | 
 | 642 |             { | 
 | 643 |                 const inotify_event *iEvent = | 
 | 644 |                     reinterpret_cast<const inotify_event *>( | 
 | 645 |                         pendingBuffer.data()); | 
 | 646 |                 switch (iEvent->mask) | 
 | 647 |                 { | 
| James Feist | 9eb0b58 | 2018-04-27 12:15:46 -0700 | [diff] [blame] | 648 |                     case IN_CREATE: | 
 | 649 |                     case IN_MOVED_TO: | 
 | 650 |                     case IN_DELETE: | 
 | 651 |                         if (boost::starts_with(std::string(iEvent->name), | 
 | 652 |                                                "i2c")) | 
 | 653 |                         { | 
 | 654 |                             devChange = true; | 
 | 655 |                         } | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 656 |                 } | 
 | 657 |  | 
 | 658 |                 pendingBuffer.erase(0, sizeof(inotify_event) + iEvent->len); | 
 | 659 |             } | 
 | 660 |             bool notRunning = false; | 
 | 661 |             if (devChange && | 
 | 662 |                 threadRunning.compare_exchange_strong(notRunning, true)) | 
 | 663 |             { | 
 | 664 |                 future = std::async(std::launch::async, [&] { | 
 | 665 |                     std::this_thread::sleep_for(std::chrono::seconds(2)); | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 666 |                     rescanBusses(busmap, dbusInterfaceMap, systemBus, objServer, | 
| James Feist | 4131aea | 2018-03-09 09:47:30 -0800 | [diff] [blame] | 667 |                                  pendingCallback); | 
 | 668 |                     threadRunning = false; | 
 | 669 |                 }); | 
 | 670 |             } | 
 | 671 |             else if (devChange) | 
 | 672 |             { | 
 | 673 |                 pendingCallback = true; | 
 | 674 |             } | 
 | 675 |             dirWatch.async_read_some(boost::asio::buffer(readBuffer), | 
 | 676 |                                      watchI2cBusses); | 
 | 677 |         }; | 
 | 678 |  | 
 | 679 |     dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses); | 
| Gunnar Mills | b3e42fe | 2018-06-13 15:48:27 -0500 | [diff] [blame] | 680 |     // run the initial scan | 
| James Feist | 2a9d6db | 2018-04-27 15:48:28 -0700 | [diff] [blame^] | 681 |     rescanBusses(busmap, dbusInterfaceMap, systemBus, objServer, | 
 | 682 |                  pendingCallback); | 
| James Feist | 918e18c | 2018-02-13 15:51:07 -0800 | [diff] [blame] | 683 |  | 
| James Feist | 3cb5fec | 2018-01-23 14:41:51 -0800 | [diff] [blame] | 684 |     io.run(); | 
 | 685 |     return 0; | 
 | 686 | } |