16714a25aSJames Feist /*
26714a25aSJames Feist // Copyright (c) 2017 Intel Corporation
36714a25aSJames Feist //
46714a25aSJames Feist // Licensed under the Apache License, Version 2.0 (the "License");
56714a25aSJames Feist // you may not use this file except in compliance with the License.
66714a25aSJames Feist // You may obtain a copy of the License at
76714a25aSJames Feist //
86714a25aSJames Feist //      http://www.apache.org/licenses/LICENSE-2.0
96714a25aSJames Feist //
106714a25aSJames Feist // Unless required by applicable law or agreed to in writing, software
116714a25aSJames Feist // distributed under the License is distributed on an "AS IS" BASIS,
126714a25aSJames Feist // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136714a25aSJames Feist // See the License for the specific language governing permissions and
146714a25aSJames Feist // limitations under the License.
156714a25aSJames Feist */
166714a25aSJames Feist 
17e73bd0a1SAndrew Jeffery #include "DeviceMgmt.hpp"
18e73bd0a1SAndrew Jeffery #include "HwmonTempSensor.hpp"
19e73bd0a1SAndrew Jeffery #include "Utils.hpp"
20e73bd0a1SAndrew Jeffery 
216714a25aSJames Feist #include <boost/algorithm/string/replace.hpp>
2296e97db7SPatrick Venture #include <boost/container/flat_map.hpp>
236714a25aSJames Feist #include <boost/container/flat_set.hpp>
2438fb5983SJames Feist #include <sdbusplus/asio/connection.hpp>
2538fb5983SJames Feist #include <sdbusplus/asio/object_server.hpp>
2638fb5983SJames Feist #include <sdbusplus/bus/match.hpp>
2738fb5983SJames Feist 
2838fb5983SJames Feist #include <array>
297dd6443bSJae Hyun Yoo #include <charconv>
3024f02f24SJames Feist #include <filesystem>
316714a25aSJames Feist #include <fstream>
3296e97db7SPatrick Venture #include <functional>
3396e97db7SPatrick Venture #include <memory>
346714a25aSJames Feist #include <regex>
3596e97db7SPatrick Venture #include <stdexcept>
3696e97db7SPatrick Venture #include <string>
3796e97db7SPatrick Venture #include <utility>
3896e97db7SPatrick Venture #include <variant>
3996e97db7SPatrick Venture #include <vector>
406714a25aSJames Feist 
4187bc67f7SJeff Lin static constexpr float pollRateDefault = 0.5;
426714a25aSJames Feist 
43544e7dc5SBruce Mitchell static constexpr double maxValuePressure = 120000; // Pascals
44544e7dc5SBruce Mitchell static constexpr double minValuePressure = 30000;  // Pascals
45544e7dc5SBruce Mitchell 
463ec41c53SBruce Mitchell static constexpr double maxValueRelativeHumidity = 100; // PercentRH
473ec41c53SBruce Mitchell static constexpr double minValueRelativeHumidity = 0;   // PercentRH
483ec41c53SBruce Mitchell 
49544e7dc5SBruce Mitchell static constexpr double maxValueTemperature = 127;  // DegreesC
50544e7dc5SBruce Mitchell static constexpr double minValueTemperature = -128; // DegreesC
51544e7dc5SBruce Mitchell 
52cf3bce6eSJames Feist namespace fs = std::filesystem;
53d29f8aa0SZev Weiss 
54d29f8aa0SZev Weiss static const I2CDeviceTypeMap sensorTypes{
55d29f8aa0SZev Weiss     {"DPS310", I2CDeviceType{"dps310", false}},
56d29f8aa0SZev Weiss     {"EMC1412", I2CDeviceType{"emc1412", true}},
57d29f8aa0SZev Weiss     {"EMC1413", I2CDeviceType{"emc1413", true}},
58d29f8aa0SZev Weiss     {"EMC1414", I2CDeviceType{"emc1414", true}},
59d29f8aa0SZev Weiss     {"HDC1080", I2CDeviceType{"hdc1080", false}},
60d29f8aa0SZev Weiss     {"JC42", I2CDeviceType{"jc42", true}},
61d29f8aa0SZev Weiss     {"LM75A", I2CDeviceType{"lm75a", true}},
62d29f8aa0SZev Weiss     {"LM95234", I2CDeviceType{"lm95234", true}},
63d29f8aa0SZev Weiss     {"MAX31725", I2CDeviceType{"max31725", true}},
64d29f8aa0SZev Weiss     {"MAX31730", I2CDeviceType{"max31730", true}},
65d29f8aa0SZev Weiss     {"MAX6581", I2CDeviceType{"max6581", true}},
66d29f8aa0SZev Weiss     {"MAX6654", I2CDeviceType{"max6654", true}},
67d29f8aa0SZev Weiss     {"NCT6779", I2CDeviceType{"nct6779", true}},
68d29f8aa0SZev Weiss     {"NCT7802", I2CDeviceType{"nct7802", true}},
69d29f8aa0SZev Weiss     {"SBTSI", I2CDeviceType{"sbtsi", true}},
70d29f8aa0SZev Weiss     {"SI7020", I2CDeviceType{"si7020", false}},
719b0a6f67STim Lee     {"TMP100", I2CDeviceType{"tmp100", true}},
72d29f8aa0SZev Weiss     {"TMP112", I2CDeviceType{"tmp112", true}},
73d29f8aa0SZev Weiss     {"TMP175", I2CDeviceType{"tmp175", true}},
74d29f8aa0SZev Weiss     {"TMP421", I2CDeviceType{"tmp421", true}},
75d29f8aa0SZev Weiss     {"TMP441", I2CDeviceType{"tmp441", true}},
76d29f8aa0SZev Weiss     {"TMP75", I2CDeviceType{"tmp75", true}},
77d29f8aa0SZev Weiss     {"W83773G", I2CDeviceType{"w83773g", true}},
78d29f8aa0SZev Weiss };
796714a25aSJames Feist 
80544e7dc5SBruce Mitchell static struct SensorParams
81544e7dc5SBruce Mitchell     getSensorParameters(const std::filesystem::path& path)
82544e7dc5SBruce Mitchell {
83544e7dc5SBruce Mitchell     // offset is to default to 0 and scale to 1, see lore
84544e7dc5SBruce Mitchell     // https://lore.kernel.org/linux-iio/5c79425f-6e88-36b6-cdfe-4080738d039f@metafoo.de/
85544e7dc5SBruce Mitchell     struct SensorParams tmpSensorParameters = {.minValue = minValueTemperature,
86544e7dc5SBruce Mitchell                                                .maxValue = maxValueTemperature,
87544e7dc5SBruce Mitchell                                                .offsetValue = 0.0,
88544e7dc5SBruce Mitchell                                                .scaleValue = 1.0,
895a86e562SBruce Mitchell                                                .units =
905a86e562SBruce Mitchell                                                    sensor_paths::unitDegreesC,
91544e7dc5SBruce Mitchell                                                .typeName = "temperature"};
92544e7dc5SBruce Mitchell 
93544e7dc5SBruce Mitchell     // For IIO RAW sensors we get a raw_value, an offset, and scale
94544e7dc5SBruce Mitchell     // to compute the value = (raw_value + offset) * scale
95544e7dc5SBruce Mitchell     // with a _raw IIO device we need to get the
96544e7dc5SBruce Mitchell     // offsetValue and scaleValue from the driver
97544e7dc5SBruce Mitchell     // these are used to compute the reading in
98544e7dc5SBruce Mitchell     // units that have yet to be scaled for D-Bus.
99544e7dc5SBruce Mitchell     const std::string pathStr = path.string();
100544e7dc5SBruce Mitchell     if (pathStr.ends_with("_raw"))
101544e7dc5SBruce Mitchell     {
102544e7dc5SBruce Mitchell         std::string pathOffsetStr =
103544e7dc5SBruce Mitchell             pathStr.substr(0, pathStr.size() - 4) + "_offset";
104544e7dc5SBruce Mitchell         std::optional<double> tmpOffsetValue = readFile(pathOffsetStr, 1.0);
105544e7dc5SBruce Mitchell         // In case there is nothing to read skip this device
106544e7dc5SBruce Mitchell         // This is not an error condition see lore
107544e7dc5SBruce Mitchell         // https://lore.kernel.org/linux-iio/5c79425f-6e88-36b6-cdfe-4080738d039f@metafoo.de/
108544e7dc5SBruce Mitchell         if (tmpOffsetValue)
109544e7dc5SBruce Mitchell         {
110544e7dc5SBruce Mitchell             tmpSensorParameters.offsetValue = *tmpOffsetValue;
111544e7dc5SBruce Mitchell         }
112544e7dc5SBruce Mitchell 
113544e7dc5SBruce Mitchell         std::string pathScaleStr =
114544e7dc5SBruce Mitchell             pathStr.substr(0, pathStr.size() - 4) + "_scale";
115544e7dc5SBruce Mitchell         std::optional<double> tmpScaleValue = readFile(pathScaleStr, 1.0);
116544e7dc5SBruce Mitchell         // In case there is nothing to read skip this device
117544e7dc5SBruce Mitchell         // This is not an error condition see lore
118544e7dc5SBruce Mitchell         // https://lore.kernel.org/linux-iio/5c79425f-6e88-36b6-cdfe-4080738d039f@metafoo.de/
119544e7dc5SBruce Mitchell         if (tmpScaleValue)
120544e7dc5SBruce Mitchell         {
121544e7dc5SBruce Mitchell             tmpSensorParameters.scaleValue = *tmpScaleValue;
122544e7dc5SBruce Mitchell         }
123544e7dc5SBruce Mitchell     }
124544e7dc5SBruce Mitchell 
125544e7dc5SBruce Mitchell     // Temperatures are read in milli degrees Celsius, we need
126544e7dc5SBruce Mitchell     // degrees Celsius. Pressures are read in kilopascal, we need
127544e7dc5SBruce Mitchell     // Pascals.  On D-Bus for Open BMC we use the International
128544e7dc5SBruce Mitchell     // System of Units without prefixes. Links to the kernel
129544e7dc5SBruce Mitchell     // documentation:
130544e7dc5SBruce Mitchell     // https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
131544e7dc5SBruce Mitchell     // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-iio
132544e7dc5SBruce Mitchell     if (path.filename() == "in_pressure_input" ||
133544e7dc5SBruce Mitchell         path.filename() == "in_pressure_raw")
134544e7dc5SBruce Mitchell     {
135544e7dc5SBruce Mitchell         tmpSensorParameters.minValue = minValuePressure;
136544e7dc5SBruce Mitchell         tmpSensorParameters.maxValue = maxValuePressure;
137544e7dc5SBruce Mitchell         // Pressures are read in kilopascal, we need Pascals.
138544e7dc5SBruce Mitchell         tmpSensorParameters.scaleValue *= 1000.0;
139544e7dc5SBruce Mitchell         tmpSensorParameters.typeName = "pressure";
1405a86e562SBruce Mitchell         tmpSensorParameters.units = sensor_paths::unitPascals;
141544e7dc5SBruce Mitchell     }
1423ec41c53SBruce Mitchell     else if (path.filename() == "in_humidityrelative_input" ||
1433ec41c53SBruce Mitchell              path.filename() == "in_humidityrelative_raw")
1443ec41c53SBruce Mitchell     {
1453ec41c53SBruce Mitchell         tmpSensorParameters.minValue = minValueRelativeHumidity;
1463ec41c53SBruce Mitchell         tmpSensorParameters.maxValue = maxValueRelativeHumidity;
1473ec41c53SBruce Mitchell         // Relative Humidity are read in milli-percent, we need percent.
1483ec41c53SBruce Mitchell         tmpSensorParameters.scaleValue *= 0.001;
1493ec41c53SBruce Mitchell         tmpSensorParameters.typeName = "humidity";
1503ec41c53SBruce Mitchell         tmpSensorParameters.units = "PercentRH";
1513ec41c53SBruce Mitchell     }
152544e7dc5SBruce Mitchell     else
153544e7dc5SBruce Mitchell     {
154544e7dc5SBruce Mitchell         // Temperatures are read in milli degrees Celsius,
155544e7dc5SBruce Mitchell         // we need degrees Celsius.
156544e7dc5SBruce Mitchell         tmpSensorParameters.scaleValue *= 0.001;
157544e7dc5SBruce Mitchell     }
158544e7dc5SBruce Mitchell 
159544e7dc5SBruce Mitchell     return tmpSensorParameters;
160544e7dc5SBruce Mitchell }
161544e7dc5SBruce Mitchell 
1629f3a74edSJayashree Dhanapal struct SensorConfigKey
1639f3a74edSJayashree Dhanapal {
1649f3a74edSJayashree Dhanapal     uint64_t bus;
1659f3a74edSJayashree Dhanapal     uint64_t addr;
1669f3a74edSJayashree Dhanapal     bool operator<(const SensorConfigKey& other) const
1679f3a74edSJayashree Dhanapal     {
1689f3a74edSJayashree Dhanapal         if (bus != other.bus)
1699f3a74edSJayashree Dhanapal         {
1709f3a74edSJayashree Dhanapal             return bus < other.bus;
1719f3a74edSJayashree Dhanapal         }
1729f3a74edSJayashree Dhanapal         return addr < other.addr;
1739f3a74edSJayashree Dhanapal     }
1749f3a74edSJayashree Dhanapal };
1759f3a74edSJayashree Dhanapal 
1769f3a74edSJayashree Dhanapal struct SensorConfig
1779f3a74edSJayashree Dhanapal {
1789f3a74edSJayashree Dhanapal     std::string sensorPath;
1799f3a74edSJayashree Dhanapal     SensorData sensorData;
1809f3a74edSJayashree Dhanapal     std::string interface;
1819f3a74edSJayashree Dhanapal     SensorBaseConfigMap config;
1829f3a74edSJayashree Dhanapal     std::vector<std::string> name;
1839f3a74edSJayashree Dhanapal };
1849f3a74edSJayashree Dhanapal 
1859f3a74edSJayashree Dhanapal using SensorConfigMap =
1869f3a74edSJayashree Dhanapal     boost::container::flat_map<SensorConfigKey, SensorConfig>;
1879f3a74edSJayashree Dhanapal 
1889f3a74edSJayashree Dhanapal static SensorConfigMap
1899f3a74edSJayashree Dhanapal     buildSensorConfigMap(const ManagedObjectType& sensorConfigs)
1909f3a74edSJayashree Dhanapal {
1919f3a74edSJayashree Dhanapal     SensorConfigMap configMap;
192a1808330SZev Weiss     for (const auto& [path, cfgData] : sensorConfigs)
1939f3a74edSJayashree Dhanapal     {
194a1808330SZev Weiss         for (const auto& [intf, cfg] : cfgData)
1959f3a74edSJayashree Dhanapal         {
1969f3a74edSJayashree Dhanapal             auto busCfg = cfg.find("Bus");
1979f3a74edSJayashree Dhanapal             auto addrCfg = cfg.find("Address");
1989f3a74edSJayashree Dhanapal             if ((busCfg == cfg.end()) || (addrCfg == cfg.end()))
1999f3a74edSJayashree Dhanapal             {
2009f3a74edSJayashree Dhanapal                 continue;
2019f3a74edSJayashree Dhanapal             }
2029f3a74edSJayashree Dhanapal 
2032049bd26SEd Tanous             if ((std::get_if<uint64_t>(&busCfg->second) == nullptr) ||
2042049bd26SEd Tanous                 (std::get_if<uint64_t>(&addrCfg->second) == nullptr))
2059f3a74edSJayashree Dhanapal             {
206a1808330SZev Weiss                 std::cerr << path.str << " Bus or Address invalid\n";
2079f3a74edSJayashree Dhanapal                 continue;
2089f3a74edSJayashree Dhanapal             }
2099f3a74edSJayashree Dhanapal 
2109f3a74edSJayashree Dhanapal             std::vector<std::string> hwmonNames;
2119f3a74edSJayashree Dhanapal             auto nameCfg = cfg.find("Name");
2129f3a74edSJayashree Dhanapal             if (nameCfg != cfg.end())
2139f3a74edSJayashree Dhanapal             {
2149f3a74edSJayashree Dhanapal                 hwmonNames.push_back(std::get<std::string>(nameCfg->second));
2159f3a74edSJayashree Dhanapal                 size_t i = 1;
2169f3a74edSJayashree Dhanapal                 while (true)
2179f3a74edSJayashree Dhanapal                 {
2189f3a74edSJayashree Dhanapal                     auto sensorNameCfg = cfg.find("Name" + std::to_string(i));
2199f3a74edSJayashree Dhanapal                     if (sensorNameCfg == cfg.end())
2209f3a74edSJayashree Dhanapal                     {
2219f3a74edSJayashree Dhanapal                         break;
2229f3a74edSJayashree Dhanapal                     }
2239f3a74edSJayashree Dhanapal                     hwmonNames.push_back(
2249f3a74edSJayashree Dhanapal                         std::get<std::string>(sensorNameCfg->second));
2259f3a74edSJayashree Dhanapal                     i++;
2269f3a74edSJayashree Dhanapal                 }
2279f3a74edSJayashree Dhanapal             }
2289f3a74edSJayashree Dhanapal 
2299f3a74edSJayashree Dhanapal             SensorConfigKey key = {std::get<uint64_t>(busCfg->second),
2309f3a74edSJayashree Dhanapal                                    std::get<uint64_t>(addrCfg->second)};
231a1808330SZev Weiss             SensorConfig val = {path.str, cfgData, intf, cfg, hwmonNames};
2329f3a74edSJayashree Dhanapal 
2339f3a74edSJayashree Dhanapal             auto [it, inserted] = configMap.emplace(key, std::move(val));
2349f3a74edSJayashree Dhanapal             if (!inserted)
2359f3a74edSJayashree Dhanapal             {
236a1808330SZev Weiss                 std::cerr << path.str << ": ignoring duplicate entry for {"
237a1808330SZev Weiss                           << key.bus << ", 0x" << std::hex << key.addr
238a1808330SZev Weiss                           << std::dec << "}\n";
2399f3a74edSJayashree Dhanapal             }
2409f3a74edSJayashree Dhanapal         }
2419f3a74edSJayashree Dhanapal     }
2429f3a74edSJayashree Dhanapal     return configMap;
2439f3a74edSJayashree Dhanapal }
2449f3a74edSJayashree Dhanapal 
2457627c860SZev Weiss // returns a {path: <I2CDevice, is_new>} map.  is_new indicates if the I2CDevice
2467627c860SZev Weiss // is newly instantiated by this call (true) or was already there (false).
2477627c860SZev Weiss boost::container::flat_map<std::string,
2487627c860SZev Weiss                            std::pair<std::shared_ptr<I2CDevice>, bool>>
249a1456c4aSZev Weiss     instantiateDevices(
250a1456c4aSZev Weiss         const ManagedObjectType& sensorConfigs,
251a1456c4aSZev Weiss         const boost::container::flat_map<
252a1456c4aSZev Weiss             std::string, std::shared_ptr<HwmonTempSensor>>& sensors)
253a1456c4aSZev Weiss {
2547627c860SZev Weiss     boost::container::flat_map<std::string,
2557627c860SZev Weiss                                std::pair<std::shared_ptr<I2CDevice>, bool>>
2567627c860SZev Weiss         devices;
257a1456c4aSZev Weiss     for (const auto& [path, sensor] : sensorConfigs)
258a1456c4aSZev Weiss     {
259a1456c4aSZev Weiss         for (const auto& [name, cfg] : sensor)
260a1456c4aSZev Weiss         {
261a1456c4aSZev Weiss             PowerState powerState = getPowerState(cfg);
262a1456c4aSZev Weiss             if (!readingStateGood(powerState))
263a1456c4aSZev Weiss             {
264a1456c4aSZev Weiss                 continue;
265a1456c4aSZev Weiss             }
266a1456c4aSZev Weiss 
267a1456c4aSZev Weiss             auto findSensorName = cfg.find("Name");
268a1456c4aSZev Weiss             if (findSensorName == cfg.end())
269a1456c4aSZev Weiss             {
270a1456c4aSZev Weiss                 continue;
271a1456c4aSZev Weiss             }
272a1456c4aSZev Weiss             std::string sensorName =
273a1456c4aSZev Weiss                 std::get<std::string>(findSensorName->second);
274a1456c4aSZev Weiss 
275a1456c4aSZev Weiss             auto findSensor = sensors.find(sensorName);
276a1456c4aSZev Weiss             if (findSensor != sensors.end() && findSensor->second != nullptr &&
277a1456c4aSZev Weiss                 findSensor->second->isActive())
278a1456c4aSZev Weiss             {
2797627c860SZev Weiss                 devices.emplace(
2807627c860SZev Weiss                     path.str,
2817627c860SZev Weiss                     std::make_pair(findSensor->second->getI2CDevice(), false));
282a1456c4aSZev Weiss                 continue;
283a1456c4aSZev Weiss             }
284a1456c4aSZev Weiss 
285a1456c4aSZev Weiss             std::optional<I2CDeviceParams> params =
286a1456c4aSZev Weiss                 getI2CDeviceParams(sensorTypes, cfg);
287a1456c4aSZev Weiss             if (params.has_value() && !params->deviceStatic())
288a1456c4aSZev Weiss             {
289a1456c4aSZev Weiss                 // There exist error cases in which a sensor device that we
290a1456c4aSZev Weiss                 // need is already instantiated, but needs to be destroyed and
291a1456c4aSZev Weiss                 // re-created in order to be useful (for example if we crash
292a1456c4aSZev Weiss                 // after instantiating a device and the sensor device's power
293a1456c4aSZev Weiss                 // is cut before we get restarted, leaving it "present" but
294a1456c4aSZev Weiss                 // not really usable).  To be on the safe side, instantiate a
295a1456c4aSZev Weiss                 // temporary device that's immediately destroyed so as to
296a1456c4aSZev Weiss                 // ensure that we end up with a fresh instance of it.
297a1456c4aSZev Weiss                 if (params->devicePresent())
298a1456c4aSZev Weiss                 {
299a1456c4aSZev Weiss                     std::cerr << "Clearing out previous instance for "
300a1456c4aSZev Weiss                               << path.str << "\n";
301a1456c4aSZev Weiss                     I2CDevice tmp(*params);
302a1456c4aSZev Weiss                 }
303a1456c4aSZev Weiss 
304a1456c4aSZev Weiss                 try
305a1456c4aSZev Weiss                 {
3067627c860SZev Weiss                     devices.emplace(
3077627c860SZev Weiss                         path.str,
3087627c860SZev Weiss                         std::make_pair(std::make_shared<I2CDevice>(*params),
3097627c860SZev Weiss                                        true));
310a1456c4aSZev Weiss                 }
311a1456c4aSZev Weiss                 catch (std::runtime_error&)
312a1456c4aSZev Weiss                 {
313a1456c4aSZev Weiss                     std::cerr << "Failed to instantiate " << params->type->name
314a1456c4aSZev Weiss                               << " at address " << params->address << " on bus "
315a1456c4aSZev Weiss                               << params->bus << "\n";
316a1456c4aSZev Weiss                 }
317a1456c4aSZev Weiss             }
318a1456c4aSZev Weiss         }
319a1456c4aSZev Weiss     }
3207627c860SZev Weiss     return devices;
321a1456c4aSZev Weiss }
322a1456c4aSZev Weiss 
3236714a25aSJames Feist void createSensors(
324*1f978631SEd Tanous     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
325f3fd1915SYong Li     boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>&
3266714a25aSJames Feist         sensors,
3276714a25aSJames Feist     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
3285591cf08SJames Feist     const std::shared_ptr<boost::container::flat_set<std::string>>&
329a1456c4aSZev Weiss         sensorsChanged,
330a1456c4aSZev Weiss     bool activateOnly)
3316714a25aSJames Feist {
332df515159SJames Feist     auto getter = std::make_shared<GetSensorConfiguration>(
333df515159SJames Feist         dbusConnection,
334a1456c4aSZev Weiss         [&io, &objectServer, &sensors, &dbusConnection, sensorsChanged,
335a1456c4aSZev Weiss          activateOnly](const ManagedObjectType& sensorConfigurations) {
3366714a25aSJames Feist         bool firstScan = sensorsChanged == nullptr;
337df515159SJames Feist 
338bb67932aSEd Tanous         SensorConfigMap configMap = buildSensorConfigMap(sensorConfigurations);
3399f3a74edSJayashree Dhanapal 
3407627c860SZev Weiss         auto devices = instantiateDevices(sensorConfigurations, sensors);
341a1456c4aSZev Weiss 
342544e7dc5SBruce Mitchell         // IIO _raw devices look like this on sysfs:
343544e7dc5SBruce Mitchell         //     /sys/bus/iio/devices/iio:device0/in_temp_raw
344544e7dc5SBruce Mitchell         //     /sys/bus/iio/devices/iio:device0/in_temp_offset
345544e7dc5SBruce Mitchell         //     /sys/bus/iio/devices/iio:device0/in_temp_scale
346544e7dc5SBruce Mitchell         //
347544e7dc5SBruce Mitchell         // Other IIO devices look like this on sysfs:
348544e7dc5SBruce Mitchell         //     /sys/bus/iio/devices/iio:device1/in_temp_input
349544e7dc5SBruce Mitchell         //     /sys/bus/iio/devices/iio:device1/in_pressure_input
3506714a25aSJames Feist         std::vector<fs::path> paths;
351544e7dc5SBruce Mitchell         fs::path root("/sys/bus/iio/devices");
352544e7dc5SBruce Mitchell         findFiles(root, R"(in_temp\d*_(input|raw))", paths);
353544e7dc5SBruce Mitchell         findFiles(root, R"(in_pressure\d*_(input|raw))", paths);
3543ec41c53SBruce Mitchell         findFiles(root, R"(in_humidityrelative\d*_(input|raw))", paths);
355544e7dc5SBruce Mitchell         findFiles(fs::path("/sys/class/hwmon"), R"(temp\d+_input)", paths);
356544e7dc5SBruce Mitchell 
357544e7dc5SBruce Mitchell         // iterate through all found temp and pressure sensors,
358544e7dc5SBruce Mitchell         // and try to match them with configuration
3596714a25aSJames Feist         for (auto& path : paths)
3606714a25aSJames Feist         {
3616714a25aSJames Feist             std::smatch match;
362544e7dc5SBruce Mitchell             const std::string pathStr = path.string();
3636714a25aSJames Feist             auto directory = path.parent_path();
364544e7dc5SBruce Mitchell             fs::path device;
3656714a25aSJames Feist 
366544e7dc5SBruce Mitchell             std::string deviceName;
367544e7dc5SBruce Mitchell             if (pathStr.starts_with("/sys/bus/iio/devices"))
3686714a25aSJames Feist             {
369544e7dc5SBruce Mitchell                 device = fs::canonical(directory);
370544e7dc5SBruce Mitchell                 deviceName = device.parent_path().stem();
37137266ca9SJames Feist             }
372544e7dc5SBruce Mitchell             else
373544e7dc5SBruce Mitchell             {
374544e7dc5SBruce Mitchell                 device = directory / "device";
375544e7dc5SBruce Mitchell                 deviceName = fs::canonical(device).stem();
376544e7dc5SBruce Mitchell             }
3778a57ec09SEd Tanous             auto findHyphen = deviceName.find('-');
37837266ca9SJames Feist             if (findHyphen == std::string::npos)
37937266ca9SJames Feist             {
38037266ca9SJames Feist                 std::cerr << "found bad device " << deviceName << "\n";
3816714a25aSJames Feist                 continue;
3826714a25aSJames Feist             }
38337266ca9SJames Feist             std::string busStr = deviceName.substr(0, findHyphen);
38437266ca9SJames Feist             std::string addrStr = deviceName.substr(findHyphen + 1);
38537266ca9SJames Feist 
3867dd6443bSJae Hyun Yoo             uint64_t bus = 0;
3877dd6443bSJae Hyun Yoo             uint64_t addr = 0;
3882049bd26SEd Tanous             std::from_chars_result res{};
389bb67932aSEd Tanous             res = std::from_chars(busStr.data(), busStr.data() + busStr.size(),
390bb67932aSEd Tanous                                   bus);
3917dd6443bSJae Hyun Yoo             if (res.ec != std::errc{})
3926714a25aSJames Feist             {
3937dd6443bSJae Hyun Yoo                 continue;
39437266ca9SJames Feist             }
395bb67932aSEd Tanous             res = std::from_chars(addrStr.data(),
396bb67932aSEd Tanous                                   addrStr.data() + addrStr.size(), addr, 16);
3977dd6443bSJae Hyun Yoo             if (res.ec != std::errc{})
39837266ca9SJames Feist             {
3996714a25aSJames Feist                 continue;
4006714a25aSJames Feist             }
40137266ca9SJames Feist 
402544e7dc5SBruce Mitchell             auto thisSensorParameters = getSensorParameters(path);
4039f3a74edSJayashree Dhanapal             auto findSensorCfg = configMap.find({bus, addr});
4049f3a74edSJayashree Dhanapal             if (findSensorCfg == configMap.end())
40537266ca9SJames Feist             {
40637266ca9SJames Feist                 continue;
40737266ca9SJames Feist             }
40837266ca9SJames Feist 
409bb67932aSEd Tanous             const std::string& interfacePath = findSensorCfg->second.sensorPath;
4107627c860SZev Weiss             auto findI2CDev = devices.find(interfacePath);
4117627c860SZev Weiss 
412a1456c4aSZev Weiss             std::shared_ptr<I2CDevice> i2cDev;
4137627c860SZev Weiss             if (findI2CDev != devices.end())
414a1456c4aSZev Weiss             {
4159bbeff75SJoseph Fu                 // If we're only looking to activate newly-instantiated i2c
4169bbeff75SJoseph Fu                 // devices and this sensor's underlying device was already there
4179bbeff75SJoseph Fu                 // before this call, there's nothing more to do here.
4189bbeff75SJoseph Fu                 if (activateOnly && !findI2CDev->second.second)
4199bbeff75SJoseph Fu                 {
4209bbeff75SJoseph Fu                     continue;
4219bbeff75SJoseph Fu                 }
4227627c860SZev Weiss                 i2cDev = findI2CDev->second.first;
423a1456c4aSZev Weiss             }
424a1456c4aSZev Weiss 
4259f3a74edSJayashree Dhanapal             const SensorData& sensorData = findSensorCfg->second.sensorData;
4269f3a74edSJayashree Dhanapal             const std::string& sensorType = findSensorCfg->second.interface;
4279f3a74edSJayashree Dhanapal             const SensorBaseConfigMap& baseConfigMap =
4289f3a74edSJayashree Dhanapal                 findSensorCfg->second.config;
429bb67932aSEd Tanous             std::vector<std::string>& hwmonName = findSensorCfg->second.name;
4306714a25aSJames Feist 
431544e7dc5SBruce Mitchell             // Temperature has "Name", pressure has "Name1"
4329f3a74edSJayashree Dhanapal             auto findSensorName = baseConfigMap.find("Name");
433fefdbe7fSPotin Lai             int index = 1;
4343ec41c53SBruce Mitchell             if (thisSensorParameters.typeName == "pressure" ||
4353ec41c53SBruce Mitchell                 thisSensorParameters.typeName == "humidity")
436544e7dc5SBruce Mitchell             {
4379f3a74edSJayashree Dhanapal                 findSensorName = baseConfigMap.find("Name1");
438fefdbe7fSPotin Lai                 index = 2;
439544e7dc5SBruce Mitchell             }
440544e7dc5SBruce Mitchell 
4419f3a74edSJayashree Dhanapal             if (findSensorName == baseConfigMap.end())
4426714a25aSJames Feist             {
4436714a25aSJames Feist                 std::cerr << "could not determine configuration name for "
44437266ca9SJames Feist                           << deviceName << "\n";
4456714a25aSJames Feist                 continue;
4466714a25aSJames Feist             }
447df515159SJames Feist             std::string sensorName =
448df515159SJames Feist                 std::get<std::string>(findSensorName->second);
4496714a25aSJames Feist             // on rescans, only update sensors we were signaled by
4506714a25aSJames Feist             auto findSensor = sensors.find(sensorName);
4516714a25aSJames Feist             if (!firstScan && findSensor != sensors.end())
4526714a25aSJames Feist             {
4536714a25aSJames Feist                 bool found = false;
454d653b75cSBruce Mitchell                 auto it = sensorsChanged->begin();
455d653b75cSBruce Mitchell                 while (it != sensorsChanged->end())
4566714a25aSJames Feist                 {
4576c106d66SZev Weiss                     if (it->ends_with(findSensor->second->name))
4586714a25aSJames Feist                     {
459d653b75cSBruce Mitchell                         it = sensorsChanged->erase(it);
4606714a25aSJames Feist                         findSensor->second = nullptr;
4616714a25aSJames Feist                         found = true;
4626714a25aSJames Feist                         break;
4636714a25aSJames Feist                     }
464d653b75cSBruce Mitchell                     ++it;
4656714a25aSJames Feist                 }
4666714a25aSJames Feist                 if (!found)
4676714a25aSJames Feist                 {
4686714a25aSJames Feist                     continue;
4696714a25aSJames Feist                 }
4706714a25aSJames Feist             }
4715636d52bSMatt Spinler 
4726714a25aSJames Feist             std::vector<thresholds::Threshold> sensorThresholds;
4735636d52bSMatt Spinler 
4749f3a74edSJayashree Dhanapal             if (!parseThresholdsFromConfig(sensorData, sensorThresholds,
4755636d52bSMatt Spinler                                            nullptr, &index))
4766714a25aSJames Feist             {
477bb67932aSEd Tanous                 std::cerr << "error populating thresholds for " << sensorName
478bb67932aSEd Tanous                           << " index " << index << "\n";
4796714a25aSJames Feist             }
48087bc67f7SJeff Lin 
4818569bf2aSZev Weiss             float pollRate = getPollRate(baseConfigMap, pollRateDefault);
482a4d2768cSZev Weiss             PowerState readState = getPowerState(baseConfigMap);
483100c20bfSJason Ling 
4849f3a74edSJayashree Dhanapal             auto permitSet = getPermitSet(baseConfigMap);
4858b3f7d40SAlex Qiu             auto& sensor = sensors[sensorName];
486a1456c4aSZev Weiss             if (!activateOnly)
487a1456c4aSZev Weiss             {
4888b3f7d40SAlex Qiu                 sensor = nullptr;
489a1456c4aSZev Weiss             }
490bb67932aSEd Tanous             auto hwmonFile =
491bb67932aSEd Tanous                 getFullHwmonFilePath(directory.string(), "temp1", permitSet);
492544e7dc5SBruce Mitchell             if (pathStr.starts_with("/sys/bus/iio/devices"))
493544e7dc5SBruce Mitchell             {
494544e7dc5SBruce Mitchell                 hwmonFile = pathStr;
495544e7dc5SBruce Mitchell             }
496100c20bfSJason Ling             if (hwmonFile)
497100c20bfSJason Ling             {
498a1456c4aSZev Weiss                 if (sensor != nullptr)
499a1456c4aSZev Weiss                 {
500a1456c4aSZev Weiss                     sensor->activate(*hwmonFile, i2cDev);
501a1456c4aSZev Weiss                 }
502a1456c4aSZev Weiss                 else
503a1456c4aSZev Weiss                 {
504f3fd1915SYong Li                     sensor = std::make_shared<HwmonTempSensor>(
505a1456c4aSZev Weiss                         *hwmonFile, sensorType, objectServer, dbusConnection,
506a1456c4aSZev Weiss                         io, sensorName, std::move(sensorThresholds),
507a1456c4aSZev Weiss                         thisSensorParameters, pollRate, interfacePath,
508a1456c4aSZev Weiss                         readState, i2cDev);
509f3fd1915SYong Li                     sensor->setupRead();
5109eb0cc3dSWilly Tu                 }
511a1456c4aSZev Weiss             }
5129f3a74edSJayashree Dhanapal             hwmonName.erase(
5139f3a74edSJayashree Dhanapal                 remove(hwmonName.begin(), hwmonName.end(), sensorName),
5149f3a74edSJayashree Dhanapal                 hwmonName.end());
5159eb0cc3dSWilly Tu 
5168b3f7d40SAlex Qiu             // Looking for keys like "Name1" for temp2_input,
5178b3f7d40SAlex Qiu             // "Name2" for temp3_input, etc.
5188b3f7d40SAlex Qiu             int i = 0;
5198b3f7d40SAlex Qiu             while (true)
52037266ca9SJames Feist             {
5218b3f7d40SAlex Qiu                 ++i;
522bb67932aSEd Tanous                 auto findKey = baseConfigMap.find("Name" + std::to_string(i));
5239f3a74edSJayashree Dhanapal                 if (findKey == baseConfigMap.end())
5248b3f7d40SAlex Qiu                 {
5258b3f7d40SAlex Qiu                     break;
52637266ca9SJames Feist                 }
527bb67932aSEd Tanous                 std::string sensorName = std::get<std::string>(findKey->second);
528bb67932aSEd Tanous                 hwmonFile = getFullHwmonFilePath(directory.string(),
529bb67932aSEd Tanous                                                  "temp" + std::to_string(i + 1),
530100c20bfSJason Ling                                                  permitSet);
531544e7dc5SBruce Mitchell                 if (pathStr.starts_with("/sys/bus/iio/devices"))
532544e7dc5SBruce Mitchell                 {
533544e7dc5SBruce Mitchell                     continue;
534544e7dc5SBruce Mitchell                 }
535100c20bfSJason Ling                 if (hwmonFile)
536100c20bfSJason Ling                 {
5375636d52bSMatt Spinler                     // To look up thresholds for these additional sensors,
5385636d52bSMatt Spinler                     // match on the Index property in the threshold data
5395636d52bSMatt Spinler                     // where the index comes from the sysfs file we're on,
5405636d52bSMatt Spinler                     // i.e. index = 2 for temp2_input.
5415636d52bSMatt Spinler                     int index = i + 1;
5425636d52bSMatt Spinler                     std::vector<thresholds::Threshold> thresholds;
5435636d52bSMatt Spinler 
5449f3a74edSJayashree Dhanapal                     if (!parseThresholdsFromConfig(sensorData, thresholds,
5455636d52bSMatt Spinler                                                    nullptr, &index))
5465636d52bSMatt Spinler                     {
5475636d52bSMatt Spinler                         std::cerr << "error populating thresholds for "
548bb67932aSEd Tanous                                   << sensorName << " index " << index << "\n";
5495636d52bSMatt Spinler                     }
5505636d52bSMatt Spinler 
5518b3f7d40SAlex Qiu                     auto& sensor = sensors[sensorName];
552a1456c4aSZev Weiss                     if (!activateOnly)
553a1456c4aSZev Weiss                     {
5548b3f7d40SAlex Qiu                         sensor = nullptr;
555a1456c4aSZev Weiss                     }
556a1456c4aSZev Weiss 
557a1456c4aSZev Weiss                     if (sensor != nullptr)
558a1456c4aSZev Weiss                     {
559a1456c4aSZev Weiss                         sensor->activate(*hwmonFile, i2cDev);
560a1456c4aSZev Weiss                     }
561a1456c4aSZev Weiss                     else
562a1456c4aSZev Weiss                     {
563f3fd1915SYong Li                         sensor = std::make_shared<HwmonTempSensor>(
564a1456c4aSZev Weiss                             *hwmonFile, sensorType, objectServer,
565a1456c4aSZev Weiss                             dbusConnection, io, sensorName,
566a1456c4aSZev Weiss                             std::move(thresholds), thisSensorParameters,
567a1456c4aSZev Weiss                             pollRate, interfacePath, readState, i2cDev);
568f3fd1915SYong Li                         sensor->setupRead();
5698b3f7d40SAlex Qiu                     }
570a1456c4aSZev Weiss                 }
5719eb0cc3dSWilly Tu 
5729eb0cc3dSWilly Tu                 hwmonName.erase(
5739eb0cc3dSWilly Tu                     remove(hwmonName.begin(), hwmonName.end(), sensorName),
5749eb0cc3dSWilly Tu                     hwmonName.end());
5756714a25aSJames Feist             }
57631ec7dbbSMatt Spinler             if (hwmonName.empty())
57731ec7dbbSMatt Spinler             {
5789f3a74edSJayashree Dhanapal                 configMap.erase(findSensorCfg);
5799f3a74edSJayashree Dhanapal             }
58031ec7dbbSMatt Spinler         }
5818a17c303SEd Tanous         });
582d29f8aa0SZev Weiss     std::vector<std::string> types(sensorTypes.size());
583d29f8aa0SZev Weiss     for (const auto& [type, dt] : sensorTypes)
584d29f8aa0SZev Weiss     {
585d29f8aa0SZev Weiss         types.push_back(type);
586d29f8aa0SZev Weiss     }
587d29f8aa0SZev Weiss     getter->getConfiguration(types);
5886714a25aSJames Feist }
5896714a25aSJames Feist 
59020bf2c1cSMatt Spinler void interfaceRemoved(
59192f8f515SPatrick Williams     sdbusplus::message_t& message,
59220bf2c1cSMatt Spinler     boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>&
59320bf2c1cSMatt Spinler         sensors)
59420bf2c1cSMatt Spinler {
59520bf2c1cSMatt Spinler     if (message.is_method_error())
59620bf2c1cSMatt Spinler     {
59720bf2c1cSMatt Spinler         std::cerr << "interfacesRemoved callback method error\n";
59820bf2c1cSMatt Spinler         return;
59920bf2c1cSMatt Spinler     }
60020bf2c1cSMatt Spinler 
60120bf2c1cSMatt Spinler     sdbusplus::message::object_path path;
60220bf2c1cSMatt Spinler     std::vector<std::string> interfaces;
60320bf2c1cSMatt Spinler 
60420bf2c1cSMatt Spinler     message.read(path, interfaces);
60520bf2c1cSMatt Spinler 
60620bf2c1cSMatt Spinler     // If the xyz.openbmc_project.Confguration.X interface was removed
60720bf2c1cSMatt Spinler     // for one or more sensors, delete those sensor objects.
60820bf2c1cSMatt Spinler     auto sensorIt = sensors.begin();
60920bf2c1cSMatt Spinler     while (sensorIt != sensors.end())
61020bf2c1cSMatt Spinler     {
61120bf2c1cSMatt Spinler         if ((sensorIt->second->configurationPath == path) &&
61220bf2c1cSMatt Spinler             (std::find(interfaces.begin(), interfaces.end(),
61320bf2c1cSMatt Spinler                        sensorIt->second->objectType) != interfaces.end()))
61420bf2c1cSMatt Spinler         {
61520bf2c1cSMatt Spinler             sensorIt = sensors.erase(sensorIt);
61620bf2c1cSMatt Spinler         }
61720bf2c1cSMatt Spinler         else
61820bf2c1cSMatt Spinler         {
61920bf2c1cSMatt Spinler             sensorIt++;
62020bf2c1cSMatt Spinler         }
62120bf2c1cSMatt Spinler     }
62220bf2c1cSMatt Spinler }
62320bf2c1cSMatt Spinler 
624a1456c4aSZev Weiss static void powerStateChanged(
625a1456c4aSZev Weiss     PowerState type, bool newState,
626a1456c4aSZev Weiss     boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>&
627a1456c4aSZev Weiss         sensors,
628*1f978631SEd Tanous     boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
629a1456c4aSZev Weiss     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
630a1456c4aSZev Weiss {
631a1456c4aSZev Weiss     if (newState)
632a1456c4aSZev Weiss     {
633a1456c4aSZev Weiss         createSensors(io, objectServer, sensors, dbusConnection, nullptr, true);
634a1456c4aSZev Weiss     }
635a1456c4aSZev Weiss     else
636a1456c4aSZev Weiss     {
637a1456c4aSZev Weiss         for (auto& [path, sensor] : sensors)
638a1456c4aSZev Weiss         {
639a1456c4aSZev Weiss             if (sensor != nullptr && sensor->readState == type)
640a1456c4aSZev Weiss             {
641a1456c4aSZev Weiss                 sensor->deactivate();
642a1456c4aSZev Weiss             }
643a1456c4aSZev Weiss         }
644a1456c4aSZev Weiss     }
645a1456c4aSZev Weiss }
646a1456c4aSZev Weiss 
647b6c0b914SJames Feist int main()
6486714a25aSJames Feist {
649*1f978631SEd Tanous     boost::asio::io_context io;
6506714a25aSJames Feist     auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
65114ed5e99SEd Tanous     sdbusplus::asio::object_server objectServer(systemBus, true);
65214ed5e99SEd Tanous     objectServer.add_manager("/xyz/openbmc_project/sensors");
6536714a25aSJames Feist     systemBus->request_name("xyz.openbmc_project.HwmonTempSensor");
65414ed5e99SEd Tanous 
655f3fd1915SYong Li     boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>
6566714a25aSJames Feist         sensors;
6575591cf08SJames Feist     auto sensorsChanged =
6585591cf08SJames Feist         std::make_shared<boost::container::flat_set<std::string>>();
6596714a25aSJames Feist 
660a1456c4aSZev Weiss     auto powerCallBack = [&sensors, &io, &objectServer,
661a1456c4aSZev Weiss                           &systemBus](PowerState type, bool state) {
662a1456c4aSZev Weiss         powerStateChanged(type, state, sensors, io, objectServer, systemBus);
663a1456c4aSZev Weiss     };
664a1456c4aSZev Weiss     setupPowerMatchCallback(systemBus, powerCallBack);
665a1456c4aSZev Weiss 
6666714a25aSJames Feist     io.post([&]() {
667a1456c4aSZev Weiss         createSensors(io, objectServer, sensors, systemBus, nullptr, false);
6686714a25aSJames Feist     });
6696714a25aSJames Feist 
6709b4a20e9SEd Tanous     boost::asio::steady_timer filterTimer(io);
67192f8f515SPatrick Williams     std::function<void(sdbusplus::message_t&)> eventHandler =
67292f8f515SPatrick Williams         [&](sdbusplus::message_t& message) {
6736714a25aSJames Feist         if (message.is_method_error())
6746714a25aSJames Feist         {
6756714a25aSJames Feist             std::cerr << "callback method error\n";
6766714a25aSJames Feist             return;
6776714a25aSJames Feist         }
6786714a25aSJames Feist         sensorsChanged->insert(message.get_path());
6796714a25aSJames Feist         // this implicitly cancels the timer
6809b4a20e9SEd Tanous         filterTimer.expires_from_now(std::chrono::seconds(1));
6816714a25aSJames Feist 
6826714a25aSJames Feist         filterTimer.async_wait([&](const boost::system::error_code& ec) {
6836714a25aSJames Feist             if (ec == boost::asio::error::operation_aborted)
6846714a25aSJames Feist             {
6856714a25aSJames Feist                 /* we were canceled*/
6866714a25aSJames Feist                 return;
6876714a25aSJames Feist             }
6888a57ec09SEd Tanous             if (ec)
6896714a25aSJames Feist             {
6906714a25aSJames Feist                 std::cerr << "timer error\n";
6916714a25aSJames Feist                 return;
6926714a25aSJames Feist             }
693a1456c4aSZev Weiss             createSensors(io, objectServer, sensors, systemBus, sensorsChanged,
694a1456c4aSZev Weiss                           false);
6956714a25aSJames Feist         });
6966714a25aSJames Feist     };
6976714a25aSJames Feist 
698214d9717SZev Weiss     std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
699214d9717SZev Weiss         setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler);
7001263c3daSBruce Lee     setupManufacturingModeMatch(*systemBus);
70120bf2c1cSMatt Spinler 
70220bf2c1cSMatt Spinler     // Watch for entity-manager to remove configuration interfaces
70320bf2c1cSMatt Spinler     // so the corresponding sensors can be removed.
70492f8f515SPatrick Williams     auto ifaceRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
70592f8f515SPatrick Williams         static_cast<sdbusplus::bus_t&>(*systemBus),
70620bf2c1cSMatt Spinler         "type='signal',member='InterfacesRemoved',arg0path='" +
70720bf2c1cSMatt Spinler             std::string(inventoryPath) + "/'",
70892f8f515SPatrick Williams         [&sensors](sdbusplus::message_t& msg) {
70920bf2c1cSMatt Spinler         interfaceRemoved(msg, sensors);
71020bf2c1cSMatt Spinler         });
71120bf2c1cSMatt Spinler 
71220bf2c1cSMatt Spinler     matches.emplace_back(std::move(ifaceRemovedMatch));
71320bf2c1cSMatt Spinler 
7146714a25aSJames Feist     io.run();
7156714a25aSJames Feist }
716