1 #pragma once
2
3 #include "Utils.hpp"
4
5 #include <boost/container/flat_map.hpp>
6
7 #include <functional>
8 #include <optional>
9 #include <string_view>
10
11 struct I2CDeviceType
12 {
13 const char* name;
14 bool createsHWMon;
15 };
16
17 struct I2CDeviceComparator
18 {
operator ()I2CDeviceComparator19 bool operator()(const std::string& a, const std::string& b) const noexcept
20 {
21 return strcasecmp(a.c_str(), b.c_str()) < 0;
22 }
23 };
24
25 using I2CDeviceTypeMap =
26 boost::container::flat_map<std::string, I2CDeviceType, I2CDeviceComparator>;
27
28 struct I2CDeviceParams
29 {
I2CDeviceParamsI2CDeviceParams30 I2CDeviceParams(const I2CDeviceType& type, uint64_t bus, uint64_t address) :
31 type(&type), bus(bus), address(address) {};
32
33 const I2CDeviceType* type;
34 uint64_t bus;
35 uint64_t address;
36
37 bool devicePresent() const;
38 bool deviceStatic() const;
39 };
40
41 std::optional<I2CDeviceParams> getI2CDeviceParams(
42 const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg);
43
44 class I2CDevice
45 {
46 public:
47 explicit I2CDevice(I2CDeviceParams params);
48 ~I2CDevice();
49
50 private:
51 I2CDeviceParams params;
52
53 int create() const;
54 int destroy() const;
55 };
56
57 // HACK: this declaration "should" live in Utils.hpp, but that leads to a
58 // tangle of header-dependency hell because each header needs types declared
59 // in the other.
60 std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
61 setupPropertiesChangedMatches(
62 sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap,
63 const std::function<void(sdbusplus::message_t&)>& handler);
64
65 // Helper find function because some sensors use underscores in their names
66 // while others use spaces. Ignore this discrepancy by changing all spaces to
67 // underscores.
sensorNameFind(const std::string & fullName,const std::string & partialName)68 inline size_t sensorNameFind(const std::string& fullName,
69 const std::string& partialName)
70 {
71 return boost::replace_all_copy(fullName, " ", "_")
72 .find(boost::replace_all_copy(partialName, " ", "_"));
73 }
74
75 // returns a {path: <I2CDevice, is_new>} map. is_new indicates if the I2CDevice
76 // is newly instantiated by this call (true) or was already there (false).
77 template <class T>
78 boost::container::flat_map<std::string,
79 std::pair<std::shared_ptr<I2CDevice>, bool>>
instantiateDevices(const ManagedObjectType & sensorConfigs,const boost::container::flat_map<std::string,std::shared_ptr<T>> & sensors,const I2CDeviceTypeMap & sensorTypes)80 instantiateDevices(
81 const ManagedObjectType& sensorConfigs,
82 const boost::container::flat_map<std::string, std::shared_ptr<T>>&
83 sensors,
84 const I2CDeviceTypeMap& sensorTypes)
85 {
86 boost::container::flat_map<std::string,
87 std::pair<std::shared_ptr<I2CDevice>, bool>>
88 devices;
89 for (const auto& [path, sensor] : sensorConfigs)
90 {
91 for (const auto& [name, cfg] : sensor)
92 {
93 PowerState powerState = getPowerState(cfg);
94 if (!readingStateGood(powerState))
95 {
96 continue;
97 }
98
99 auto findSensorName = cfg.find("Name");
100 if (findSensorName == cfg.end())
101 {
102 continue;
103 }
104
105 const auto* sensorName =
106 std::get_if<std::string>(&findSensorName->second);
107 if (sensorName == nullptr)
108 {
109 std::cerr << "Unable to find sensor name " << name
110 << " on path " << path.str << "\n";
111 continue;
112 }
113
114 std::shared_ptr<T> findSensor(nullptr);
115 for (const auto& sensor : sensors)
116 {
117 if (sensorNameFind(sensor.first, *sensorName) !=
118 std::string::npos)
119 {
120 findSensor = sensor.second;
121 break;
122 }
123 }
124 if (findSensor != nullptr && findSensor->isActive())
125 {
126 devices.emplace(
127 path.str,
128 std::make_pair(findSensor->getI2CDevice(), false));
129 continue;
130 }
131
132 std::optional<I2CDeviceParams> params =
133 getI2CDeviceParams(sensorTypes, cfg);
134 if (params.has_value() && !params->deviceStatic())
135 {
136 // There exist error cases in which a sensor device that we
137 // need is already instantiated, but needs to be destroyed and
138 // re-created in order to be useful (for example if we crash
139 // after instantiating a device and the sensor device's power
140 // is cut before we get restarted, leaving it "present" but
141 // not really usable). To be on the safe side, instantiate a
142 // temporary device that's immediately destroyed so as to
143 // ensure that we end up with a fresh instance of it.
144 if (params->devicePresent())
145 {
146 std::cerr << "Clearing out previous instance for "
147 << path.str << "\n";
148 I2CDevice tmp(*params);
149 }
150
151 try
152 {
153 devices.emplace(
154 path.str,
155 std::make_pair(std::make_shared<I2CDevice>(*params),
156 true));
157 }
158 catch (std::runtime_error&)
159 {
160 std::cerr << "Failed to instantiate " << params->type->name
161 << " at address " << params->address << " on bus "
162 << params->bus << "\n";
163 }
164 }
165 }
166 }
167 return devices;
168 }
169