blob: a1934712e126f6127f6ed5ba68daac851bbcf282 [file] [log] [blame]
#pragma once
#include "sdeventplus.hpp"
#include <phosphor-logging/lg2.hpp>
#include <sdeventplus/clock.hpp>
#include <sdeventplus/utility/timer.hpp>
#include <filesystem>
#include <fstream>
#include <string>
namespace phosphor::fan::presence
{
using namespace phosphor::fan::util;
/**
* @class EEPROMDevice
*
* Provides an API to bind an EEPROM driver to a device, after waiting
* a configurable amount of time in case the device needs time
* to initialize after being plugged into a system.
*/
class EEPROMDevice
{
public:
EEPROMDevice() = delete;
~EEPROMDevice() = default;
EEPROMDevice(const EEPROMDevice&) = delete;
EEPROMDevice& operator=(const EEPROMDevice&) = delete;
EEPROMDevice(EEPROMDevice&&) = delete;
EEPROMDevice& operator=(EEPROMDevice&&) = delete;
/**
* @brief Constructor
* @param[in] address - The bus-address string as used by drivers
* in sysfs.
* @param[in] driver - The I2C driver name in sysfs
* @param[in] bindDelayInMS - The time in milliseconds to wait
* before actually doing the bind.
*/
EEPROMDevice(const std::string& address, const std::string& driver,
size_t bindDelayInMS) :
address(address), path(baseDriverPath / driver),
bindDelay(bindDelayInMS),
timer(SDEventPlus::getEvent(),
std::bind(std::mem_fn(&EEPROMDevice::bindTimerExpired), this))
{}
/**
* @brief Kicks off the timer to do the actual bind
*/
void bind()
{
timer.restartOnce(std::chrono::milliseconds{bindDelay});
}
/**
* @brief Stops the bind timer if running and unbinds the device
*/
void unbind()
{
if (timer.isEnabled())
{
timer.setEnabled(false);
}
unbindDevice();
}
private:
/**
* @brief When the bind timer expires it will bind the device.
*/
void bindTimerExpired() const
{
unbindDevice();
auto bindPath = path / "bind";
std::ofstream bind{bindPath};
if (bind.good())
{
lg2::info("Binding fan EEPROM device with address {ADDRESS}",
"ADDRESS", address);
bind << address;
}
if (bind.fail())
{
lg2::error("Error while binding fan EEPROM device with path {PATH}"
" and address {ADDR}",
"PATH", bindPath, "ADDR", address);
}
}
/**
* @brief Unbinds the device.
*/
void unbindDevice() const
{
auto devicePath = path / address;
if (!std::filesystem::exists(devicePath))
{
return;
}
auto unbindPath = path / "unbind";
std::ofstream unbind{unbindPath};
if (unbind.good())
{
unbind << address;
}
if (unbind.fail())
{
lg2::error("Error while unbinding fan EEPROM device with path"
" {PATH} and address {ADDR}",
"PATH", unbindPath, "ADDR", address);
}
}
/** @brief The base I2C drivers directory in sysfs */
const std::filesystem::path baseDriverPath{"/sys/bus/i2c/drivers"};
/**
* @brief The address string with the i2c bus and address.
* Example: '32-0050'
*/
const std::string address;
/** @brief The path to the driver dir, like /sys/bus/i2c/drivers/at24 */
const std::filesystem::path path;
/** @brief Number of milliseconds to delay to actually do the bind. */
const size_t bindDelay{};
/** @brief The timer to do the delay with */
sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> timer;
};
} // namespace phosphor::fan::presence