13cbff97fSChristopher Meis /*
23cbff97fSChristopher Meis // Copyright (c) 2018 Intel Corporation
33cbff97fSChristopher Meis //
43cbff97fSChristopher Meis // Licensed under the Apache License, Version 2.0 (the "License");
53cbff97fSChristopher Meis // you may not use this file except in compliance with the License.
63cbff97fSChristopher Meis // You may obtain a copy of the License at
73cbff97fSChristopher Meis //
83cbff97fSChristopher Meis // http://www.apache.org/licenses/LICENSE-2.0
93cbff97fSChristopher Meis //
103cbff97fSChristopher Meis // Unless required by applicable law or agreed to in writing, software
113cbff97fSChristopher Meis // distributed under the License is distributed on an "AS IS" BASIS,
123cbff97fSChristopher Meis // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133cbff97fSChristopher Meis // See the License for the specific language governing permissions and
143cbff97fSChristopher Meis // limitations under the License.
153cbff97fSChristopher Meis */
163cbff97fSChristopher Meis /// \file fru_device.cpp
173cbff97fSChristopher Meis
183cbff97fSChristopher Meis #include "../utils.hpp"
193cbff97fSChristopher Meis #include "fru_utils.hpp"
203cbff97fSChristopher Meis
213cbff97fSChristopher Meis #include <fcntl.h>
223cbff97fSChristopher Meis #include <sys/inotify.h>
233cbff97fSChristopher Meis #include <sys/ioctl.h>
243cbff97fSChristopher Meis
253cbff97fSChristopher Meis #include <boost/algorithm/string/predicate.hpp>
263cbff97fSChristopher Meis #include <boost/asio/io_context.hpp>
273cbff97fSChristopher Meis #include <boost/asio/steady_timer.hpp>
283cbff97fSChristopher Meis #include <boost/container/flat_map.hpp>
293cbff97fSChristopher Meis #include <nlohmann/json.hpp>
303cbff97fSChristopher Meis #include <phosphor-logging/lg2.hpp>
313cbff97fSChristopher Meis #include <sdbusplus/asio/connection.hpp>
323cbff97fSChristopher Meis #include <sdbusplus/asio/object_server.hpp>
333cbff97fSChristopher Meis
343cbff97fSChristopher Meis #include <array>
353cbff97fSChristopher Meis #include <cerrno>
363cbff97fSChristopher Meis #include <charconv>
373cbff97fSChristopher Meis #include <chrono>
383cbff97fSChristopher Meis #include <ctime>
393cbff97fSChristopher Meis #include <filesystem>
403cbff97fSChristopher Meis #include <fstream>
413cbff97fSChristopher Meis #include <functional>
423cbff97fSChristopher Meis #include <future>
433cbff97fSChristopher Meis #include <iomanip>
443cbff97fSChristopher Meis #include <iostream>
453cbff97fSChristopher Meis #include <limits>
463cbff97fSChristopher Meis #include <map>
473cbff97fSChristopher Meis #include <optional>
483cbff97fSChristopher Meis #include <regex>
493cbff97fSChristopher Meis #include <set>
503cbff97fSChristopher Meis #include <sstream>
513cbff97fSChristopher Meis #include <string>
523cbff97fSChristopher Meis #include <thread>
533cbff97fSChristopher Meis #include <utility>
543cbff97fSChristopher Meis #include <variant>
553cbff97fSChristopher Meis #include <vector>
563cbff97fSChristopher Meis
573cbff97fSChristopher Meis extern "C"
583cbff97fSChristopher Meis {
593cbff97fSChristopher Meis #include <i2c/smbus.h>
603cbff97fSChristopher Meis #include <linux/i2c-dev.h>
613cbff97fSChristopher Meis }
623cbff97fSChristopher Meis
633cbff97fSChristopher Meis namespace fs = std::filesystem;
643cbff97fSChristopher Meis constexpr size_t maxFruSize = 512;
653cbff97fSChristopher Meis constexpr size_t maxEepromPageIndex = 255;
663cbff97fSChristopher Meis constexpr size_t busTimeoutSeconds = 10;
673cbff97fSChristopher Meis
683cbff97fSChristopher Meis constexpr const char* blocklistPath = PACKAGE_DIR "blacklist.json";
693cbff97fSChristopher Meis
703cbff97fSChristopher Meis const static constexpr char* baseboardFruLocation =
713cbff97fSChristopher Meis "/etc/fru/baseboard.fru.bin";
723cbff97fSChristopher Meis
733cbff97fSChristopher Meis const static constexpr char* i2CDevLocation = "/dev";
743cbff97fSChristopher Meis
753cbff97fSChristopher Meis constexpr const char* fruDevice16BitDetectMode = FRU_DEVICE_16BITDETECTMODE;
763cbff97fSChristopher Meis
773cbff97fSChristopher Meis // TODO Refactor these to not be globals
783cbff97fSChristopher Meis // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)
793cbff97fSChristopher Meis static boost::container::flat_map<size_t, std::optional<std::set<size_t>>>
803cbff97fSChristopher Meis busBlocklist;
813cbff97fSChristopher Meis struct FindDevicesWithCallback;
823cbff97fSChristopher Meis
833cbff97fSChristopher Meis static boost::container::flat_map<
843cbff97fSChristopher Meis std::pair<size_t, size_t>, std::shared_ptr<sdbusplus::asio::dbus_interface>>
853cbff97fSChristopher Meis foundDevices;
863cbff97fSChristopher Meis
873cbff97fSChristopher Meis static boost::container::flat_map<size_t, std::set<size_t>> failedAddresses;
883cbff97fSChristopher Meis static boost::container::flat_map<size_t, std::set<size_t>> fruAddresses;
893cbff97fSChristopher Meis
903cbff97fSChristopher Meis boost::asio::io_context io;
913cbff97fSChristopher Meis // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
923cbff97fSChristopher Meis
933cbff97fSChristopher Meis bool updateFRUProperty(
943cbff97fSChristopher Meis const std::string& updatePropertyReq, uint32_t bus, uint32_t address,
953cbff97fSChristopher Meis const std::string& propertyName,
963cbff97fSChristopher Meis boost::container::flat_map<
973cbff97fSChristopher Meis std::pair<size_t, size_t>,
983cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
993cbff97fSChristopher Meis size_t& unknownBusObjectCount, const bool& powerIsOn,
1003cbff97fSChristopher Meis sdbusplus::asio::object_server& objServer,
1013cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::connection>& systemBus);
1023cbff97fSChristopher Meis
1033cbff97fSChristopher Meis // Given a bus/address, produce the path in sysfs for an eeprom.
getEepromPath(size_t bus,size_t address)1043cbff97fSChristopher Meis static std::string getEepromPath(size_t bus, size_t address)
1053cbff97fSChristopher Meis {
1063cbff97fSChristopher Meis std::stringstream output;
1073cbff97fSChristopher Meis output << "/sys/bus/i2c/devices/" << bus << "-" << std::right
1083cbff97fSChristopher Meis << std::setfill('0') << std::setw(4) << std::hex << address
1093cbff97fSChristopher Meis << "/eeprom";
1103cbff97fSChristopher Meis return output.str();
1113cbff97fSChristopher Meis }
1123cbff97fSChristopher Meis
hasEepromFile(size_t bus,size_t address)1133cbff97fSChristopher Meis static bool hasEepromFile(size_t bus, size_t address)
1143cbff97fSChristopher Meis {
1153cbff97fSChristopher Meis auto path = getEepromPath(bus, address);
1163cbff97fSChristopher Meis try
1173cbff97fSChristopher Meis {
1183cbff97fSChristopher Meis return fs::exists(path);
1193cbff97fSChristopher Meis }
1203cbff97fSChristopher Meis catch (...)
1213cbff97fSChristopher Meis {
1223cbff97fSChristopher Meis return false;
1233cbff97fSChristopher Meis }
1243cbff97fSChristopher Meis }
1253cbff97fSChristopher Meis
readFromEeprom(int fd,off_t offset,size_t len,uint8_t * buf)1263cbff97fSChristopher Meis static int64_t readFromEeprom(int fd, off_t offset, size_t len, uint8_t* buf)
1273cbff97fSChristopher Meis {
1283cbff97fSChristopher Meis auto result = lseek(fd, offset, SEEK_SET);
1293cbff97fSChristopher Meis if (result < 0)
1303cbff97fSChristopher Meis {
1313cbff97fSChristopher Meis std::cerr << "failed to seek\n";
1323cbff97fSChristopher Meis return -1;
1333cbff97fSChristopher Meis }
1343cbff97fSChristopher Meis
1353cbff97fSChristopher Meis return read(fd, buf, len);
1363cbff97fSChristopher Meis }
1373cbff97fSChristopher Meis
busStrToInt(const std::string_view busName)1383cbff97fSChristopher Meis static int busStrToInt(const std::string_view busName)
1393cbff97fSChristopher Meis {
1403cbff97fSChristopher Meis auto findBus = busName.rfind('-');
1413cbff97fSChristopher Meis if (findBus == std::string::npos)
1423cbff97fSChristopher Meis {
1433cbff97fSChristopher Meis return -1;
1443cbff97fSChristopher Meis }
1453cbff97fSChristopher Meis std::string_view num = busName.substr(findBus + 1);
1463cbff97fSChristopher Meis int val = 0;
1473cbff97fSChristopher Meis std::from_chars(num.data(), num.data() + num.size(), val);
1483cbff97fSChristopher Meis return val;
1493cbff97fSChristopher Meis }
1503cbff97fSChristopher Meis
getRootBus(size_t bus)1513cbff97fSChristopher Meis static int getRootBus(size_t bus)
1523cbff97fSChristopher Meis {
1533cbff97fSChristopher Meis auto ec = std::error_code();
1543cbff97fSChristopher Meis auto path = std::filesystem::read_symlink(
1553cbff97fSChristopher Meis std::filesystem::path(
1563cbff97fSChristopher Meis "/sys/bus/i2c/devices/i2c-" + std::to_string(bus) + "/mux_device"),
1573cbff97fSChristopher Meis ec);
1583cbff97fSChristopher Meis if (ec)
1593cbff97fSChristopher Meis {
1603cbff97fSChristopher Meis return -1;
1613cbff97fSChristopher Meis }
1623cbff97fSChristopher Meis
1633cbff97fSChristopher Meis std::string filename = path.filename();
1643cbff97fSChristopher Meis auto findBus = filename.find('-');
1653cbff97fSChristopher Meis if (findBus == std::string::npos)
1663cbff97fSChristopher Meis {
1673cbff97fSChristopher Meis return -1;
1683cbff97fSChristopher Meis }
1693cbff97fSChristopher Meis return std::stoi(filename.substr(0, findBus));
1703cbff97fSChristopher Meis }
1713cbff97fSChristopher Meis
isMuxBus(size_t bus)1723cbff97fSChristopher Meis static bool isMuxBus(size_t bus)
1733cbff97fSChristopher Meis {
1743cbff97fSChristopher Meis auto ec = std::error_code();
1753cbff97fSChristopher Meis auto isSymlink =
1763cbff97fSChristopher Meis is_symlink(std::filesystem::path("/sys/bus/i2c/devices/i2c-" +
1773cbff97fSChristopher Meis std::to_string(bus) + "/mux_device"),
1783cbff97fSChristopher Meis ec);
1793cbff97fSChristopher Meis return (!ec && isSymlink);
1803cbff97fSChristopher Meis }
1813cbff97fSChristopher Meis
makeProbeInterface(size_t bus,size_t address,sdbusplus::asio::object_server & objServer)1823cbff97fSChristopher Meis static void makeProbeInterface(size_t bus, size_t address,
1833cbff97fSChristopher Meis sdbusplus::asio::object_server& objServer)
1843cbff97fSChristopher Meis {
1853cbff97fSChristopher Meis if (isMuxBus(bus))
1863cbff97fSChristopher Meis {
1873cbff97fSChristopher Meis return; // the mux buses are random, no need to publish
1883cbff97fSChristopher Meis }
1893cbff97fSChristopher Meis auto [it, success] = foundDevices.emplace(
1903cbff97fSChristopher Meis std::make_pair(bus, address),
1913cbff97fSChristopher Meis objServer.add_interface(
1923cbff97fSChristopher Meis "/xyz/openbmc_project/FruDevice/" + std::to_string(bus) + "_" +
1933cbff97fSChristopher Meis std::to_string(address),
1943cbff97fSChristopher Meis "xyz.openbmc_project.Inventory.Item.I2CDevice"));
1953cbff97fSChristopher Meis if (!success)
1963cbff97fSChristopher Meis {
1973cbff97fSChristopher Meis return; // already added
1983cbff97fSChristopher Meis }
1993cbff97fSChristopher Meis it->second->register_property("Bus", bus);
2003cbff97fSChristopher Meis it->second->register_property("Address", address);
2013cbff97fSChristopher Meis it->second->initialize();
2023cbff97fSChristopher Meis }
2033cbff97fSChristopher Meis
2043cbff97fSChristopher Meis // Issue an I2C transaction to first write to_target_buf_len bytes,then read
2053cbff97fSChristopher Meis // from_target_buf_len bytes.
i2cSmbusWriteThenRead(int file,uint16_t address,uint8_t * toTargetBuf,uint8_t toTargetBufLen,uint8_t * fromTargetBuf,uint8_t fromTargetBufLen)2063cbff97fSChristopher Meis static int i2cSmbusWriteThenRead(
2073cbff97fSChristopher Meis int file, uint16_t address, uint8_t* toTargetBuf, uint8_t toTargetBufLen,
2083cbff97fSChristopher Meis uint8_t* fromTargetBuf, uint8_t fromTargetBufLen)
2093cbff97fSChristopher Meis {
2103cbff97fSChristopher Meis if (toTargetBuf == nullptr || toTargetBufLen == 0 ||
2113cbff97fSChristopher Meis fromTargetBuf == nullptr || fromTargetBufLen == 0)
2123cbff97fSChristopher Meis {
2133cbff97fSChristopher Meis return -1;
2143cbff97fSChristopher Meis }
2153cbff97fSChristopher Meis
2163cbff97fSChristopher Meis constexpr size_t smbusWriteThenReadMsgCount = 2;
2173cbff97fSChristopher Meis std::array<struct i2c_msg, smbusWriteThenReadMsgCount> msgs{};
2183cbff97fSChristopher Meis struct i2c_rdwr_ioctl_data rdwr{};
2193cbff97fSChristopher Meis
2203cbff97fSChristopher Meis msgs[0].addr = address;
2213cbff97fSChristopher Meis msgs[0].flags = 0;
2223cbff97fSChristopher Meis msgs[0].len = toTargetBufLen;
2233cbff97fSChristopher Meis msgs[0].buf = toTargetBuf;
2243cbff97fSChristopher Meis msgs[1].addr = address;
2253cbff97fSChristopher Meis msgs[1].flags = I2C_M_RD;
2263cbff97fSChristopher Meis msgs[1].len = fromTargetBufLen;
2273cbff97fSChristopher Meis msgs[1].buf = fromTargetBuf;
2283cbff97fSChristopher Meis
2293cbff97fSChristopher Meis rdwr.msgs = msgs.data();
2303cbff97fSChristopher Meis rdwr.nmsgs = msgs.size();
2313cbff97fSChristopher Meis
2323cbff97fSChristopher Meis int ret = ioctl(file, I2C_RDWR, &rdwr);
2333cbff97fSChristopher Meis
2343cbff97fSChristopher Meis return (ret == static_cast<int>(msgs.size())) ? msgs[1].len : -1;
2353cbff97fSChristopher Meis }
2363cbff97fSChristopher Meis
readData(bool is16bit,bool isBytewise,int file,uint16_t address,off_t offset,size_t len,uint8_t * buf)2373cbff97fSChristopher Meis static int64_t readData(bool is16bit, bool isBytewise, int file,
2383cbff97fSChristopher Meis uint16_t address, off_t offset, size_t len,
2393cbff97fSChristopher Meis uint8_t* buf)
2403cbff97fSChristopher Meis {
2413cbff97fSChristopher Meis if (!is16bit)
2423cbff97fSChristopher Meis {
2433cbff97fSChristopher Meis if (!isBytewise)
2443cbff97fSChristopher Meis {
2453cbff97fSChristopher Meis return i2c_smbus_read_i2c_block_data(
2463cbff97fSChristopher Meis file, static_cast<uint8_t>(offset), len, buf);
2473cbff97fSChristopher Meis }
2483cbff97fSChristopher Meis
2493cbff97fSChristopher Meis std::span<uint8_t> bufspan{buf, len};
2503cbff97fSChristopher Meis for (size_t i = 0; i < len; i++)
2513cbff97fSChristopher Meis {
2523cbff97fSChristopher Meis int byte = i2c_smbus_read_byte_data(
2533cbff97fSChristopher Meis file, static_cast<uint8_t>(offset + i));
2543cbff97fSChristopher Meis if (byte < 0)
2553cbff97fSChristopher Meis {
2563cbff97fSChristopher Meis return static_cast<int64_t>(byte);
2573cbff97fSChristopher Meis }
2583cbff97fSChristopher Meis bufspan[i] = static_cast<uint8_t>(byte);
2593cbff97fSChristopher Meis }
2603cbff97fSChristopher Meis return static_cast<int64_t>(len);
2613cbff97fSChristopher Meis }
2623cbff97fSChristopher Meis
2633cbff97fSChristopher Meis offset = htobe16(offset);
2643cbff97fSChristopher Meis // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
2653cbff97fSChristopher Meis uint8_t* u8Offset = reinterpret_cast<uint8_t*>(&offset);
2663cbff97fSChristopher Meis return i2cSmbusWriteThenRead(file, address, u8Offset, 2, buf, len);
2673cbff97fSChristopher Meis }
2683cbff97fSChristopher Meis
2693cbff97fSChristopher Meis // Mode_1:
2703cbff97fSChristopher Meis // --------
2713cbff97fSChristopher Meis // Please refer to document docs/address_size_detection_modes.md for
2723cbff97fSChristopher Meis // more details and explanations.
isDevice16BitMode1(int file)2733cbff97fSChristopher Meis static std::optional<bool> isDevice16BitMode1(int file)
2743cbff97fSChristopher Meis {
2753cbff97fSChristopher Meis // Set the higher data word address bits to 0. It's safe on 8-bit
2763cbff97fSChristopher Meis // addressing EEPROMs because it doesn't write any actual data.
2773cbff97fSChristopher Meis int ret = i2c_smbus_write_byte(file, 0);
2783cbff97fSChristopher Meis if (ret < 0)
2793cbff97fSChristopher Meis {
2803cbff97fSChristopher Meis return std::nullopt;
2813cbff97fSChristopher Meis }
2823cbff97fSChristopher Meis
2833cbff97fSChristopher Meis /* Get first byte */
2843cbff97fSChristopher Meis int byte1 = i2c_smbus_read_byte_data(file, 0);
2853cbff97fSChristopher Meis if (byte1 < 0)
2863cbff97fSChristopher Meis {
2873cbff97fSChristopher Meis return std::nullopt;
2883cbff97fSChristopher Meis }
2893cbff97fSChristopher Meis /* Read 7 more bytes, it will read same first byte in case of
2903cbff97fSChristopher Meis * 8 bit but it will read next byte in case of 16 bit
2913cbff97fSChristopher Meis */
2923cbff97fSChristopher Meis for (int i = 0; i < 7; i++)
2933cbff97fSChristopher Meis {
2943cbff97fSChristopher Meis int byte2 = i2c_smbus_read_byte_data(file, 0);
2953cbff97fSChristopher Meis if (byte2 < 0)
2963cbff97fSChristopher Meis {
2973cbff97fSChristopher Meis return std::nullopt;
2983cbff97fSChristopher Meis }
2993cbff97fSChristopher Meis if (byte2 != byte1)
3003cbff97fSChristopher Meis {
3013cbff97fSChristopher Meis return true;
3023cbff97fSChristopher Meis }
3033cbff97fSChristopher Meis }
3043cbff97fSChristopher Meis return false;
3053cbff97fSChristopher Meis }
3063cbff97fSChristopher Meis
3073cbff97fSChristopher Meis // Mode_2:
3083cbff97fSChristopher Meis // --------
3093cbff97fSChristopher Meis // Please refer to document docs/address_size_detection_modes.md for
3103cbff97fSChristopher Meis // more details and explanations.
isDevice16BitMode2(int file,uint16_t address)3113cbff97fSChristopher Meis static std::optional<bool> isDevice16BitMode2(int file, uint16_t address)
3123cbff97fSChristopher Meis {
3133cbff97fSChristopher Meis uint8_t first = 0;
3143cbff97fSChristopher Meis uint8_t cur = 0;
3153cbff97fSChristopher Meis uint16_t v = 0;
3163cbff97fSChristopher Meis int ret = 0;
3173cbff97fSChristopher Meis // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3183cbff97fSChristopher Meis uint8_t* p = reinterpret_cast<uint8_t*>(&v);
3193cbff97fSChristopher Meis
3203cbff97fSChristopher Meis /*
3213cbff97fSChristopher Meis * Write 2 bytes byte0 = 0, byte1 = {0..7} and then subsequent read byte
3223cbff97fSChristopher Meis * It will read same first byte in case of 8 bit but
3233cbff97fSChristopher Meis * it will read next byte in case of 16 bit
3243cbff97fSChristopher Meis */
3253cbff97fSChristopher Meis for (int i = 0; i < 8; i++)
3263cbff97fSChristopher Meis {
3273cbff97fSChristopher Meis v = htobe16(i);
3283cbff97fSChristopher Meis
3293cbff97fSChristopher Meis ret = i2cSmbusWriteThenRead(file, address, p, 2, &cur, 1);
3303cbff97fSChristopher Meis if (ret < 0)
3313cbff97fSChristopher Meis {
3323cbff97fSChristopher Meis return std::nullopt;
3333cbff97fSChristopher Meis }
3343cbff97fSChristopher Meis
3353cbff97fSChristopher Meis if (i == 0)
3363cbff97fSChristopher Meis {
3373cbff97fSChristopher Meis first = cur;
3383cbff97fSChristopher Meis }
3393cbff97fSChristopher Meis
3403cbff97fSChristopher Meis if (first != cur)
3413cbff97fSChristopher Meis {
3423cbff97fSChristopher Meis return true;
3433cbff97fSChristopher Meis }
3443cbff97fSChristopher Meis }
3453cbff97fSChristopher Meis return false;
3463cbff97fSChristopher Meis }
3473cbff97fSChristopher Meis
isDevice16Bit(int file,uint16_t address)3483cbff97fSChristopher Meis static std::optional<bool> isDevice16Bit(int file, uint16_t address)
3493cbff97fSChristopher Meis {
3503cbff97fSChristopher Meis std::string mode(fruDevice16BitDetectMode);
3513cbff97fSChristopher Meis
3523cbff97fSChristopher Meis if (mode == "MODE_2")
3533cbff97fSChristopher Meis {
3543cbff97fSChristopher Meis return isDevice16BitMode2(file, address);
3553cbff97fSChristopher Meis }
3563cbff97fSChristopher Meis
3573cbff97fSChristopher Meis return isDevice16BitMode1(file);
3583cbff97fSChristopher Meis }
3593cbff97fSChristopher Meis
3603cbff97fSChristopher Meis // TODO: This code is very similar to the non-eeprom version and can be merged
3613cbff97fSChristopher Meis // with some tweaks.
processEeprom(int bus,int address)3623cbff97fSChristopher Meis static std::vector<uint8_t> processEeprom(int bus, int address)
3633cbff97fSChristopher Meis {
3643cbff97fSChristopher Meis auto path = getEepromPath(bus, address);
3653cbff97fSChristopher Meis
3663cbff97fSChristopher Meis int file = open(path.c_str(), O_RDONLY);
3673cbff97fSChristopher Meis if (file < 0)
3683cbff97fSChristopher Meis {
3693cbff97fSChristopher Meis std::cerr << "Unable to open eeprom file: " << path << "\n";
3703cbff97fSChristopher Meis return {};
3713cbff97fSChristopher Meis }
3723cbff97fSChristopher Meis
3733cbff97fSChristopher Meis std::string errorMessage = "eeprom at " + std::to_string(bus) +
3743cbff97fSChristopher Meis " address " + std::to_string(address);
3753cbff97fSChristopher Meis auto readFunc = [file](off_t offset, size_t length, uint8_t* outbuf) {
3763cbff97fSChristopher Meis return readFromEeprom(file, offset, length, outbuf);
3773cbff97fSChristopher Meis };
3783cbff97fSChristopher Meis FRUReader reader(std::move(readFunc));
3793cbff97fSChristopher Meis std::pair<std::vector<uint8_t>, bool> pair =
3803cbff97fSChristopher Meis readFRUContents(reader, errorMessage);
3813cbff97fSChristopher Meis
3823cbff97fSChristopher Meis close(file);
3833cbff97fSChristopher Meis return pair.first;
3843cbff97fSChristopher Meis }
3853cbff97fSChristopher Meis
findI2CEeproms(int i2cBus,const std::shared_ptr<DeviceMap> & devices)3863cbff97fSChristopher Meis std::set<size_t> findI2CEeproms(int i2cBus,
3873cbff97fSChristopher Meis const std::shared_ptr<DeviceMap>& devices)
3883cbff97fSChristopher Meis {
3893cbff97fSChristopher Meis std::set<size_t> foundList;
3903cbff97fSChristopher Meis
3913cbff97fSChristopher Meis std::string path = "/sys/bus/i2c/devices/i2c-" + std::to_string(i2cBus);
3923cbff97fSChristopher Meis
3933cbff97fSChristopher Meis // For each file listed under the i2c device
3943cbff97fSChristopher Meis // NOTE: This should be faster than just checking for each possible address
3953cbff97fSChristopher Meis // path.
3963cbff97fSChristopher Meis auto ec = std::error_code();
3973cbff97fSChristopher Meis for (const auto& p : fs::directory_iterator(path, ec))
3983cbff97fSChristopher Meis {
3993cbff97fSChristopher Meis if (ec)
4003cbff97fSChristopher Meis {
4013cbff97fSChristopher Meis std::cerr << "directory_iterator err " << ec.message() << "\n";
4023cbff97fSChristopher Meis break;
4033cbff97fSChristopher Meis }
4043cbff97fSChristopher Meis const std::string node = p.path().string();
4053cbff97fSChristopher Meis std::smatch m;
4063cbff97fSChristopher Meis bool found =
4073cbff97fSChristopher Meis std::regex_match(node, m, std::regex(".+\\d+-([0-9abcdef]+$)"));
4083cbff97fSChristopher Meis
4093cbff97fSChristopher Meis if (!found)
4103cbff97fSChristopher Meis {
4113cbff97fSChristopher Meis continue;
4123cbff97fSChristopher Meis }
4133cbff97fSChristopher Meis if (m.size() != 2)
4143cbff97fSChristopher Meis {
4153cbff97fSChristopher Meis std::cerr << "regex didn't capture\n";
4163cbff97fSChristopher Meis continue;
4173cbff97fSChristopher Meis }
4183cbff97fSChristopher Meis
4193cbff97fSChristopher Meis std::ssub_match subMatch = m[1];
4203cbff97fSChristopher Meis std::string addressString = subMatch.str();
4213cbff97fSChristopher Meis std::string_view addressStringView(addressString);
4223cbff97fSChristopher Meis
4233cbff97fSChristopher Meis size_t address = 0;
4243cbff97fSChristopher Meis std::from_chars(addressStringView.begin(), addressStringView.end(),
4253cbff97fSChristopher Meis address, 16);
4263cbff97fSChristopher Meis
4273cbff97fSChristopher Meis const std::string eeprom = node + "/eeprom";
4283cbff97fSChristopher Meis
4293cbff97fSChristopher Meis try
4303cbff97fSChristopher Meis {
4313cbff97fSChristopher Meis if (!fs::exists(eeprom))
4323cbff97fSChristopher Meis {
4333cbff97fSChristopher Meis continue;
4343cbff97fSChristopher Meis }
4353cbff97fSChristopher Meis }
4363cbff97fSChristopher Meis catch (...)
4373cbff97fSChristopher Meis {
4383cbff97fSChristopher Meis continue;
4393cbff97fSChristopher Meis }
4403cbff97fSChristopher Meis
4413cbff97fSChristopher Meis // There is an eeprom file at this address, it may have invalid
4423cbff97fSChristopher Meis // contents, but we found it.
4433cbff97fSChristopher Meis foundList.insert(address);
4443cbff97fSChristopher Meis
4453cbff97fSChristopher Meis std::vector<uint8_t> device = processEeprom(i2cBus, address);
4463cbff97fSChristopher Meis if (!device.empty())
4473cbff97fSChristopher Meis {
4483cbff97fSChristopher Meis devices->emplace(address, device);
4493cbff97fSChristopher Meis }
4503cbff97fSChristopher Meis }
4513cbff97fSChristopher Meis
4523cbff97fSChristopher Meis return foundList;
4533cbff97fSChristopher Meis }
4543cbff97fSChristopher Meis
getBusFRUs(int file,int first,int last,int bus,std::shared_ptr<DeviceMap> devices,const bool & powerIsOn,sdbusplus::asio::object_server & objServer)4553cbff97fSChristopher Meis int getBusFRUs(int file, int first, int last, int bus,
4563cbff97fSChristopher Meis std::shared_ptr<DeviceMap> devices, const bool& powerIsOn,
4573cbff97fSChristopher Meis sdbusplus::asio::object_server& objServer)
4583cbff97fSChristopher Meis {
4593cbff97fSChristopher Meis std::future<int> future = std::async(std::launch::async, [&]() {
4603cbff97fSChristopher Meis // NOTE: When reading the devices raw on the bus, it can interfere with
4613cbff97fSChristopher Meis // the driver's ability to operate, therefore read eeproms first before
4623cbff97fSChristopher Meis // scanning for devices without drivers. Several experiments were run
4633cbff97fSChristopher Meis // and it was determined that if there were any devices on the bus
4643cbff97fSChristopher Meis // before the eeprom was hit and read, the eeprom driver wouldn't open
4653cbff97fSChristopher Meis // while the bus device was open. An experiment was not performed to see
4663cbff97fSChristopher Meis // if this issue was resolved if the i2c bus device was closed, but
4673cbff97fSChristopher Meis // hexdumps of the eeprom later were successful.
4683cbff97fSChristopher Meis
4693cbff97fSChristopher Meis // Scan for i2c eeproms loaded on this bus.
4703cbff97fSChristopher Meis std::set<size_t> skipList = findI2CEeproms(bus, devices);
4713cbff97fSChristopher Meis std::set<size_t>& failedItems = failedAddresses[bus];
4723cbff97fSChristopher Meis std::set<size_t>& foundItems = fruAddresses[bus];
4733cbff97fSChristopher Meis foundItems.clear();
4743cbff97fSChristopher Meis
4753cbff97fSChristopher Meis auto busFind = busBlocklist.find(bus);
4763cbff97fSChristopher Meis if (busFind != busBlocklist.end())
4773cbff97fSChristopher Meis {
4783cbff97fSChristopher Meis if (busFind->second != std::nullopt)
4793cbff97fSChristopher Meis {
4803cbff97fSChristopher Meis for (const auto& address : *(busFind->second))
4813cbff97fSChristopher Meis {
4823cbff97fSChristopher Meis skipList.insert(address);
4833cbff97fSChristopher Meis }
4843cbff97fSChristopher Meis }
4853cbff97fSChristopher Meis }
4863cbff97fSChristopher Meis
4873cbff97fSChristopher Meis std::set<size_t>* rootFailures = nullptr;
4883cbff97fSChristopher Meis int rootBus = getRootBus(bus);
4893cbff97fSChristopher Meis
4903cbff97fSChristopher Meis if (rootBus >= 0)
4913cbff97fSChristopher Meis {
4923cbff97fSChristopher Meis auto rootBusFind = busBlocklist.find(rootBus);
4933cbff97fSChristopher Meis if (rootBusFind != busBlocklist.end())
4943cbff97fSChristopher Meis {
4953cbff97fSChristopher Meis if (rootBusFind->second != std::nullopt)
4963cbff97fSChristopher Meis {
4973cbff97fSChristopher Meis for (const auto& rootAddress : *(rootBusFind->second))
4983cbff97fSChristopher Meis {
4993cbff97fSChristopher Meis skipList.insert(rootAddress);
5003cbff97fSChristopher Meis }
5013cbff97fSChristopher Meis }
5023cbff97fSChristopher Meis }
5033cbff97fSChristopher Meis rootFailures = &(failedAddresses[rootBus]);
5043cbff97fSChristopher Meis foundItems = fruAddresses[rootBus];
5053cbff97fSChristopher Meis }
5063cbff97fSChristopher Meis
5073cbff97fSChristopher Meis constexpr int startSkipTargetAddr = 0;
5083cbff97fSChristopher Meis constexpr int endSkipTargetAddr = 12;
5093cbff97fSChristopher Meis
5103cbff97fSChristopher Meis for (int ii = first; ii <= last; ii++)
5113cbff97fSChristopher Meis {
512*6459993cSPatrick Williams if (foundItems.contains(ii))
5133cbff97fSChristopher Meis {
5143cbff97fSChristopher Meis continue;
5153cbff97fSChristopher Meis }
516*6459993cSPatrick Williams if (skipList.contains(ii))
5173cbff97fSChristopher Meis {
5183cbff97fSChristopher Meis continue;
5193cbff97fSChristopher Meis }
5203cbff97fSChristopher Meis // skipping since no device is present in this range
5213cbff97fSChristopher Meis if (ii >= startSkipTargetAddr && ii <= endSkipTargetAddr)
5223cbff97fSChristopher Meis {
5233cbff97fSChristopher Meis continue;
5243cbff97fSChristopher Meis }
5253cbff97fSChristopher Meis // Set target address
5263cbff97fSChristopher Meis if (ioctl(file, I2C_SLAVE, ii) < 0)
5273cbff97fSChristopher Meis {
5283cbff97fSChristopher Meis std::cerr << "device at bus " << bus << " address " << ii
5293cbff97fSChristopher Meis << " busy\n";
5303cbff97fSChristopher Meis continue;
5313cbff97fSChristopher Meis }
5323cbff97fSChristopher Meis // probe
5333cbff97fSChristopher Meis if (i2c_smbus_read_byte(file) < 0)
5343cbff97fSChristopher Meis {
5353cbff97fSChristopher Meis continue;
5363cbff97fSChristopher Meis }
5373cbff97fSChristopher Meis
5383cbff97fSChristopher Meis lg2::debug("something at bus {BUS}, addr {ADDR}", "BUS", bus,
5393cbff97fSChristopher Meis "ADDR", ii);
5403cbff97fSChristopher Meis
5413cbff97fSChristopher Meis makeProbeInterface(bus, ii, objServer);
5423cbff97fSChristopher Meis
543*6459993cSPatrick Williams if (failedItems.contains(ii))
5443cbff97fSChristopher Meis {
5453cbff97fSChristopher Meis // if we failed to read it once, unlikely we can read it later
5463cbff97fSChristopher Meis continue;
5473cbff97fSChristopher Meis }
5483cbff97fSChristopher Meis
5493cbff97fSChristopher Meis if (rootFailures != nullptr)
5503cbff97fSChristopher Meis {
551*6459993cSPatrick Williams if (rootFailures->contains(ii))
5523cbff97fSChristopher Meis {
5533cbff97fSChristopher Meis continue;
5543cbff97fSChristopher Meis }
5553cbff97fSChristopher Meis }
5563cbff97fSChristopher Meis
5573cbff97fSChristopher Meis /* Check for Device type if it is 8 bit or 16 bit */
5583cbff97fSChristopher Meis std::optional<bool> is16Bit = isDevice16Bit(file, ii);
5593cbff97fSChristopher Meis if (!is16Bit.has_value())
5603cbff97fSChristopher Meis {
5613cbff97fSChristopher Meis std::cerr << "failed to read bus " << bus << " address " << ii
5623cbff97fSChristopher Meis << "\n";
5633cbff97fSChristopher Meis if (powerIsOn)
5643cbff97fSChristopher Meis {
5653cbff97fSChristopher Meis failedItems.insert(ii);
5663cbff97fSChristopher Meis }
5673cbff97fSChristopher Meis continue;
5683cbff97fSChristopher Meis }
5693cbff97fSChristopher Meis bool is16BitBool{*is16Bit};
5703cbff97fSChristopher Meis
5713cbff97fSChristopher Meis auto readFunc = [is16BitBool, file,
5723cbff97fSChristopher Meis ii](off_t offset, size_t length, uint8_t* outbuf) {
5733cbff97fSChristopher Meis return readData(is16BitBool, false, file, ii, offset, length,
5743cbff97fSChristopher Meis outbuf);
5753cbff97fSChristopher Meis };
5763cbff97fSChristopher Meis FRUReader reader(std::move(readFunc));
5773cbff97fSChristopher Meis std::string errorMessage =
5783cbff97fSChristopher Meis "bus " + std::to_string(bus) + " address " + std::to_string(ii);
5793cbff97fSChristopher Meis std::pair<std::vector<uint8_t>, bool> pair =
5803cbff97fSChristopher Meis readFRUContents(reader, errorMessage);
5813cbff97fSChristopher Meis const bool foundHeader = pair.second;
5823cbff97fSChristopher Meis
5833cbff97fSChristopher Meis if (!foundHeader && !is16BitBool)
5843cbff97fSChristopher Meis {
5853cbff97fSChristopher Meis // certain FRU eeproms require bytewise reading.
5863cbff97fSChristopher Meis // otherwise garbage is read. e.g. SuperMicro PWS 920P-SQ
5873cbff97fSChristopher Meis
5883cbff97fSChristopher Meis auto readFunc =
5893cbff97fSChristopher Meis [is16BitBool, file,
5903cbff97fSChristopher Meis ii](off_t offset, size_t length, uint8_t* outbuf) {
5913cbff97fSChristopher Meis return readData(is16BitBool, true, file, ii, offset,
5923cbff97fSChristopher Meis length, outbuf);
5933cbff97fSChristopher Meis };
5943cbff97fSChristopher Meis FRUReader readerBytewise(std::move(readFunc));
5953cbff97fSChristopher Meis pair = readFRUContents(readerBytewise, errorMessage);
5963cbff97fSChristopher Meis }
5973cbff97fSChristopher Meis
5983cbff97fSChristopher Meis if (pair.first.empty())
5993cbff97fSChristopher Meis {
6003cbff97fSChristopher Meis continue;
6013cbff97fSChristopher Meis }
6023cbff97fSChristopher Meis
6033cbff97fSChristopher Meis devices->emplace(ii, pair.first);
6043cbff97fSChristopher Meis fruAddresses[bus].insert(ii);
6053cbff97fSChristopher Meis }
6063cbff97fSChristopher Meis return 1;
6073cbff97fSChristopher Meis });
6083cbff97fSChristopher Meis std::future_status status =
6093cbff97fSChristopher Meis future.wait_for(std::chrono::seconds(busTimeoutSeconds));
6103cbff97fSChristopher Meis if (status == std::future_status::timeout)
6113cbff97fSChristopher Meis {
6123cbff97fSChristopher Meis std::cerr << "Error reading bus " << bus << "\n";
6133cbff97fSChristopher Meis if (powerIsOn)
6143cbff97fSChristopher Meis {
6153cbff97fSChristopher Meis busBlocklist[bus] = std::nullopt;
6163cbff97fSChristopher Meis }
6173cbff97fSChristopher Meis close(file);
6183cbff97fSChristopher Meis return -1;
6193cbff97fSChristopher Meis }
6203cbff97fSChristopher Meis
6213cbff97fSChristopher Meis close(file);
6223cbff97fSChristopher Meis return future.get();
6233cbff97fSChristopher Meis }
6243cbff97fSChristopher Meis
loadBlocklist(const char * path)6253cbff97fSChristopher Meis void loadBlocklist(const char* path)
6263cbff97fSChristopher Meis {
6273cbff97fSChristopher Meis std::ifstream blocklistStream(path);
6283cbff97fSChristopher Meis if (!blocklistStream.good())
6293cbff97fSChristopher Meis {
6303cbff97fSChristopher Meis // File is optional.
6313cbff97fSChristopher Meis std::cerr << "Cannot open blocklist file.\n\n";
6323cbff97fSChristopher Meis return;
6333cbff97fSChristopher Meis }
6343cbff97fSChristopher Meis
6353cbff97fSChristopher Meis nlohmann::json data =
6363cbff97fSChristopher Meis nlohmann::json::parse(blocklistStream, nullptr, false);
6373cbff97fSChristopher Meis if (data.is_discarded())
6383cbff97fSChristopher Meis {
6393cbff97fSChristopher Meis std::cerr << "Illegal blocklist file detected, cannot validate JSON, "
6403cbff97fSChristopher Meis "exiting\n";
6413cbff97fSChristopher Meis std::exit(EXIT_FAILURE);
6423cbff97fSChristopher Meis }
6433cbff97fSChristopher Meis
6443cbff97fSChristopher Meis // It's expected to have at least one field, "buses" that is an array of the
6453cbff97fSChristopher Meis // buses by integer. Allow for future options to exclude further aspects,
6463cbff97fSChristopher Meis // such as specific addresses or ranges.
6473cbff97fSChristopher Meis if (data.type() != nlohmann::json::value_t::object)
6483cbff97fSChristopher Meis {
6493cbff97fSChristopher Meis std::cerr << "Illegal blocklist, expected to read dictionary\n";
6503cbff97fSChristopher Meis std::exit(EXIT_FAILURE);
6513cbff97fSChristopher Meis }
6523cbff97fSChristopher Meis
6533cbff97fSChristopher Meis // If buses field is missing, that's fine.
6543cbff97fSChristopher Meis if (data.count("buses") == 1)
6553cbff97fSChristopher Meis {
6563cbff97fSChristopher Meis // Parse the buses array after a little validation.
6573cbff97fSChristopher Meis auto buses = data.at("buses");
6583cbff97fSChristopher Meis if (buses.type() != nlohmann::json::value_t::array)
6593cbff97fSChristopher Meis {
6603cbff97fSChristopher Meis // Buses field present but invalid, therefore this is an error.
6613cbff97fSChristopher Meis std::cerr << "Invalid contents for blocklist buses field\n";
6623cbff97fSChristopher Meis std::exit(EXIT_FAILURE);
6633cbff97fSChristopher Meis }
6643cbff97fSChristopher Meis
6653cbff97fSChristopher Meis // Catch exception here for type mis-match.
6663cbff97fSChristopher Meis try
6673cbff97fSChristopher Meis {
6683cbff97fSChristopher Meis for (const auto& busIterator : buses)
6693cbff97fSChristopher Meis {
6703cbff97fSChristopher Meis // If bus and addresses field are missing, that's fine.
6713cbff97fSChristopher Meis if (busIterator.contains("bus") &&
6723cbff97fSChristopher Meis busIterator.contains("addresses"))
6733cbff97fSChristopher Meis {
6743cbff97fSChristopher Meis auto busData = busIterator.at("bus");
6753cbff97fSChristopher Meis auto bus = busData.get<size_t>();
6763cbff97fSChristopher Meis
6773cbff97fSChristopher Meis auto addressData = busIterator.at("addresses");
6783cbff97fSChristopher Meis auto addresses =
6793cbff97fSChristopher Meis addressData.get<std::set<std::string_view>>();
6803cbff97fSChristopher Meis
6813cbff97fSChristopher Meis auto& block = busBlocklist[bus].emplace();
6823cbff97fSChristopher Meis for (const auto& address : addresses)
6833cbff97fSChristopher Meis {
6843cbff97fSChristopher Meis size_t addressInt = 0;
6853cbff97fSChristopher Meis std::from_chars(address.begin() + 2, address.end(),
6863cbff97fSChristopher Meis addressInt, 16);
6873cbff97fSChristopher Meis block.insert(addressInt);
6883cbff97fSChristopher Meis }
6893cbff97fSChristopher Meis }
6903cbff97fSChristopher Meis else
6913cbff97fSChristopher Meis {
6923cbff97fSChristopher Meis busBlocklist[busIterator.get<size_t>()] = std::nullopt;
6933cbff97fSChristopher Meis }
6943cbff97fSChristopher Meis }
6953cbff97fSChristopher Meis }
6963cbff97fSChristopher Meis catch (const nlohmann::detail::type_error& e)
6973cbff97fSChristopher Meis {
6983cbff97fSChristopher Meis // Type mis-match is a critical error.
6993cbff97fSChristopher Meis std::cerr << "Invalid bus type: " << e.what() << "\n";
7003cbff97fSChristopher Meis std::exit(EXIT_FAILURE);
7013cbff97fSChristopher Meis }
7023cbff97fSChristopher Meis }
7033cbff97fSChristopher Meis }
7043cbff97fSChristopher Meis
findI2CDevices(const std::vector<fs::path> & i2cBuses,BusMap & busmap,const bool & powerIsOn,sdbusplus::asio::object_server & objServer)7053cbff97fSChristopher Meis static void findI2CDevices(const std::vector<fs::path>& i2cBuses,
7063cbff97fSChristopher Meis BusMap& busmap, const bool& powerIsOn,
7073cbff97fSChristopher Meis sdbusplus::asio::object_server& objServer)
7083cbff97fSChristopher Meis {
7093cbff97fSChristopher Meis for (const auto& i2cBus : i2cBuses)
7103cbff97fSChristopher Meis {
7113cbff97fSChristopher Meis int bus = busStrToInt(i2cBus.string());
7123cbff97fSChristopher Meis
7133cbff97fSChristopher Meis if (bus < 0)
7143cbff97fSChristopher Meis {
7153cbff97fSChristopher Meis std::cerr << "Cannot translate " << i2cBus << " to int\n";
7163cbff97fSChristopher Meis continue;
7173cbff97fSChristopher Meis }
7183cbff97fSChristopher Meis auto busFind = busBlocklist.find(bus);
7193cbff97fSChristopher Meis if (busFind != busBlocklist.end())
7203cbff97fSChristopher Meis {
7213cbff97fSChristopher Meis if (busFind->second == std::nullopt)
7223cbff97fSChristopher Meis {
7233cbff97fSChristopher Meis continue; // Skip blocked busses.
7243cbff97fSChristopher Meis }
7253cbff97fSChristopher Meis }
7263cbff97fSChristopher Meis int rootBus = getRootBus(bus);
7273cbff97fSChristopher Meis auto rootBusFind = busBlocklist.find(rootBus);
7283cbff97fSChristopher Meis if (rootBusFind != busBlocklist.end())
7293cbff97fSChristopher Meis {
7303cbff97fSChristopher Meis if (rootBusFind->second == std::nullopt)
7313cbff97fSChristopher Meis {
7323cbff97fSChristopher Meis continue;
7333cbff97fSChristopher Meis }
7343cbff97fSChristopher Meis }
7353cbff97fSChristopher Meis
7363cbff97fSChristopher Meis auto file = open(i2cBus.c_str(), O_RDWR);
7373cbff97fSChristopher Meis if (file < 0)
7383cbff97fSChristopher Meis {
7393cbff97fSChristopher Meis std::cerr << "unable to open i2c device " << i2cBus.string()
7403cbff97fSChristopher Meis << "\n";
7413cbff97fSChristopher Meis continue;
7423cbff97fSChristopher Meis }
7433cbff97fSChristopher Meis unsigned long funcs = 0;
7443cbff97fSChristopher Meis
7453cbff97fSChristopher Meis if (ioctl(file, I2C_FUNCS, &funcs) < 0)
7463cbff97fSChristopher Meis {
7473cbff97fSChristopher Meis std::cerr
7483cbff97fSChristopher Meis << "Error: Could not get the adapter functionality matrix bus "
7493cbff97fSChristopher Meis << bus << "\n";
7503cbff97fSChristopher Meis close(file);
7513cbff97fSChristopher Meis continue;
7523cbff97fSChristopher Meis }
7533cbff97fSChristopher Meis if (((funcs & I2C_FUNC_SMBUS_READ_BYTE) == 0U) ||
7543cbff97fSChristopher Meis ((I2C_FUNC_SMBUS_READ_I2C_BLOCK) == 0))
7553cbff97fSChristopher Meis {
7563cbff97fSChristopher Meis std::cerr << "Error: Can't use SMBus Receive Byte command bus "
7573cbff97fSChristopher Meis << bus << "\n";
7583cbff97fSChristopher Meis close(file);
7593cbff97fSChristopher Meis continue;
7603cbff97fSChristopher Meis }
7613cbff97fSChristopher Meis auto& device = busmap[bus];
7623cbff97fSChristopher Meis device = std::make_shared<DeviceMap>();
7633cbff97fSChristopher Meis
7643cbff97fSChristopher Meis // i2cdetect by default uses the range 0x03 to 0x77, as
7653cbff97fSChristopher Meis // this is what we have tested with, use this range. Could be
7663cbff97fSChristopher Meis // changed in future.
7673cbff97fSChristopher Meis lg2::debug("Scanning bus {BUS}", "BUS", bus);
7683cbff97fSChristopher Meis
7693cbff97fSChristopher Meis // fd is closed in this function in case the bus locks up
7703cbff97fSChristopher Meis getBusFRUs(file, 0x03, 0x77, bus, device, powerIsOn, objServer);
7713cbff97fSChristopher Meis
7723cbff97fSChristopher Meis lg2::debug("Done scanning bus {BUS}", "BUS", bus);
7733cbff97fSChristopher Meis }
7743cbff97fSChristopher Meis }
7753cbff97fSChristopher Meis
7763cbff97fSChristopher Meis // this class allows an async response after all i2c devices are discovered
7773cbff97fSChristopher Meis struct FindDevicesWithCallback :
7783cbff97fSChristopher Meis std::enable_shared_from_this<FindDevicesWithCallback>
7793cbff97fSChristopher Meis {
FindDevicesWithCallbackFindDevicesWithCallback7803cbff97fSChristopher Meis FindDevicesWithCallback(const std::vector<fs::path>& i2cBuses,
7813cbff97fSChristopher Meis BusMap& busmap, const bool& powerIsOn,
7823cbff97fSChristopher Meis sdbusplus::asio::object_server& objServer,
7833cbff97fSChristopher Meis std::function<void()>&& callback) :
7843cbff97fSChristopher Meis _i2cBuses(i2cBuses), _busMap(busmap), _powerIsOn(powerIsOn),
7853cbff97fSChristopher Meis _objServer(objServer), _callback(std::move(callback))
7863cbff97fSChristopher Meis {}
~FindDevicesWithCallbackFindDevicesWithCallback7873cbff97fSChristopher Meis ~FindDevicesWithCallback()
7883cbff97fSChristopher Meis {
7893cbff97fSChristopher Meis _callback();
7903cbff97fSChristopher Meis }
runFindDevicesWithCallback7913cbff97fSChristopher Meis void run()
7923cbff97fSChristopher Meis {
7933cbff97fSChristopher Meis findI2CDevices(_i2cBuses, _busMap, _powerIsOn, _objServer);
7943cbff97fSChristopher Meis }
7953cbff97fSChristopher Meis
7963cbff97fSChristopher Meis const std::vector<fs::path>& _i2cBuses;
7973cbff97fSChristopher Meis BusMap& _busMap;
7983cbff97fSChristopher Meis const bool& _powerIsOn;
7993cbff97fSChristopher Meis sdbusplus::asio::object_server& _objServer;
8003cbff97fSChristopher Meis std::function<void()> _callback;
8013cbff97fSChristopher Meis };
8023cbff97fSChristopher Meis
addFruObjectToDbus(std::vector<uint8_t> & device,boost::container::flat_map<std::pair<size_t,size_t>,std::shared_ptr<sdbusplus::asio::dbus_interface>> & dbusInterfaceMap,uint32_t bus,uint32_t address,size_t & unknownBusObjectCount,const bool & powerIsOn,sdbusplus::asio::object_server & objServer,std::shared_ptr<sdbusplus::asio::connection> & systemBus)8033cbff97fSChristopher Meis void addFruObjectToDbus(
8043cbff97fSChristopher Meis std::vector<uint8_t>& device,
8053cbff97fSChristopher Meis boost::container::flat_map<
8063cbff97fSChristopher Meis std::pair<size_t, size_t>,
8073cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
8083cbff97fSChristopher Meis uint32_t bus, uint32_t address, size_t& unknownBusObjectCount,
8093cbff97fSChristopher Meis const bool& powerIsOn, sdbusplus::asio::object_server& objServer,
8103cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::connection>& systemBus)
8113cbff97fSChristopher Meis {
8123cbff97fSChristopher Meis boost::container::flat_map<std::string, std::string> formattedFRU;
8133cbff97fSChristopher Meis
8143cbff97fSChristopher Meis std::optional<std::string> optionalProductName = getProductName(
8153cbff97fSChristopher Meis device, formattedFRU, bus, address, unknownBusObjectCount);
8163cbff97fSChristopher Meis if (!optionalProductName)
8173cbff97fSChristopher Meis {
8183cbff97fSChristopher Meis std::cerr << "getProductName failed. product name is empty.\n";
8193cbff97fSChristopher Meis return;
8203cbff97fSChristopher Meis }
8213cbff97fSChristopher Meis
8223cbff97fSChristopher Meis std::string productName =
8233cbff97fSChristopher Meis "/xyz/openbmc_project/FruDevice/" + optionalProductName.value();
8243cbff97fSChristopher Meis
8253cbff97fSChristopher Meis std::optional<int> index = findIndexForFRU(dbusInterfaceMap, productName);
8263cbff97fSChristopher Meis if (index.has_value())
8273cbff97fSChristopher Meis {
8283cbff97fSChristopher Meis productName += "_";
8293cbff97fSChristopher Meis productName += std::to_string(++(*index));
8303cbff97fSChristopher Meis }
8313cbff97fSChristopher Meis
8323cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
8333cbff97fSChristopher Meis objServer.add_interface(productName, "xyz.openbmc_project.FruDevice");
8343cbff97fSChristopher Meis dbusInterfaceMap[std::pair<size_t, size_t>(bus, address)] = iface;
8353cbff97fSChristopher Meis
8363cbff97fSChristopher Meis for (auto& property : formattedFRU)
8373cbff97fSChristopher Meis {
8383cbff97fSChristopher Meis std::regex_replace(property.second.begin(), property.second.begin(),
8393cbff97fSChristopher Meis property.second.end(), nonAsciiRegex, "_");
8403cbff97fSChristopher Meis if (property.second.empty() && property.first != "PRODUCT_ASSET_TAG")
8413cbff97fSChristopher Meis {
8423cbff97fSChristopher Meis continue;
8433cbff97fSChristopher Meis }
8443cbff97fSChristopher Meis std::string key =
8453cbff97fSChristopher Meis std::regex_replace(property.first, nonAsciiRegex, "_");
8463cbff97fSChristopher Meis
8473cbff97fSChristopher Meis if (property.first == "PRODUCT_ASSET_TAG")
8483cbff97fSChristopher Meis {
8493cbff97fSChristopher Meis std::string propertyName = property.first;
8503cbff97fSChristopher Meis iface->register_property(
8513cbff97fSChristopher Meis key, property.second + '\0',
8523cbff97fSChristopher Meis [bus, address, propertyName, &dbusInterfaceMap,
8533cbff97fSChristopher Meis &unknownBusObjectCount, &powerIsOn, &objServer,
8543cbff97fSChristopher Meis &systemBus](const std::string& req, std::string& resp) {
8553cbff97fSChristopher Meis if (strcmp(req.c_str(), resp.c_str()) != 0)
8563cbff97fSChristopher Meis {
8573cbff97fSChristopher Meis // call the method which will update
8583cbff97fSChristopher Meis if (updateFRUProperty(req, bus, address, propertyName,
8593cbff97fSChristopher Meis dbusInterfaceMap,
8603cbff97fSChristopher Meis unknownBusObjectCount, powerIsOn,
8613cbff97fSChristopher Meis objServer, systemBus))
8623cbff97fSChristopher Meis {
8633cbff97fSChristopher Meis resp = req;
8643cbff97fSChristopher Meis }
8653cbff97fSChristopher Meis else
8663cbff97fSChristopher Meis {
8673cbff97fSChristopher Meis throw std::invalid_argument(
8683cbff97fSChristopher Meis "FRU property update failed.");
8693cbff97fSChristopher Meis }
8703cbff97fSChristopher Meis }
8713cbff97fSChristopher Meis return 1;
8723cbff97fSChristopher Meis });
8733cbff97fSChristopher Meis }
8743cbff97fSChristopher Meis else if (!iface->register_property(key, property.second + '\0'))
8753cbff97fSChristopher Meis {
8763cbff97fSChristopher Meis std::cerr << "illegal key: " << key << "\n";
8773cbff97fSChristopher Meis }
8783cbff97fSChristopher Meis lg2::debug("parsed FRU property: {FIRST}: {SECOND}", "FIRST",
8793cbff97fSChristopher Meis property.first, "SECOND", property.second);
8803cbff97fSChristopher Meis }
8813cbff97fSChristopher Meis
8823cbff97fSChristopher Meis // baseboard will be 0, 0
8833cbff97fSChristopher Meis iface->register_property("BUS", bus);
8843cbff97fSChristopher Meis iface->register_property("ADDRESS", address);
8853cbff97fSChristopher Meis
8863cbff97fSChristopher Meis iface->initialize();
8873cbff97fSChristopher Meis }
8883cbff97fSChristopher Meis
readBaseboardFRU(std::vector<uint8_t> & baseboardFRU)8893cbff97fSChristopher Meis static bool readBaseboardFRU(std::vector<uint8_t>& baseboardFRU)
8903cbff97fSChristopher Meis {
8913cbff97fSChristopher Meis // try to read baseboard fru from file
8923cbff97fSChristopher Meis std::ifstream baseboardFRUFile(baseboardFruLocation, std::ios::binary);
8933cbff97fSChristopher Meis if (baseboardFRUFile.good())
8943cbff97fSChristopher Meis {
8953cbff97fSChristopher Meis baseboardFRUFile.seekg(0, std::ios_base::end);
8963cbff97fSChristopher Meis size_t fileSize = static_cast<size_t>(baseboardFRUFile.tellg());
8973cbff97fSChristopher Meis baseboardFRU.resize(fileSize);
8983cbff97fSChristopher Meis baseboardFRUFile.seekg(0, std::ios_base::beg);
8993cbff97fSChristopher Meis // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
9003cbff97fSChristopher Meis char* charOffset = reinterpret_cast<char*>(baseboardFRU.data());
9013cbff97fSChristopher Meis baseboardFRUFile.read(charOffset, fileSize);
9023cbff97fSChristopher Meis }
9033cbff97fSChristopher Meis else
9043cbff97fSChristopher Meis {
9053cbff97fSChristopher Meis return false;
9063cbff97fSChristopher Meis }
9073cbff97fSChristopher Meis return true;
9083cbff97fSChristopher Meis }
9093cbff97fSChristopher Meis
writeFRU(uint8_t bus,uint8_t address,const std::vector<uint8_t> & fru)9103cbff97fSChristopher Meis bool writeFRU(uint8_t bus, uint8_t address, const std::vector<uint8_t>& fru)
9113cbff97fSChristopher Meis {
9123cbff97fSChristopher Meis boost::container::flat_map<std::string, std::string> tmp;
9133cbff97fSChristopher Meis if (fru.size() > maxFruSize)
9143cbff97fSChristopher Meis {
9153cbff97fSChristopher Meis std::cerr << "Invalid fru.size() during writeFRU\n";
9163cbff97fSChristopher Meis return false;
9173cbff97fSChristopher Meis }
9183cbff97fSChristopher Meis // verify legal fru by running it through fru parsing logic
9193cbff97fSChristopher Meis if (formatIPMIFRU(fru, tmp) != resCodes::resOK)
9203cbff97fSChristopher Meis {
9213cbff97fSChristopher Meis std::cerr << "Invalid fru format during writeFRU\n";
9223cbff97fSChristopher Meis return false;
9233cbff97fSChristopher Meis }
9243cbff97fSChristopher Meis // baseboard fru
9253cbff97fSChristopher Meis if (bus == 0 && address == 0)
9263cbff97fSChristopher Meis {
9273cbff97fSChristopher Meis std::ofstream file(baseboardFruLocation, std::ios_base::binary);
9283cbff97fSChristopher Meis if (!file.good())
9293cbff97fSChristopher Meis {
9303cbff97fSChristopher Meis std::cerr << "Error opening file " << baseboardFruLocation << "\n";
9313cbff97fSChristopher Meis throw DBusInternalError();
9323cbff97fSChristopher Meis return false;
9333cbff97fSChristopher Meis }
9343cbff97fSChristopher Meis // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
9353cbff97fSChristopher Meis const char* charOffset = reinterpret_cast<const char*>(fru.data());
9363cbff97fSChristopher Meis file.write(charOffset, fru.size());
9373cbff97fSChristopher Meis return file.good();
9383cbff97fSChristopher Meis }
9393cbff97fSChristopher Meis
9403cbff97fSChristopher Meis if (hasEepromFile(bus, address))
9413cbff97fSChristopher Meis {
9423cbff97fSChristopher Meis auto path = getEepromPath(bus, address);
9433cbff97fSChristopher Meis off_t offset = 0;
9443cbff97fSChristopher Meis
9453cbff97fSChristopher Meis int eeprom = open(path.c_str(), O_RDWR | O_CLOEXEC);
9463cbff97fSChristopher Meis if (eeprom < 0)
9473cbff97fSChristopher Meis {
9483cbff97fSChristopher Meis std::cerr << "unable to open i2c device " << path << "\n";
9493cbff97fSChristopher Meis throw DBusInternalError();
9503cbff97fSChristopher Meis return false;
9513cbff97fSChristopher Meis }
9523cbff97fSChristopher Meis
9533cbff97fSChristopher Meis std::array<uint8_t, I2C_SMBUS_BLOCK_MAX> blockData{};
9543cbff97fSChristopher Meis std::string errorMessage = "eeprom at " + std::to_string(bus) +
9553cbff97fSChristopher Meis " address " + std::to_string(address);
9563cbff97fSChristopher Meis auto readFunc = [eeprom](off_t offset, size_t length, uint8_t* outbuf) {
9573cbff97fSChristopher Meis return readFromEeprom(eeprom, offset, length, outbuf);
9583cbff97fSChristopher Meis };
9593cbff97fSChristopher Meis FRUReader reader(std::move(readFunc));
9603cbff97fSChristopher Meis
9613cbff97fSChristopher Meis if (!findFRUHeader(reader, errorMessage, blockData, offset))
9623cbff97fSChristopher Meis {
9633cbff97fSChristopher Meis offset = 0;
9643cbff97fSChristopher Meis }
9653cbff97fSChristopher Meis
9663cbff97fSChristopher Meis if (lseek(eeprom, offset, SEEK_SET) < 0)
9673cbff97fSChristopher Meis {
9683cbff97fSChristopher Meis std::cerr << "Unable to seek to offset " << offset
9693cbff97fSChristopher Meis << " in device: " << path << "\n";
9703cbff97fSChristopher Meis close(eeprom);
9713cbff97fSChristopher Meis throw DBusInternalError();
9723cbff97fSChristopher Meis }
9733cbff97fSChristopher Meis
9743cbff97fSChristopher Meis ssize_t writtenBytes = write(eeprom, fru.data(), fru.size());
9753cbff97fSChristopher Meis if (writtenBytes < 0)
9763cbff97fSChristopher Meis {
9773cbff97fSChristopher Meis std::cerr << "unable to write to i2c device " << path << "\n";
9783cbff97fSChristopher Meis close(eeprom);
9793cbff97fSChristopher Meis throw DBusInternalError();
9803cbff97fSChristopher Meis return false;
9813cbff97fSChristopher Meis }
9823cbff97fSChristopher Meis
9833cbff97fSChristopher Meis close(eeprom);
9843cbff97fSChristopher Meis return true;
9853cbff97fSChristopher Meis }
9863cbff97fSChristopher Meis
9873cbff97fSChristopher Meis std::string i2cBus = "/dev/i2c-" + std::to_string(bus);
9883cbff97fSChristopher Meis
9893cbff97fSChristopher Meis int file = open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
9903cbff97fSChristopher Meis if (file < 0)
9913cbff97fSChristopher Meis {
9923cbff97fSChristopher Meis std::cerr << "unable to open i2c device " << i2cBus << "\n";
9933cbff97fSChristopher Meis throw DBusInternalError();
9943cbff97fSChristopher Meis return false;
9953cbff97fSChristopher Meis }
9963cbff97fSChristopher Meis if (ioctl(file, I2C_SLAVE_FORCE, address) < 0)
9973cbff97fSChristopher Meis {
9983cbff97fSChristopher Meis std::cerr << "unable to set device address\n";
9993cbff97fSChristopher Meis close(file);
10003cbff97fSChristopher Meis throw DBusInternalError();
10013cbff97fSChristopher Meis return false;
10023cbff97fSChristopher Meis }
10033cbff97fSChristopher Meis
10043cbff97fSChristopher Meis constexpr const size_t retryMax = 2;
10053cbff97fSChristopher Meis uint16_t index = 0;
10063cbff97fSChristopher Meis size_t retries = retryMax;
10073cbff97fSChristopher Meis while (index < fru.size())
10083cbff97fSChristopher Meis {
10093cbff97fSChristopher Meis if (((index != 0U) && ((index % (maxEepromPageIndex + 1)) == 0)) &&
10103cbff97fSChristopher Meis (retries == retryMax))
10113cbff97fSChristopher Meis {
10123cbff97fSChristopher Meis // The 4K EEPROM only uses the A2 and A1 device address bits
10133cbff97fSChristopher Meis // with the third bit being a memory page address bit.
10143cbff97fSChristopher Meis if (ioctl(file, I2C_SLAVE_FORCE, ++address) < 0)
10153cbff97fSChristopher Meis {
10163cbff97fSChristopher Meis std::cerr << "unable to set device address\n";
10173cbff97fSChristopher Meis close(file);
10183cbff97fSChristopher Meis throw DBusInternalError();
10193cbff97fSChristopher Meis return false;
10203cbff97fSChristopher Meis }
10213cbff97fSChristopher Meis }
10223cbff97fSChristopher Meis
10233cbff97fSChristopher Meis if (i2c_smbus_write_byte_data(file, static_cast<uint8_t>(index),
10243cbff97fSChristopher Meis fru[index]) < 0)
10253cbff97fSChristopher Meis {
10263cbff97fSChristopher Meis if ((retries--) == 0U)
10273cbff97fSChristopher Meis {
10283cbff97fSChristopher Meis std::cerr << "error writing fru: " << strerror(errno) << "\n";
10293cbff97fSChristopher Meis close(file);
10303cbff97fSChristopher Meis throw DBusInternalError();
10313cbff97fSChristopher Meis return false;
10323cbff97fSChristopher Meis }
10333cbff97fSChristopher Meis }
10343cbff97fSChristopher Meis else
10353cbff97fSChristopher Meis {
10363cbff97fSChristopher Meis retries = retryMax;
10373cbff97fSChristopher Meis index++;
10383cbff97fSChristopher Meis }
10393cbff97fSChristopher Meis // most eeproms require 5-10ms between writes
10403cbff97fSChristopher Meis std::this_thread::sleep_for(std::chrono::milliseconds(10));
10413cbff97fSChristopher Meis }
10423cbff97fSChristopher Meis close(file);
10433cbff97fSChristopher Meis return true;
10443cbff97fSChristopher Meis }
10453cbff97fSChristopher Meis
rescanOneBus(BusMap & busmap,uint16_t busNum,boost::container::flat_map<std::pair<size_t,size_t>,std::shared_ptr<sdbusplus::asio::dbus_interface>> & dbusInterfaceMap,bool dbusCall,size_t & unknownBusObjectCount,const bool & powerIsOn,sdbusplus::asio::object_server & objServer,std::shared_ptr<sdbusplus::asio::connection> & systemBus)10463cbff97fSChristopher Meis void rescanOneBus(
10473cbff97fSChristopher Meis BusMap& busmap, uint16_t busNum,
10483cbff97fSChristopher Meis boost::container::flat_map<
10493cbff97fSChristopher Meis std::pair<size_t, size_t>,
10503cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
10513cbff97fSChristopher Meis bool dbusCall, size_t& unknownBusObjectCount, const bool& powerIsOn,
10523cbff97fSChristopher Meis sdbusplus::asio::object_server& objServer,
10533cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::connection>& systemBus)
10543cbff97fSChristopher Meis {
10553cbff97fSChristopher Meis for (auto device = foundDevices.begin(); device != foundDevices.end();)
10563cbff97fSChristopher Meis {
10573cbff97fSChristopher Meis if (device->first.first == static_cast<size_t>(busNum))
10583cbff97fSChristopher Meis {
10593cbff97fSChristopher Meis objServer.remove_interface(device->second);
10603cbff97fSChristopher Meis device = foundDevices.erase(device);
10613cbff97fSChristopher Meis }
10623cbff97fSChristopher Meis else
10633cbff97fSChristopher Meis {
10643cbff97fSChristopher Meis device++;
10653cbff97fSChristopher Meis }
10663cbff97fSChristopher Meis }
10673cbff97fSChristopher Meis
10683cbff97fSChristopher Meis fs::path busPath = fs::path("/dev/i2c-" + std::to_string(busNum));
10693cbff97fSChristopher Meis if (!fs::exists(busPath))
10703cbff97fSChristopher Meis {
10713cbff97fSChristopher Meis if (dbusCall)
10723cbff97fSChristopher Meis {
10733cbff97fSChristopher Meis std::cerr << "Unable to access i2c bus " << static_cast<int>(busNum)
10743cbff97fSChristopher Meis << "\n";
10753cbff97fSChristopher Meis throw std::invalid_argument("Invalid Bus.");
10763cbff97fSChristopher Meis }
10773cbff97fSChristopher Meis return;
10783cbff97fSChristopher Meis }
10793cbff97fSChristopher Meis
10803cbff97fSChristopher Meis std::vector<fs::path> i2cBuses;
10813cbff97fSChristopher Meis i2cBuses.emplace_back(busPath);
10823cbff97fSChristopher Meis
10833cbff97fSChristopher Meis auto scan = std::make_shared<FindDevicesWithCallback>(
10843cbff97fSChristopher Meis i2cBuses, busmap, powerIsOn, objServer,
10853cbff97fSChristopher Meis [busNum, &busmap, &dbusInterfaceMap, &unknownBusObjectCount, &powerIsOn,
10863cbff97fSChristopher Meis &objServer, &systemBus]() {
10873cbff97fSChristopher Meis for (auto busIface = dbusInterfaceMap.begin();
10883cbff97fSChristopher Meis busIface != dbusInterfaceMap.end();)
10893cbff97fSChristopher Meis {
10903cbff97fSChristopher Meis if (busIface->first.first == static_cast<size_t>(busNum))
10913cbff97fSChristopher Meis {
10923cbff97fSChristopher Meis objServer.remove_interface(busIface->second);
10933cbff97fSChristopher Meis busIface = dbusInterfaceMap.erase(busIface);
10943cbff97fSChristopher Meis }
10953cbff97fSChristopher Meis else
10963cbff97fSChristopher Meis {
10973cbff97fSChristopher Meis busIface++;
10983cbff97fSChristopher Meis }
10993cbff97fSChristopher Meis }
11003cbff97fSChristopher Meis auto found = busmap.find(busNum);
11013cbff97fSChristopher Meis if (found == busmap.end() || found->second == nullptr)
11023cbff97fSChristopher Meis {
11033cbff97fSChristopher Meis return;
11043cbff97fSChristopher Meis }
11053cbff97fSChristopher Meis for (auto& device : *(found->second))
11063cbff97fSChristopher Meis {
11073cbff97fSChristopher Meis addFruObjectToDbus(device.second, dbusInterfaceMap,
11083cbff97fSChristopher Meis static_cast<uint32_t>(busNum), device.first,
11093cbff97fSChristopher Meis unknownBusObjectCount, powerIsOn, objServer,
11103cbff97fSChristopher Meis systemBus);
11113cbff97fSChristopher Meis }
11123cbff97fSChristopher Meis });
11133cbff97fSChristopher Meis scan->run();
11143cbff97fSChristopher Meis }
11153cbff97fSChristopher Meis
rescanBusses(BusMap & busmap,boost::container::flat_map<std::pair<size_t,size_t>,std::shared_ptr<sdbusplus::asio::dbus_interface>> & dbusInterfaceMap,size_t & unknownBusObjectCount,const bool & powerIsOn,sdbusplus::asio::object_server & objServer,std::shared_ptr<sdbusplus::asio::connection> & systemBus)11163cbff97fSChristopher Meis void rescanBusses(
11173cbff97fSChristopher Meis BusMap& busmap,
11183cbff97fSChristopher Meis boost::container::flat_map<
11193cbff97fSChristopher Meis std::pair<size_t, size_t>,
11203cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
11213cbff97fSChristopher Meis size_t& unknownBusObjectCount, const bool& powerIsOn,
11223cbff97fSChristopher Meis sdbusplus::asio::object_server& objServer,
11233cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::connection>& systemBus)
11243cbff97fSChristopher Meis {
11253cbff97fSChristopher Meis static boost::asio::steady_timer timer(io);
11263cbff97fSChristopher Meis timer.expires_after(std::chrono::seconds(1));
11273cbff97fSChristopher Meis
11283cbff97fSChristopher Meis // setup an async wait in case we get flooded with requests
11293cbff97fSChristopher Meis timer.async_wait([&](const boost::system::error_code& ec) {
11303cbff97fSChristopher Meis if (ec == boost::asio::error::operation_aborted)
11313cbff97fSChristopher Meis {
11323cbff97fSChristopher Meis return;
11333cbff97fSChristopher Meis }
11343cbff97fSChristopher Meis
11353cbff97fSChristopher Meis if (ec)
11363cbff97fSChristopher Meis {
11373cbff97fSChristopher Meis std::cerr << "Error in timer: " << ec.message() << "\n";
11383cbff97fSChristopher Meis return;
11393cbff97fSChristopher Meis }
11403cbff97fSChristopher Meis
11413cbff97fSChristopher Meis auto devDir = fs::path("/dev/");
11423cbff97fSChristopher Meis std::vector<fs::path> i2cBuses;
11433cbff97fSChristopher Meis
11443cbff97fSChristopher Meis boost::container::flat_map<size_t, fs::path> busPaths;
11453cbff97fSChristopher Meis if (!getI2cDevicePaths(devDir, busPaths))
11463cbff97fSChristopher Meis {
11473cbff97fSChristopher Meis std::cerr << "unable to find i2c devices\n";
11483cbff97fSChristopher Meis return;
11493cbff97fSChristopher Meis }
11503cbff97fSChristopher Meis
11513cbff97fSChristopher Meis for (const auto& busPath : busPaths)
11523cbff97fSChristopher Meis {
11533cbff97fSChristopher Meis i2cBuses.emplace_back(busPath.second);
11543cbff97fSChristopher Meis }
11553cbff97fSChristopher Meis
11563cbff97fSChristopher Meis busmap.clear();
11573cbff97fSChristopher Meis for (auto& [pair, interface] : foundDevices)
11583cbff97fSChristopher Meis {
11593cbff97fSChristopher Meis objServer.remove_interface(interface);
11603cbff97fSChristopher Meis }
11613cbff97fSChristopher Meis foundDevices.clear();
11623cbff97fSChristopher Meis
11633cbff97fSChristopher Meis auto scan = std::make_shared<FindDevicesWithCallback>(
11643cbff97fSChristopher Meis i2cBuses, busmap, powerIsOn, objServer, [&]() {
11653cbff97fSChristopher Meis for (auto& busIface : dbusInterfaceMap)
11663cbff97fSChristopher Meis {
11673cbff97fSChristopher Meis objServer.remove_interface(busIface.second);
11683cbff97fSChristopher Meis }
11693cbff97fSChristopher Meis
11703cbff97fSChristopher Meis dbusInterfaceMap.clear();
11713cbff97fSChristopher Meis unknownBusObjectCount = 0;
11723cbff97fSChristopher Meis
11733cbff97fSChristopher Meis // todo, get this from a more sensable place
11743cbff97fSChristopher Meis std::vector<uint8_t> baseboardFRU;
11753cbff97fSChristopher Meis if (readBaseboardFRU(baseboardFRU))
11763cbff97fSChristopher Meis {
11773cbff97fSChristopher Meis // If no device on i2c bus 0, the insertion will happen.
11783cbff97fSChristopher Meis auto bus0 =
11793cbff97fSChristopher Meis busmap.try_emplace(0, std::make_shared<DeviceMap>());
11803cbff97fSChristopher Meis bus0.first->second->emplace(0, baseboardFRU);
11813cbff97fSChristopher Meis }
11823cbff97fSChristopher Meis for (auto& devicemap : busmap)
11833cbff97fSChristopher Meis {
11843cbff97fSChristopher Meis for (auto& device : *devicemap.second)
11853cbff97fSChristopher Meis {
11863cbff97fSChristopher Meis addFruObjectToDbus(device.second, dbusInterfaceMap,
11873cbff97fSChristopher Meis devicemap.first, device.first,
11883cbff97fSChristopher Meis unknownBusObjectCount, powerIsOn,
11893cbff97fSChristopher Meis objServer, systemBus);
11903cbff97fSChristopher Meis }
11913cbff97fSChristopher Meis }
11923cbff97fSChristopher Meis });
11933cbff97fSChristopher Meis scan->run();
11943cbff97fSChristopher Meis });
11953cbff97fSChristopher Meis }
11963cbff97fSChristopher Meis
11973cbff97fSChristopher Meis // Details with example of Asset Tag Update
11983cbff97fSChristopher Meis // To find location of Product Info Area asset tag as per FRU specification
11993cbff97fSChristopher Meis // 1. Find product Info area starting offset (*8 - as header will be in
12003cbff97fSChristopher Meis // multiple of 8 bytes).
12013cbff97fSChristopher Meis // 2. Skip 3 bytes of product info area (like format version, area length,
12023cbff97fSChristopher Meis // and language code).
12033cbff97fSChristopher Meis // 3. Traverse manufacturer name, product name, product version, & product
12043cbff97fSChristopher Meis // serial number, by reading type/length code to reach the Asset Tag.
12053cbff97fSChristopher Meis // 4. Update the Asset Tag, reposition the product Info area in multiple of
12063cbff97fSChristopher Meis // 8 bytes. Update the Product area length and checksum.
12073cbff97fSChristopher Meis
updateFRUProperty(const std::string & updatePropertyReq,uint32_t bus,uint32_t address,const std::string & propertyName,boost::container::flat_map<std::pair<size_t,size_t>,std::shared_ptr<sdbusplus::asio::dbus_interface>> & dbusInterfaceMap,size_t & unknownBusObjectCount,const bool & powerIsOn,sdbusplus::asio::object_server & objServer,std::shared_ptr<sdbusplus::asio::connection> & systemBus)12083cbff97fSChristopher Meis bool updateFRUProperty(
12093cbff97fSChristopher Meis const std::string& updatePropertyReq, uint32_t bus, uint32_t address,
12103cbff97fSChristopher Meis const std::string& propertyName,
12113cbff97fSChristopher Meis boost::container::flat_map<
12123cbff97fSChristopher Meis std::pair<size_t, size_t>,
12133cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::dbus_interface>>& dbusInterfaceMap,
12143cbff97fSChristopher Meis size_t& unknownBusObjectCount, const bool& powerIsOn,
12153cbff97fSChristopher Meis sdbusplus::asio::object_server& objServer,
12163cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::connection>& systemBus)
12173cbff97fSChristopher Meis {
12183cbff97fSChristopher Meis size_t updatePropertyReqLen = updatePropertyReq.length();
12193cbff97fSChristopher Meis if (updatePropertyReqLen == 1 || updatePropertyReqLen > 63)
12203cbff97fSChristopher Meis {
12213cbff97fSChristopher Meis std::cerr
12223cbff97fSChristopher Meis << "FRU field data cannot be of 1 char or more than 63 chars. "
12233cbff97fSChristopher Meis "Invalid Length "
12243cbff97fSChristopher Meis << updatePropertyReqLen << "\n";
12253cbff97fSChristopher Meis return false;
12263cbff97fSChristopher Meis }
12273cbff97fSChristopher Meis
12283cbff97fSChristopher Meis std::vector<uint8_t> fruData;
12293cbff97fSChristopher Meis
12303cbff97fSChristopher Meis if (!getFruData(fruData, bus, address))
12313cbff97fSChristopher Meis {
12323cbff97fSChristopher Meis std::cerr << "Failure getting FRU Data \n";
12333cbff97fSChristopher Meis return false;
12343cbff97fSChristopher Meis }
12353cbff97fSChristopher Meis
12363cbff97fSChristopher Meis struct FruArea fruAreaParams{};
12373cbff97fSChristopher Meis
12383cbff97fSChristopher Meis if (!findFruAreaLocationAndField(fruData, propertyName, fruAreaParams))
12393cbff97fSChristopher Meis {
12403cbff97fSChristopher Meis std::cerr << "findFruAreaLocationAndField failed \n";
12413cbff97fSChristopher Meis return false;
12423cbff97fSChristopher Meis }
12433cbff97fSChristopher Meis
12443cbff97fSChristopher Meis std::vector<uint8_t> restFRUAreaFieldsData;
12453cbff97fSChristopher Meis if (!copyRestFRUArea(fruData, propertyName, fruAreaParams,
12463cbff97fSChristopher Meis restFRUAreaFieldsData))
12473cbff97fSChristopher Meis {
12483cbff97fSChristopher Meis std::cerr << "copyRestFRUArea failed \n";
12493cbff97fSChristopher Meis return false;
12503cbff97fSChristopher Meis }
12513cbff97fSChristopher Meis
12523cbff97fSChristopher Meis // Push post update fru areas if any
12533cbff97fSChristopher Meis unsigned int nextFRUAreaLoc = 0;
12543cbff97fSChristopher Meis for (fruAreas nextFRUArea = fruAreas::fruAreaInternal;
12553cbff97fSChristopher Meis nextFRUArea <= fruAreas::fruAreaMultirecord; ++nextFRUArea)
12563cbff97fSChristopher Meis {
12573cbff97fSChristopher Meis unsigned int fruAreaLoc =
12583cbff97fSChristopher Meis fruData[getHeaderAreaFieldOffset(nextFRUArea)] * fruBlockSize;
12593cbff97fSChristopher Meis if ((fruAreaLoc > fruAreaParams.restFieldsEnd) &&
12603cbff97fSChristopher Meis ((nextFRUAreaLoc == 0) || (fruAreaLoc < nextFRUAreaLoc)))
12613cbff97fSChristopher Meis {
12623cbff97fSChristopher Meis nextFRUAreaLoc = fruAreaLoc;
12633cbff97fSChristopher Meis }
12643cbff97fSChristopher Meis }
12653cbff97fSChristopher Meis std::vector<uint8_t> restFRUAreasData;
12663cbff97fSChristopher Meis if (nextFRUAreaLoc != 0U)
12673cbff97fSChristopher Meis {
12683cbff97fSChristopher Meis std::copy_n(fruData.begin() + nextFRUAreaLoc,
12693cbff97fSChristopher Meis fruData.size() - nextFRUAreaLoc,
12703cbff97fSChristopher Meis std::back_inserter(restFRUAreasData));
12713cbff97fSChristopher Meis }
12723cbff97fSChristopher Meis
12733cbff97fSChristopher Meis // check FRU area size
12743cbff97fSChristopher Meis size_t fruAreaDataSize =
12753cbff97fSChristopher Meis ((fruAreaParams.updateFieldLoc - fruAreaParams.start + 1) +
12763cbff97fSChristopher Meis restFRUAreaFieldsData.size());
12773cbff97fSChristopher Meis size_t fruAreaAvailableSize = fruAreaParams.size - fruAreaDataSize;
12783cbff97fSChristopher Meis if ((updatePropertyReqLen + 1) > fruAreaAvailableSize)
12793cbff97fSChristopher Meis {
12803cbff97fSChristopher Meis #ifdef ENABLE_FRU_AREA_RESIZE
12813cbff97fSChristopher Meis size_t newFRUAreaSize = fruAreaDataSize + updatePropertyReqLen + 1;
12823cbff97fSChristopher Meis // round size to 8-byte blocks
12833cbff97fSChristopher Meis newFRUAreaSize =
12843cbff97fSChristopher Meis ((newFRUAreaSize - 1) / fruBlockSize + 1) * fruBlockSize;
12853cbff97fSChristopher Meis size_t newFRUDataSize =
12863cbff97fSChristopher Meis fruData.size() + newFRUAreaSize - fruAreaParams.size;
12873cbff97fSChristopher Meis fruData.resize(newFRUDataSize);
12883cbff97fSChristopher Meis fruAreaParams.size = newFRUAreaSize;
12893cbff97fSChristopher Meis fruAreaParams.end = fruAreaParams.start + fruAreaParams.size;
12903cbff97fSChristopher Meis #else
12913cbff97fSChristopher Meis std::cerr << "FRU field length: " << updatePropertyReqLen + 1
12923cbff97fSChristopher Meis << " should not be greater than available FRU area size: "
12933cbff97fSChristopher Meis << fruAreaAvailableSize << "\n";
12943cbff97fSChristopher Meis return false;
12953cbff97fSChristopher Meis #endif // ENABLE_FRU_AREA_RESIZE
12963cbff97fSChristopher Meis }
12973cbff97fSChristopher Meis
12983cbff97fSChristopher Meis // write new requested property field length and data
12993cbff97fSChristopher Meis constexpr uint8_t newTypeLenMask = 0xC0;
13003cbff97fSChristopher Meis fruData[fruAreaParams.updateFieldLoc] =
13013cbff97fSChristopher Meis static_cast<uint8_t>(updatePropertyReqLen | newTypeLenMask);
13023cbff97fSChristopher Meis fruAreaParams.updateFieldLoc++;
13033cbff97fSChristopher Meis std::copy(updatePropertyReq.begin(), updatePropertyReq.end(),
13043cbff97fSChristopher Meis fruData.begin() + fruAreaParams.updateFieldLoc);
13053cbff97fSChristopher Meis
13063cbff97fSChristopher Meis // Copy remaining data to main fru area - post updated fru field vector
13073cbff97fSChristopher Meis fruAreaParams.restFieldsLoc =
13083cbff97fSChristopher Meis fruAreaParams.updateFieldLoc + updatePropertyReqLen;
13093cbff97fSChristopher Meis size_t fruAreaDataEnd =
13103cbff97fSChristopher Meis fruAreaParams.restFieldsLoc + restFRUAreaFieldsData.size();
13113cbff97fSChristopher Meis
13123cbff97fSChristopher Meis std::copy(restFRUAreaFieldsData.begin(), restFRUAreaFieldsData.end(),
13133cbff97fSChristopher Meis fruData.begin() + fruAreaParams.restFieldsLoc);
13143cbff97fSChristopher Meis
13153cbff97fSChristopher Meis // Update final fru with new fru area length and checksum
13163cbff97fSChristopher Meis unsigned int nextFRUAreaNewLoc = updateFRUAreaLenAndChecksum(
13173cbff97fSChristopher Meis fruData, fruAreaParams.start, fruAreaDataEnd, fruAreaParams.end);
13183cbff97fSChristopher Meis
13193cbff97fSChristopher Meis #ifdef ENABLE_FRU_AREA_RESIZE
13203cbff97fSChristopher Meis ++nextFRUAreaNewLoc;
13213cbff97fSChristopher Meis ssize_t nextFRUAreaOffsetDiff =
13223cbff97fSChristopher Meis (nextFRUAreaNewLoc - nextFRUAreaLoc) / fruBlockSize;
13233cbff97fSChristopher Meis // Append rest FRU Areas if size changed and there were other sections after
13243cbff97fSChristopher Meis // updated one
13253cbff97fSChristopher Meis if (nextFRUAreaOffsetDiff && nextFRUAreaLoc)
13263cbff97fSChristopher Meis {
13273cbff97fSChristopher Meis std::copy(restFRUAreasData.begin(), restFRUAreasData.end(),
13283cbff97fSChristopher Meis fruData.begin() + nextFRUAreaNewLoc);
13293cbff97fSChristopher Meis // Update Common Header
13303cbff97fSChristopher Meis for (fruAreas nextFRUArea = fruAreas::fruAreaInternal;
13313cbff97fSChristopher Meis nextFRUArea <= fruAreas::fruAreaMultirecord; ++nextFRUArea)
13323cbff97fSChristopher Meis {
13333cbff97fSChristopher Meis unsigned int fruAreaOffsetField =
13343cbff97fSChristopher Meis getHeaderAreaFieldOffset(nextFRUArea);
13353cbff97fSChristopher Meis size_t curFRUAreaOffset = fruData[fruAreaOffsetField];
13363cbff97fSChristopher Meis if (curFRUAreaOffset > fruAreaParams.end)
13373cbff97fSChristopher Meis {
13383cbff97fSChristopher Meis fruData[fruAreaOffsetField] = static_cast<int8_t>(
13393cbff97fSChristopher Meis curFRUAreaOffset + nextFRUAreaOffsetDiff);
13403cbff97fSChristopher Meis }
13413cbff97fSChristopher Meis }
13423cbff97fSChristopher Meis // Calculate new checksum
13433cbff97fSChristopher Meis std::vector<uint8_t> headerFRUData;
13443cbff97fSChristopher Meis std::copy_n(fruData.begin(), 7, std::back_inserter(headerFRUData));
13453cbff97fSChristopher Meis size_t checksumVal = calculateChecksum(headerFRUData);
13463cbff97fSChristopher Meis fruData[7] = static_cast<uint8_t>(checksumVal);
13473cbff97fSChristopher Meis // fill zeros if FRU Area size decreased
13483cbff97fSChristopher Meis if (nextFRUAreaOffsetDiff < 0)
13493cbff97fSChristopher Meis {
13503cbff97fSChristopher Meis std::fill(fruData.begin() + nextFRUAreaNewLoc +
13513cbff97fSChristopher Meis restFRUAreasData.size(),
13523cbff97fSChristopher Meis fruData.end(), 0);
13533cbff97fSChristopher Meis }
13543cbff97fSChristopher Meis }
13553cbff97fSChristopher Meis #else
13563cbff97fSChristopher Meis // this is to avoid "unused variable" warning
13573cbff97fSChristopher Meis (void)nextFRUAreaNewLoc;
13583cbff97fSChristopher Meis #endif // ENABLE_FRU_AREA_RESIZE
13593cbff97fSChristopher Meis if (fruData.empty())
13603cbff97fSChristopher Meis {
13613cbff97fSChristopher Meis return false;
13623cbff97fSChristopher Meis }
13633cbff97fSChristopher Meis
13643cbff97fSChristopher Meis if (!writeFRU(static_cast<uint8_t>(bus), static_cast<uint8_t>(address),
13653cbff97fSChristopher Meis fruData))
13663cbff97fSChristopher Meis {
13673cbff97fSChristopher Meis return false;
13683cbff97fSChristopher Meis }
13693cbff97fSChristopher Meis
13703cbff97fSChristopher Meis // Rescan the bus so that GetRawFru dbus-call fetches updated values
13713cbff97fSChristopher Meis rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, powerIsOn,
13723cbff97fSChristopher Meis objServer, systemBus);
13733cbff97fSChristopher Meis return true;
13743cbff97fSChristopher Meis }
13753cbff97fSChristopher Meis
main()13763cbff97fSChristopher Meis int main()
13773cbff97fSChristopher Meis {
13783cbff97fSChristopher Meis auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
13793cbff97fSChristopher Meis sdbusplus::asio::object_server objServer(systemBus);
13803cbff97fSChristopher Meis
13813cbff97fSChristopher Meis static size_t unknownBusObjectCount = 0;
13823cbff97fSChristopher Meis static bool powerIsOn = false;
13833cbff97fSChristopher Meis auto devDir = fs::path("/dev/");
13843cbff97fSChristopher Meis auto matchString = std::string(R"(i2c-\d+$)");
13853cbff97fSChristopher Meis std::vector<fs::path> i2cBuses;
13863cbff97fSChristopher Meis
13873cbff97fSChristopher Meis if (!findFiles(devDir, matchString, i2cBuses))
13883cbff97fSChristopher Meis {
13893cbff97fSChristopher Meis std::cerr << "unable to find i2c devices\n";
13903cbff97fSChristopher Meis return 1;
13913cbff97fSChristopher Meis }
13923cbff97fSChristopher Meis
13933cbff97fSChristopher Meis // check for and load blocklist with initial buses.
13943cbff97fSChristopher Meis loadBlocklist(blocklistPath);
13953cbff97fSChristopher Meis
13963cbff97fSChristopher Meis systemBus->request_name("xyz.openbmc_project.FruDevice");
13973cbff97fSChristopher Meis
13983cbff97fSChristopher Meis // this is a map with keys of pair(bus number, address) and values of
13993cbff97fSChristopher Meis // the object on dbus
14003cbff97fSChristopher Meis boost::container::flat_map<std::pair<size_t, size_t>,
14013cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::dbus_interface>>
14023cbff97fSChristopher Meis dbusInterfaceMap;
14033cbff97fSChristopher Meis
14043cbff97fSChristopher Meis std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
14053cbff97fSChristopher Meis objServer.add_interface("/xyz/openbmc_project/FruDevice",
14063cbff97fSChristopher Meis "xyz.openbmc_project.FruDeviceManager");
14073cbff97fSChristopher Meis
14083cbff97fSChristopher Meis iface->register_method("ReScan", [&]() {
14093cbff97fSChristopher Meis rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, powerIsOn,
14103cbff97fSChristopher Meis objServer, systemBus);
14113cbff97fSChristopher Meis });
14123cbff97fSChristopher Meis
14133cbff97fSChristopher Meis iface->register_method("ReScanBus", [&](uint16_t bus) {
14143cbff97fSChristopher Meis rescanOneBus(busMap, bus, dbusInterfaceMap, true, unknownBusObjectCount,
14153cbff97fSChristopher Meis powerIsOn, objServer, systemBus);
14163cbff97fSChristopher Meis });
14173cbff97fSChristopher Meis
14183cbff97fSChristopher Meis iface->register_method("GetRawFru", getFRUInfo);
14193cbff97fSChristopher Meis
14203cbff97fSChristopher Meis iface->register_method(
14213cbff97fSChristopher Meis "WriteFru", [&](const uint16_t bus, const uint8_t address,
14223cbff97fSChristopher Meis const std::vector<uint8_t>& data) {
14233cbff97fSChristopher Meis if (!writeFRU(bus, address, data))
14243cbff97fSChristopher Meis {
14253cbff97fSChristopher Meis throw std::invalid_argument("Invalid Arguments.");
14263cbff97fSChristopher Meis return;
14273cbff97fSChristopher Meis }
14283cbff97fSChristopher Meis // schedule rescan on success
14293cbff97fSChristopher Meis rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount,
14303cbff97fSChristopher Meis powerIsOn, objServer, systemBus);
14313cbff97fSChristopher Meis });
14323cbff97fSChristopher Meis iface->initialize();
14333cbff97fSChristopher Meis
14343cbff97fSChristopher Meis std::function<void(sdbusplus::message_t & message)> eventHandler =
14353cbff97fSChristopher Meis [&](sdbusplus::message_t& message) {
14363cbff97fSChristopher Meis std::string objectName;
14373cbff97fSChristopher Meis boost::container::flat_map<
14383cbff97fSChristopher Meis std::string,
14393cbff97fSChristopher Meis std::variant<std::string, bool, int64_t, uint64_t, double>>
14403cbff97fSChristopher Meis values;
14413cbff97fSChristopher Meis message.read(objectName, values);
14423cbff97fSChristopher Meis auto findState = values.find("CurrentHostState");
14433cbff97fSChristopher Meis if (findState != values.end())
14443cbff97fSChristopher Meis {
14453cbff97fSChristopher Meis if (std::get<std::string>(findState->second) ==
14463cbff97fSChristopher Meis "xyz.openbmc_project.State.Host.HostState.Running")
14473cbff97fSChristopher Meis {
14483cbff97fSChristopher Meis powerIsOn = true;
14493cbff97fSChristopher Meis }
14503cbff97fSChristopher Meis }
14513cbff97fSChristopher Meis
14523cbff97fSChristopher Meis if (powerIsOn)
14533cbff97fSChristopher Meis {
14543cbff97fSChristopher Meis rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount,
14553cbff97fSChristopher Meis powerIsOn, objServer, systemBus);
14563cbff97fSChristopher Meis }
14573cbff97fSChristopher Meis };
14583cbff97fSChristopher Meis
14593cbff97fSChristopher Meis sdbusplus::bus::match_t powerMatch = sdbusplus::bus::match_t(
14603cbff97fSChristopher Meis static_cast<sdbusplus::bus_t&>(*systemBus),
14613cbff97fSChristopher Meis "type='signal',interface='org.freedesktop.DBus.Properties',path='/xyz/"
14623cbff97fSChristopher Meis "openbmc_project/state/"
14633cbff97fSChristopher Meis "host0',arg0='xyz.openbmc_project.State.Host'",
14643cbff97fSChristopher Meis eventHandler);
14653cbff97fSChristopher Meis
14663cbff97fSChristopher Meis int fd = inotify_init();
14673cbff97fSChristopher Meis inotify_add_watch(fd, i2CDevLocation, IN_CREATE | IN_MOVED_TO | IN_DELETE);
14683cbff97fSChristopher Meis std::array<char, 4096> readBuffer{};
14693cbff97fSChristopher Meis // monitor for new i2c devices
14703cbff97fSChristopher Meis boost::asio::posix::stream_descriptor dirWatch(io, fd);
14713cbff97fSChristopher Meis std::function<void(const boost::system::error_code, std::size_t)>
14723cbff97fSChristopher Meis watchI2cBusses = [&](const boost::system::error_code& ec,
14733cbff97fSChristopher Meis std::size_t bytesTransferred) {
14743cbff97fSChristopher Meis if (ec)
14753cbff97fSChristopher Meis {
14763cbff97fSChristopher Meis std::cout << "Callback Error " << ec << "\n";
14773cbff97fSChristopher Meis return;
14783cbff97fSChristopher Meis }
14793cbff97fSChristopher Meis size_t index = 0;
14803cbff97fSChristopher Meis while ((index + sizeof(inotify_event)) <= bytesTransferred)
14813cbff97fSChristopher Meis {
14823cbff97fSChristopher Meis const char* p = &readBuffer[index];
14833cbff97fSChristopher Meis // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
14843cbff97fSChristopher Meis const auto* iEvent = reinterpret_cast<const inotify_event*>(p);
14853cbff97fSChristopher Meis switch (iEvent->mask)
14863cbff97fSChristopher Meis {
14873cbff97fSChristopher Meis case IN_CREATE:
14883cbff97fSChristopher Meis case IN_MOVED_TO:
14893cbff97fSChristopher Meis case IN_DELETE:
14903cbff97fSChristopher Meis {
14913cbff97fSChristopher Meis std::string_view name(&iEvent->name[0], iEvent->len);
14923cbff97fSChristopher Meis if (boost::starts_with(name, "i2c"))
14933cbff97fSChristopher Meis {
14943cbff97fSChristopher Meis int bus = busStrToInt(name);
14953cbff97fSChristopher Meis if (bus < 0)
14963cbff97fSChristopher Meis {
14973cbff97fSChristopher Meis std::cerr
14983cbff97fSChristopher Meis << "Could not parse bus " << name << "\n";
14993cbff97fSChristopher Meis continue;
15003cbff97fSChristopher Meis }
15013cbff97fSChristopher Meis int rootBus = getRootBus(bus);
15023cbff97fSChristopher Meis if (rootBus >= 0)
15033cbff97fSChristopher Meis {
15043cbff97fSChristopher Meis rescanOneBus(busMap,
15053cbff97fSChristopher Meis static_cast<uint16_t>(rootBus),
15063cbff97fSChristopher Meis dbusInterfaceMap, false,
15073cbff97fSChristopher Meis unknownBusObjectCount, powerIsOn,
15083cbff97fSChristopher Meis objServer, systemBus);
15093cbff97fSChristopher Meis }
15103cbff97fSChristopher Meis rescanOneBus(busMap, static_cast<uint16_t>(bus),
15113cbff97fSChristopher Meis dbusInterfaceMap, false,
15123cbff97fSChristopher Meis unknownBusObjectCount, powerIsOn,
15133cbff97fSChristopher Meis objServer, systemBus);
15143cbff97fSChristopher Meis }
15153cbff97fSChristopher Meis }
15163cbff97fSChristopher Meis break;
15173cbff97fSChristopher Meis default:
15183cbff97fSChristopher Meis break;
15193cbff97fSChristopher Meis }
15203cbff97fSChristopher Meis index += sizeof(inotify_event) + iEvent->len;
15213cbff97fSChristopher Meis }
15223cbff97fSChristopher Meis
15233cbff97fSChristopher Meis dirWatch.async_read_some(boost::asio::buffer(readBuffer),
15243cbff97fSChristopher Meis watchI2cBusses);
15253cbff97fSChristopher Meis };
15263cbff97fSChristopher Meis
15273cbff97fSChristopher Meis dirWatch.async_read_some(boost::asio::buffer(readBuffer), watchI2cBusses);
15283cbff97fSChristopher Meis // run the initial scan
15293cbff97fSChristopher Meis rescanBusses(busMap, dbusInterfaceMap, unknownBusObjectCount, powerIsOn,
15303cbff97fSChristopher Meis objServer, systemBus);
15313cbff97fSChristopher Meis
15323cbff97fSChristopher Meis io.run();
15333cbff97fSChristopher Meis return 0;
15343cbff97fSChristopher Meis }
1535