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