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