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