blob: a1934712e126f6127f6ed5ba68daac851bbcf282 [file] [log] [blame]
Matt Spinlerbc4179e2022-10-04 15:15:06 -05001#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
13namespace phosphor::fan::presence
14{
15using 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 */
24class 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 Williamsdfddd642024-08-16 15:21:51 -040044 address(address), path(baseDriverPath / driver),
45 bindDelay(bindDelayInMS),
Matt Spinlerbc4179e2022-10-04 15:15:06 -050046 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