blob: 2467b18a1e8ff4dba444d1ca55020c57ba30d189 [file] [log] [blame] [edit]
#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;
}