xref: /openbmc/dbus-sensors/src/DeviceMgmt.hpp (revision d7be555e)
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 {
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 
25 using I2CDeviceTypeMap =
26     boost::container::flat_map<std::string, I2CDeviceType, I2CDeviceComparator>;
27 
28 struct I2CDeviceParams
29 {
30     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.
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>>
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