xref: /openbmc/dbus-sensors/src/DeviceMgmt.hpp (revision 18b6186e531ae37dd22b634c6530f793528473f4)
1 #pragma once
2 
3 #include "Utils.hpp"
4 
5 #include <strings.h>
6 
7 #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>
15 #include <functional>
16 #include <iostream>
17 #include <memory>
18 #include <optional>
19 #include <stdexcept>
20 #include <string>
21 #include <utility>
22 #include <variant>
23 #include <vector>
24 
25 struct I2CDeviceType
26 {
27     const char* name;
28     bool createsHWMon;
29 };
30 
31 struct I2CDeviceComparator
32 {
operator ()I2CDeviceComparator33     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 
39 using I2CDeviceTypeMap =
40     boost::container::flat_map<std::string, I2CDeviceType, I2CDeviceComparator>;
41 
42 struct I2CDeviceParams
43 {
I2CDeviceParamsI2CDeviceParams44     I2CDeviceParams(const I2CDeviceType& type, uint64_t bus, uint64_t address) :
45         type(&type), bus(bus), address(address) {};
46 
47     const I2CDeviceType* type;
48     uint64_t bus;
49     uint64_t address;
50 
51     bool devicePresent() const;
52     bool deviceStatic() const;
53 };
54 
55 std::optional<I2CDeviceParams> getI2CDeviceParams(
56     const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg);
57 
58 class I2CDevice
59 {
60   public:
61     explicit I2CDevice(I2CDeviceParams params);
62     ~I2CDevice();
63 
64   private:
65     I2CDeviceParams params;
66 
67     int create() const;
68     int destroy() const;
69 };
70 
71 // 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.
74 std::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);
78 
79 // 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.
sensorNameFind(const std::string & fullName,const std::string & partialName)82 inline 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 
89 // 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).
91 template <class T>
92 boost::container::flat_map<std::string,
93                            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)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             }
118 
119             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())
139             {
140                 devices.emplace(
141                     path.str,
142                     std::make_pair(findSensor->getI2CDevice(), false));
143                 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 }
183