1 #pragma once 2 3 #include "sdeventplus.hpp" 4 5 #include <phosphor-logging/lg2.hpp> 6 #include <sdeventplus/clock.hpp> 7 #include <sdeventplus/utility/timer.hpp> 8 9 #include <filesystem> 10 #include <fstream> 11 #include <string> 12 13 namespace phosphor::fan::presence 14 { 15 using namespace phosphor::fan::util; 16 17 /** 18 * @class EEPROMDevice 19 * 20 * Provides an API to bind an EEPROM driver to a device, after waiting 21 * a configurable amount of time in case the device needs time 22 * to initialize after being plugged into a system. 23 */ 24 class EEPROMDevice 25 { 26 public: 27 EEPROMDevice() = delete; 28 ~EEPROMDevice() = default; 29 EEPROMDevice(const EEPROMDevice&) = delete; 30 EEPROMDevice& operator=(const EEPROMDevice&) = delete; 31 EEPROMDevice(EEPROMDevice&&) = delete; 32 EEPROMDevice& operator=(EEPROMDevice&&) = delete; 33 34 /** 35 * @brief Constructor 36 * @param[in] address - The bus-address string as used by drivers 37 * in sysfs. 38 * @param[in] driver - The I2C driver name in sysfs 39 * @param[in] bindDelayInMS - The time in milliseconds to wait 40 * before actually doing the bind. 41 */ 42 EEPROMDevice(const std::string& address, const std::string& driver, 43 size_t bindDelayInMS) : 44 address(address), 45 path(baseDriverPath / driver), bindDelay(bindDelayInMS), 46 timer(SDEventPlus::getEvent(), 47 std::bind(std::mem_fn(&EEPROMDevice::bindTimerExpired), this)) 48 {} 49 50 /** 51 * @brief Kicks off the timer to do the actual bind 52 */ 53 void bind() 54 { 55 timer.restartOnce(std::chrono::milliseconds{bindDelay}); 56 } 57 58 /** 59 * @brief Stops the bind timer if running and unbinds the device 60 */ 61 void unbind() 62 { 63 if (timer.isEnabled()) 64 { 65 timer.setEnabled(false); 66 } 67 68 unbindDevice(); 69 } 70 71 private: 72 /** 73 * @brief When the bind timer expires it will bind the device. 74 */ 75 void bindTimerExpired() const 76 { 77 unbindDevice(); 78 79 auto bindPath = path / "bind"; 80 std::ofstream bind{bindPath}; 81 if (bind.good()) 82 { 83 lg2::info("Binding fan EEPROM device with address {ADDRESS}", 84 "ADDRESS", address); 85 bind << address; 86 } 87 88 if (bind.fail()) 89 { 90 lg2::error("Error while binding fan EEPROM device with path {PATH}" 91 " and address {ADDR}", 92 "PATH", bindPath, "ADDR", address); 93 } 94 } 95 96 /** 97 * @brief Unbinds the device. 98 */ 99 void unbindDevice() const 100 { 101 auto devicePath = path / address; 102 if (!std::filesystem::exists(devicePath)) 103 { 104 return; 105 } 106 107 auto unbindPath = path / "unbind"; 108 std::ofstream unbind{unbindPath}; 109 if (unbind.good()) 110 { 111 unbind << address; 112 } 113 114 if (unbind.fail()) 115 { 116 lg2::error("Error while unbinding fan EEPROM device with path" 117 " {PATH} and address {ADDR}", 118 "PATH", unbindPath, "ADDR", address); 119 } 120 } 121 122 /** @brief The base I2C drivers directory in sysfs */ 123 const std::filesystem::path baseDriverPath{"/sys/bus/i2c/drivers"}; 124 125 /** 126 * @brief The address string with the i2c bus and address. 127 * Example: '32-0050' 128 */ 129 const std::string address; 130 131 /** @brief The path to the driver dir, like /sys/bus/i2c/drivers/at24 */ 132 const std::filesystem::path path; 133 134 /** @brief Number of milliseconds to delay to actually do the bind. */ 135 const size_t bindDelay{}; 136 137 /** @brief The timer to do the delay with */ 138 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic> timer; 139 }; 140 141 } // namespace phosphor::fan::presence 142