xref: /openbmc/dbus-sensors/src/DeviceMgmt.hpp (revision c564eda5)
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(void) const;
38     bool deviceStatic(void) const;
39 };
40 
41 std::optional<I2CDeviceParams>
42     getI2CDeviceParams(const I2CDeviceTypeMap& dtmap,
43                        const SensorBaseConfigMap& cfg);
44 
45 class I2CDevice
46 {
47   public:
48     explicit I2CDevice(I2CDeviceParams params);
49     ~I2CDevice();
50 
51   private:
52     I2CDeviceParams params;
53 
54     int create(void) const;
55     int destroy(void) const;
56 };
57 
58 // 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.
61 std::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);
65 
66 // returns a {path: <I2CDevice, is_new>} map.  is_new indicates if the I2CDevice
67 // is newly instantiated by this call (true) or was already there (false).
68 template <class T>
69 boost::container::flat_map<std::string,
70                            std::pair<std::shared_ptr<I2CDevice>, bool>>
71     instantiateDevices(
72         const ManagedObjectType& sensorConfigs,
73         const boost::container::flat_map<std::string, std::shared_ptr<T>>&
74             sensors,
75         const I2CDeviceTypeMap& sensorTypes)
76 {
77     boost::container::flat_map<std::string,
78                                std::pair<std::shared_ptr<I2CDevice>, bool>>
79         devices;
80     for (const auto& [path, sensor] : sensorConfigs)
81     {
82         for (const auto& [name, cfg] : sensor)
83         {
84             PowerState powerState = getPowerState(cfg);
85             if (!readingStateGood(powerState))
86             {
87                 continue;
88             }
89 
90             auto findSensorName = cfg.find("Name");
91             if (findSensorName == cfg.end())
92             {
93                 continue;
94             }
95             std::string sensorName =
96                 std::get<std::string>(findSensorName->second);
97 
98             auto findSensor = sensors.find(sensorName);
99             if (findSensor != sensors.end() && findSensor->second != nullptr &&
100                 findSensor->second->isActive())
101             {
102                 devices.emplace(
103                     path.str,
104                     std::make_pair(findSensor->second->getI2CDevice(), false));
105                 continue;
106             }
107 
108             std::optional<I2CDeviceParams> params =
109                 getI2CDeviceParams(sensorTypes, cfg);
110             if (params.has_value() && !params->deviceStatic())
111             {
112                 // There exist error cases in which a sensor device that we
113                 // need is already instantiated, but needs to be destroyed and
114                 // re-created in order to be useful (for example if we crash
115                 // after instantiating a device and the sensor device's power
116                 // is cut before we get restarted, leaving it "present" but
117                 // not really usable).  To be on the safe side, instantiate a
118                 // temporary device that's immediately destroyed so as to
119                 // ensure that we end up with a fresh instance of it.
120                 if (params->devicePresent())
121                 {
122                     std::cerr << "Clearing out previous instance for "
123                               << path.str << "\n";
124                     I2CDevice tmp(*params);
125                 }
126 
127                 try
128                 {
129                     devices.emplace(
130                         path.str,
131                         std::make_pair(std::make_shared<I2CDevice>(*params),
132                                        true));
133                 }
134                 catch (std::runtime_error&)
135                 {
136                     std::cerr << "Failed to instantiate " << params->type->name
137                               << " at address " << params->address << " on bus "
138                               << params->bus << "\n";
139                 }
140             }
141         }
142     }
143     return devices;
144 }
145