| Matt Spinler | bc4179e | 2022-10-04 15:15:06 -0500 | [diff] [blame] | 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) : | 
| Patrick Williams | dfddd64 | 2024-08-16 15:21:51 -0400 | [diff] [blame^] | 44 | address(address), path(baseDriverPath / driver), | 
|  | 45 | bindDelay(bindDelayInMS), | 
| Matt Spinler | bc4179e | 2022-10-04 15:15:06 -0500 | [diff] [blame] | 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 |