| #include "fru_device.hpp" |
| |
| #include "fru_utils.hpp" |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/i2c.h> |
| #include <linux/limits.h> |
| #include <sys/ioctl.h> |
| #include <unistd.h> |
| |
| #include <sdbusplus/bus.hpp> |
| #include <sdbusplus/exception.hpp> |
| |
| #include <cstdint> |
| #include <iostream> |
| #include <string> |
| #include <vector> |
| |
| extern "C" |
| { |
| #include <i2c/smbus.h> |
| #include <linux/i2c-dev.h> |
| } |
| |
| static constexpr const char* FRUDeviceService = "xyz.openbmc_project.FruDevice"; |
| static constexpr const char* FRUDeviceObjPath = |
| "/xyz/openbmc_project/FruDevice"; |
| static constexpr const char* FRUDeviceManagerInterface = |
| "xyz.openbmc_project.FruDeviceManager"; |
| struct FileDesc |
| { |
| explicit FileDesc(int fd) : _desc(fd) |
| {} |
| |
| explicit operator int() const |
| { |
| return _desc; |
| } |
| |
| bool operator==(const FileDesc& other) const |
| { |
| return _desc == other._desc; |
| } |
| bool operator!=(const FileDesc& other) const |
| { |
| return _desc != other._desc; |
| } |
| bool operator==(std::nullptr_t) const |
| { |
| return _desc == -1; |
| } |
| bool operator!=(std::nullptr_t) const |
| { |
| return _desc != -1; |
| } |
| |
| ~FileDesc() |
| { |
| if (_desc != -1) |
| { |
| close(_desc); |
| } |
| } |
| |
| int _desc; |
| }; |
| |
| // Code reference from |
| // https://kernel.googlesource.com/pub/scm/utils/i2c-tools/i2c-tools/+/v3.1.2/tools/i2cdetect.c |
| bool FRUDevice::isValidDevice() const |
| { |
| uint64_t funcs; |
| |
| std::string i2cBus = "/dev/i2c-" + std::to_string(bus); |
| |
| // Make sure file is closed before any exit path. |
| FileDesc file(open(i2cBus.c_str(), O_RDWR | O_CLOEXEC)); |
| if (static_cast<int>(file) < 0) |
| { |
| return false; |
| } |
| |
| if (ioctl(static_cast<int>(file), I2C_FUNCS, &funcs) < 0) |
| { |
| std::cerr << "Error: Could not get the I2C adapter functionality " |
| "matrix: " |
| << strerror(errno) << '\n'; |
| return false; |
| } |
| if (!(funcs & I2C_FUNC_SMBUS_READ_BYTE)) |
| { |
| std::cerr << "Error: Can't use SMBus Read Byte command on this bus" |
| << '\n'; |
| return false; |
| } |
| |
| if (ioctl(static_cast<int>(file), I2C_SLAVE, addr) < 0) |
| { |
| // Valid device if true |
| return errno == EBUSY; |
| } |
| |
| if (i2c_smbus_read_byte(static_cast<int>(file)) < 0) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| std::vector<uint8_t> FRUDevice::getFRUContents() const |
| { |
| uint8_t retry = 0; |
| std::vector<uint8_t> rawFRU; |
| |
| auto systemBus = sdbusplus::bus::new_bus(); |
| |
| auto method = |
| systemBus.new_method_call(FRUDeviceService, FRUDeviceObjPath, |
| FRUDeviceManagerInterface, "GetRawFru"); |
| method.append(static_cast<uint16_t>(bus)); |
| method.append(static_cast<uint8_t>(addr)); |
| |
| do |
| { |
| try |
| { |
| sdbusplus::message_t reply = systemBus.call(method); |
| reply.read(rawFRU); |
| break; |
| } |
| catch (const sdbusplus::exception_t& e) |
| { |
| std::cerr << "GetRawFru call failed on Bus: " << bus |
| << " at address: " << addr << "retrying after 1 second." |
| << e.what() << '\n'; |
| } |
| sleep(1); |
| } while (retry++ < 10); |
| |
| if (rawFRU.empty()) |
| { |
| std::cerr << "FRU device manager does have this FRU detected. Bus: " |
| << bus << "Address: " << addr << '\n'; |
| } |
| |
| return rawFRU; |
| } |