#pragma once #include "sdeventplus.hpp" #include #include #include #include #include #include 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 timer; }; } // namespace phosphor::fan::presence