xref: /openbmc/dbus-sensors/src/DeviceMgmt.hpp (revision 18b6186e531ae37dd22b634c6530f793528473f4)
1e73bd0a1SAndrew Jeffery #pragma once
2e73bd0a1SAndrew Jeffery 
3e73bd0a1SAndrew Jeffery #include "Utils.hpp"
4e73bd0a1SAndrew Jeffery 
5*18b6186eSEd Tanous #include <strings.h>
6e73bd0a1SAndrew Jeffery 
7*18b6186eSEd Tanous #include <boost/algorithm/string/replace.hpp>
8*18b6186eSEd Tanous #include <boost/container/flat_map.hpp>
9*18b6186eSEd Tanous #include <sdbusplus/asio/connection.hpp>
10*18b6186eSEd Tanous #include <sdbusplus/bus/match.hpp>
11*18b6186eSEd Tanous #include <sdbusplus/message.hpp>
12*18b6186eSEd Tanous 
13*18b6186eSEd Tanous #include <cstddef>
14*18b6186eSEd Tanous #include <cstdint>
15e73bd0a1SAndrew Jeffery #include <functional>
16*18b6186eSEd Tanous #include <iostream>
17*18b6186eSEd Tanous #include <memory>
18e73bd0a1SAndrew Jeffery #include <optional>
19*18b6186eSEd Tanous #include <stdexcept>
20*18b6186eSEd Tanous #include <string>
21*18b6186eSEd Tanous #include <utility>
22*18b6186eSEd Tanous #include <variant>
23*18b6186eSEd Tanous #include <vector>
24e73bd0a1SAndrew Jeffery 
25e73bd0a1SAndrew Jeffery struct I2CDeviceType
26e73bd0a1SAndrew Jeffery {
27e73bd0a1SAndrew Jeffery     const char* name;
28e73bd0a1SAndrew Jeffery     bool createsHWMon;
29e73bd0a1SAndrew Jeffery };
30e73bd0a1SAndrew Jeffery 
31786efb80SMatt Simmering struct I2CDeviceComparator
32786efb80SMatt Simmering {
operator ()I2CDeviceComparator33786efb80SMatt Simmering     bool operator()(const std::string& a, const std::string& b) const noexcept
34786efb80SMatt Simmering     {
35786efb80SMatt Simmering         return strcasecmp(a.c_str(), b.c_str()) < 0;
36786efb80SMatt Simmering     }
37786efb80SMatt Simmering };
38786efb80SMatt Simmering 
39e73bd0a1SAndrew Jeffery using I2CDeviceTypeMap =
40786efb80SMatt Simmering     boost::container::flat_map<std::string, I2CDeviceType, I2CDeviceComparator>;
41e73bd0a1SAndrew Jeffery 
42e73bd0a1SAndrew Jeffery struct I2CDeviceParams
43e73bd0a1SAndrew Jeffery {
I2CDeviceParamsI2CDeviceParams44e73bd0a1SAndrew Jeffery     I2CDeviceParams(const I2CDeviceType& type, uint64_t bus, uint64_t address) :
45e73bd0a1SAndrew Jeffery         type(&type), bus(bus), address(address) {};
46e73bd0a1SAndrew Jeffery 
47e73bd0a1SAndrew Jeffery     const I2CDeviceType* type;
48e73bd0a1SAndrew Jeffery     uint64_t bus;
49e73bd0a1SAndrew Jeffery     uint64_t address;
50e73bd0a1SAndrew Jeffery 
51201a1015SEd Tanous     bool devicePresent() const;
52201a1015SEd Tanous     bool deviceStatic() const;
53e73bd0a1SAndrew Jeffery };
54e73bd0a1SAndrew Jeffery 
552aaf7175SPatrick Williams std::optional<I2CDeviceParams> getI2CDeviceParams(
562aaf7175SPatrick Williams     const I2CDeviceTypeMap& dtmap, const SensorBaseConfigMap& cfg);
57e73bd0a1SAndrew Jeffery 
58e73bd0a1SAndrew Jeffery class I2CDevice
59e73bd0a1SAndrew Jeffery {
60e73bd0a1SAndrew Jeffery   public:
61e73bd0a1SAndrew Jeffery     explicit I2CDevice(I2CDeviceParams params);
62e73bd0a1SAndrew Jeffery     ~I2CDevice();
63e73bd0a1SAndrew Jeffery 
64e73bd0a1SAndrew Jeffery   private:
65e73bd0a1SAndrew Jeffery     I2CDeviceParams params;
66e73bd0a1SAndrew Jeffery 
67201a1015SEd Tanous     int create() const;
68201a1015SEd Tanous     int destroy() const;
69e73bd0a1SAndrew Jeffery };
70e73bd0a1SAndrew Jeffery 
71e73bd0a1SAndrew Jeffery // HACK: this declaration "should" live in Utils.hpp, but that leads to a
72e73bd0a1SAndrew Jeffery // tangle of header-dependency hell because each header needs types declared
73e73bd0a1SAndrew Jeffery // in the other.
74e73bd0a1SAndrew Jeffery std::vector<std::unique_ptr<sdbusplus::bus::match_t>>
75e73bd0a1SAndrew Jeffery     setupPropertiesChangedMatches(
76e73bd0a1SAndrew Jeffery         sdbusplus::asio::connection& bus, const I2CDeviceTypeMap& typeMap,
77e73bd0a1SAndrew Jeffery         const std::function<void(sdbusplus::message_t&)>& handler);
78c564eda5SMatt Simmering 
79cafd72f6SMatt Simmering // Helper find function because some sensors use underscores in their names
80cafd72f6SMatt Simmering // while others use spaces. Ignore this discrepancy by changing all spaces to
81cafd72f6SMatt Simmering // underscores.
sensorNameFind(const std::string & fullName,const std::string & partialName)82cafd72f6SMatt Simmering inline size_t sensorNameFind(const std::string& fullName,
83cafd72f6SMatt Simmering                              const std::string& partialName)
84cafd72f6SMatt Simmering {
85cafd72f6SMatt Simmering     return boost::replace_all_copy(fullName, " ", "_")
86cafd72f6SMatt Simmering         .find(boost::replace_all_copy(partialName, " ", "_"));
87cafd72f6SMatt Simmering }
88cafd72f6SMatt Simmering 
89c564eda5SMatt Simmering // returns a {path: <I2CDevice, is_new>} map.  is_new indicates if the I2CDevice
90c564eda5SMatt Simmering // is newly instantiated by this call (true) or was already there (false).
91c564eda5SMatt Simmering template <class T>
92c564eda5SMatt Simmering boost::container::flat_map<std::string,
93c564eda5SMatt Simmering                            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)94c564eda5SMatt Simmering     instantiateDevices(
95c564eda5SMatt Simmering         const ManagedObjectType& sensorConfigs,
96c564eda5SMatt Simmering         const boost::container::flat_map<std::string, std::shared_ptr<T>>&
97c564eda5SMatt Simmering             sensors,
98c564eda5SMatt Simmering         const I2CDeviceTypeMap& sensorTypes)
99c564eda5SMatt Simmering {
100c564eda5SMatt Simmering     boost::container::flat_map<std::string,
101c564eda5SMatt Simmering                                std::pair<std::shared_ptr<I2CDevice>, bool>>
102c564eda5SMatt Simmering         devices;
103c564eda5SMatt Simmering     for (const auto& [path, sensor] : sensorConfigs)
104c564eda5SMatt Simmering     {
105c564eda5SMatt Simmering         for (const auto& [name, cfg] : sensor)
106c564eda5SMatt Simmering         {
107c564eda5SMatt Simmering             PowerState powerState = getPowerState(cfg);
108c564eda5SMatt Simmering             if (!readingStateGood(powerState))
109c564eda5SMatt Simmering             {
110c564eda5SMatt Simmering                 continue;
111c564eda5SMatt Simmering             }
112c564eda5SMatt Simmering 
113c564eda5SMatt Simmering             auto findSensorName = cfg.find("Name");
114c564eda5SMatt Simmering             if (findSensorName == cfg.end())
115c564eda5SMatt Simmering             {
116c564eda5SMatt Simmering                 continue;
117c564eda5SMatt Simmering             }
118c564eda5SMatt Simmering 
119cafd72f6SMatt Simmering             const auto* sensorName =
120cafd72f6SMatt Simmering                 std::get_if<std::string>(&findSensorName->second);
121cafd72f6SMatt Simmering             if (sensorName == nullptr)
122cafd72f6SMatt Simmering             {
123cafd72f6SMatt Simmering                 std::cerr << "Unable to find sensor name " << name
124cafd72f6SMatt Simmering                           << " on path " << path.str << "\n";
125cafd72f6SMatt Simmering                 continue;
126cafd72f6SMatt Simmering             }
127cafd72f6SMatt Simmering 
128cafd72f6SMatt Simmering             std::shared_ptr<T> findSensor(nullptr);
129cafd72f6SMatt Simmering             for (const auto& sensor : sensors)
130cafd72f6SMatt Simmering             {
131cafd72f6SMatt Simmering                 if (sensorNameFind(sensor.first, *sensorName) !=
132cafd72f6SMatt Simmering                     std::string::npos)
133cafd72f6SMatt Simmering                 {
134cafd72f6SMatt Simmering                     findSensor = sensor.second;
135cafd72f6SMatt Simmering                     break;
136cafd72f6SMatt Simmering                 }
137cafd72f6SMatt Simmering             }
138cafd72f6SMatt Simmering             if (findSensor != nullptr && findSensor->isActive())
139c564eda5SMatt Simmering             {
140c564eda5SMatt Simmering                 devices.emplace(
141c564eda5SMatt Simmering                     path.str,
142cafd72f6SMatt Simmering                     std::make_pair(findSensor->getI2CDevice(), false));
143c564eda5SMatt Simmering                 continue;
144c564eda5SMatt Simmering             }
145c564eda5SMatt Simmering 
146c564eda5SMatt Simmering             std::optional<I2CDeviceParams> params =
147c564eda5SMatt Simmering                 getI2CDeviceParams(sensorTypes, cfg);
148c564eda5SMatt Simmering             if (params.has_value() && !params->deviceStatic())
149c564eda5SMatt Simmering             {
150c564eda5SMatt Simmering                 // There exist error cases in which a sensor device that we
151c564eda5SMatt Simmering                 // need is already instantiated, but needs to be destroyed and
152c564eda5SMatt Simmering                 // re-created in order to be useful (for example if we crash
153c564eda5SMatt Simmering                 // after instantiating a device and the sensor device's power
154c564eda5SMatt Simmering                 // is cut before we get restarted, leaving it "present" but
155c564eda5SMatt Simmering                 // not really usable).  To be on the safe side, instantiate a
156c564eda5SMatt Simmering                 // temporary device that's immediately destroyed so as to
157c564eda5SMatt Simmering                 // ensure that we end up with a fresh instance of it.
158c564eda5SMatt Simmering                 if (params->devicePresent())
159c564eda5SMatt Simmering                 {
160c564eda5SMatt Simmering                     std::cerr << "Clearing out previous instance for "
161c564eda5SMatt Simmering                               << path.str << "\n";
162c564eda5SMatt Simmering                     I2CDevice tmp(*params);
163c564eda5SMatt Simmering                 }
164c564eda5SMatt Simmering 
165c564eda5SMatt Simmering                 try
166c564eda5SMatt Simmering                 {
167c564eda5SMatt Simmering                     devices.emplace(
168c564eda5SMatt Simmering                         path.str,
169c564eda5SMatt Simmering                         std::make_pair(std::make_shared<I2CDevice>(*params),
170c564eda5SMatt Simmering                                        true));
171c564eda5SMatt Simmering                 }
172c564eda5SMatt Simmering                 catch (std::runtime_error&)
173c564eda5SMatt Simmering                 {
174c564eda5SMatt Simmering                     std::cerr << "Failed to instantiate " << params->type->name
175c564eda5SMatt Simmering                               << " at address " << params->address << " on bus "
176c564eda5SMatt Simmering                               << params->bus << "\n";
177c564eda5SMatt Simmering                 }
178c564eda5SMatt Simmering             }
179c564eda5SMatt Simmering         }
180c564eda5SMatt Simmering     }
181c564eda5SMatt Simmering     return devices;
182c564eda5SMatt Simmering }
183