blob: 5247486974f6a965466828933d85cbe01f798250 [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
Ed Tanous18b61862025-01-30 10:56:28 -08005#include <strings.h>
Zev Weissdabd48d2022-08-03 15:43:17 -07006
Ed Tanous18b61862025-01-30 10:56:28 -08007#include <boost/algorithm/string/replace.hpp>
8#include <boost/container/flat_map.hpp>
9#include <sdbusplus/asio/connection.hpp>
10#include <sdbusplus/bus/match.hpp>
11#include <sdbusplus/message.hpp>
12
13#include <cstddef>
14#include <cstdint>
Zev Weissdabd48d2022-08-03 15:43:17 -070015#include <functional>
Ed Tanous18b61862025-01-30 10:56:28 -080016#include <iostream>
17#include <memory>
Zev Weissdabd48d2022-08-03 15:43:17 -070018#include <optional>
Ed Tanous18b61862025-01-30 10:56:28 -080019#include <stdexcept>
20#include <string>
21#include <utility>
22#include <variant>
23#include <vector>
Zev Weissdabd48d2022-08-03 15:43:17 -070024
25struct I2CDeviceType
26{
27 const char* name;
28 bool createsHWMon;
29};
30
Matt Simmering786efb82023-01-18 14:09:21 -080031struct I2CDeviceComparator
32{
33 bool operator()(const std::string& a, const std::string& b) const noexcept
34 {
35 return strcasecmp(a.c_str(), b.c_str()) < 0;
36 }
37};
38
Zev Weissdabd48d2022-08-03 15:43:17 -070039using I2CDeviceTypeMap =
Matt Simmering786efb82023-01-18 14:09:21 -080040 boost::container::flat_map<std::string, I2CDeviceType, I2CDeviceComparator>;
Zev Weissdabd48d2022-08-03 15:43:17 -070041
Zev Weiss3ee959a2022-09-21 17:16:28 -070042struct I2CDeviceParams
Zev Weissdabd48d2022-08-03 15:43:17 -070043{
Zev Weiss3ee959a2022-09-21 17:16:28 -070044 I2CDeviceParams(const I2CDeviceType& type, uint64_t bus, uint64_t address) :
Patrick Williams2aaf7172024-08-16 15:20:40 -040045 type(&type), bus(bus), address(address) {};
Zev Weissdabd48d2022-08-03 15:43:17 -070046
47 const I2CDeviceType* type;
48 uint64_t bus;
49 uint64_t address;
50
Ed Tanous201a1012024-04-03 18:07:28 -070051 bool devicePresent() const;
52 bool deviceStatic() const;
Zev Weiss3ee959a2022-09-21 17:16:28 -070053};
54
Patrick Williams2aaf7172024-08-16 15:20:40 -040055std::optional<I2CDeviceParams> getI2CDeviceParams(
56 const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg);
Zev Weiss3ee959a2022-09-21 17:16:28 -070057
58class I2CDevice
59{
60 public:
61 explicit I2CDevice(I2CDeviceParams params);
62 ~I2CDevice();
63
64 private:
65 I2CDeviceParams params;
66
Ed Tanous201a1012024-04-03 18:07:28 -070067 int create() const;
68 int destroy() const;
Zev Weissdabd48d2022-08-03 15:43:17 -070069};
70
Zev Weissdabd48d2022-08-03 15:43:17 -070071// HACK: this declaration "should" live in Utils.hpp, but that leads to a
72// tangle of header-dependency hell because each header needs types declared
73// in the other.
74std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
75 setupPropertiesChangedMatches(
76 sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap,
77 const std::function<void(sdbusplus::message_t&)>& handler);
Matt Simmeringc564eda2023-05-12 13:04:43 -070078
Matt Simmeringcafd72f2022-12-16 15:35:12 -080079// Helper find function because some sensors use underscores in their names
80// while others use spaces. Ignore this discrepancy by changing all spaces to
81// underscores.
82inline size_t sensorNameFind(const std::string& fullName,
83 const std::string& partialName)
84{
85 return boost::replace_all_copy(fullName, " ", "_")
86 .find(boost::replace_all_copy(partialName, " ", "_"));
87}
88
Matt Simmeringc564eda2023-05-12 13:04:43 -070089// returns a {path: <I2CDevice, is_new>} map. is_new indicates if the I2CDevice
90// is newly instantiated by this call (true) or was already there (false).
91template <class T>
92boost::container::flat_map<std::string,
93 std::pair<std::shared_ptr<I2CDevice>, bool>>
94 instantiateDevices(
95 const ManagedObjectType& sensorConfigs,
96 const boost::container::flat_map<std::string, std::shared_ptr<T>>&
97 sensors,
98 const I2CDeviceTypeMap& sensorTypes)
99{
100 boost::container::flat_map<std::string,
101 std::pair<std::shared_ptr<I2CDevice>, bool>>
102 devices;
103 for (const auto& [path, sensor] : sensorConfigs)
104 {
105 for (const auto& [name, cfg] : sensor)
106 {
107 PowerState powerState = getPowerState(cfg);
108 if (!readingStateGood(powerState))
109 {
110 continue;
111 }
112
113 auto findSensorName = cfg.find("Name");
114 if (findSensorName == cfg.end())
115 {
116 continue;
117 }
Matt Simmeringc564eda2023-05-12 13:04:43 -0700118
Matt Simmeringcafd72f2022-12-16 15:35:12 -0800119 const auto* sensorName =
120 std::get_if<std::string>(&findSensorName->second);
121 if (sensorName == nullptr)
122 {
123 std::cerr << "Unable to find sensor name " << name
124 << " on path " << path.str << "\n";
125 continue;
126 }
127
128 std::shared_ptr<T> findSensor(nullptr);
129 for (const auto& sensor : sensors)
130 {
131 if (sensorNameFind(sensor.first, *sensorName) !=
132 std::string::npos)
133 {
134 findSensor = sensor.second;
135 break;
136 }
137 }
138 if (findSensor != nullptr && findSensor->isActive())
Matt Simmeringc564eda2023-05-12 13:04:43 -0700139 {
140 devices.emplace(
141 path.str,
Matt Simmeringcafd72f2022-12-16 15:35:12 -0800142 std::make_pair(findSensor->getI2CDevice(), false));
Matt Simmeringc564eda2023-05-12 13:04:43 -0700143 continue;
144 }
145
146 std::optional<I2CDeviceParams> params =
147 getI2CDeviceParams(sensorTypes, cfg);
148 if (params.has_value() && !params->deviceStatic())
149 {
150 // There exist error cases in which a sensor device that we
151 // need is already instantiated, but needs to be destroyed and
152 // re-created in order to be useful (for example if we crash
153 // after instantiating a device and the sensor device's power
154 // is cut before we get restarted, leaving it "present" but
155 // not really usable). To be on the safe side, instantiate a
156 // temporary device that's immediately destroyed so as to
157 // ensure that we end up with a fresh instance of it.
158 if (params->devicePresent())
159 {
160 std::cerr << "Clearing out previous instance for "
161 << path.str << "\n";
162 I2CDevice tmp(*params);
163 }
164
165 try
166 {
167 devices.emplace(
168 path.str,
169 std::make_pair(std::make_shared<I2CDevice>(*params),
170 true));
171 }
172 catch (std::runtime_error&)
173 {
174 std::cerr << "Failed to instantiate " << params->type->name
175 << " at address " << params->address << " on bus "
176 << params->bus << "\n";
177 }
178 }
179 }
180 }
181 return devices;
182}