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"
19eacbfdd1SEd Tanous #include "SensorPaths.hpp"
20eacbfdd1SEd Tanous #include "Thresholds.hpp"
21e73bd0a1SAndrew Jeffery #include "Utils.hpp"
22e73bd0a1SAndrew Jeffery
23eacbfdd1SEd Tanous #include <boost/asio/error.hpp>
24eacbfdd1SEd Tanous #include <boost/asio/io_context.hpp>
25eacbfdd1SEd Tanous #include <boost/asio/post.hpp>
26eacbfdd1SEd Tanous #include <boost/asio/steady_timer.hpp>
2796e97db7SPatrick Venture #include <boost/container/flat_map.hpp>
286714a25aSJames Feist #include <boost/container/flat_set.hpp>
2938fb5983SJames Feist #include <sdbusplus/asio/connection.hpp>
3038fb5983SJames Feist #include <sdbusplus/asio/object_server.hpp>
31eacbfdd1SEd Tanous #include <sdbusplus/bus.hpp>
3238fb5983SJames Feist #include <sdbusplus/bus/match.hpp>
33eacbfdd1SEd Tanous #include <sdbusplus/message.hpp>
34eacbfdd1SEd Tanous #include <sdbusplus/message/native_types.hpp>
3538fb5983SJames Feist
36eacbfdd1SEd Tanous #include <algorithm>
3738fb5983SJames Feist #include <array>
38eacbfdd1SEd Tanous #include <chrono>
39eacbfdd1SEd Tanous #include <cstddef>
40eacbfdd1SEd Tanous #include <cstdint>
4124f02f24SJames Feist #include <filesystem>
4296e97db7SPatrick Venture #include <functional>
43eacbfdd1SEd Tanous #include <ios>
44eacbfdd1SEd Tanous #include <iostream>
4596e97db7SPatrick Venture #include <memory>
46eacbfdd1SEd Tanous #include <optional>
476714a25aSJames Feist #include <regex>
4896e97db7SPatrick Venture #include <string>
49eacbfdd1SEd Tanous #include <system_error>
5096e97db7SPatrick Venture #include <utility>
5196e97db7SPatrick Venture #include <variant>
5296e97db7SPatrick Venture #include <vector>
536714a25aSJames Feist
5487bc67f7SJeff Lin static constexpr float pollRateDefault = 0.5;
556714a25aSJames Feist
56544e7dc5SBruce Mitchell static constexpr double maxValuePressure = 120000; // Pascals
57544e7dc5SBruce Mitchell static constexpr double minValuePressure = 30000; // Pascals
58544e7dc5SBruce Mitchell
593ec41c53SBruce Mitchell static constexpr double maxValueRelativeHumidity = 100; // PercentRH
603ec41c53SBruce Mitchell static constexpr double minValueRelativeHumidity = 0; // PercentRH
613ec41c53SBruce Mitchell
62544e7dc5SBruce Mitchell static constexpr double maxValueTemperature = 127; // DegreesC
63544e7dc5SBruce Mitchell static constexpr double minValueTemperature = -128; // DegreesC
64544e7dc5SBruce Mitchell
65cf3bce6eSJames Feist namespace fs = std::filesystem;
66d29f8aa0SZev Weiss
67d29f8aa0SZev Weiss static const I2CDeviceTypeMap sensorTypes{
6876d3676bSPatrick Rudolph {"ADM1021", I2CDeviceType{"adm1021", true}},
69d29f8aa0SZev Weiss {"DPS310", I2CDeviceType{"dps310", false}},
70*53896db5SDelphine CC Chiu {"EMC1403", I2CDeviceType{"emc1403", true}},
71d29f8aa0SZev Weiss {"EMC1412", I2CDeviceType{"emc1412", true}},
72d29f8aa0SZev Weiss {"EMC1413", I2CDeviceType{"emc1413", true}},
73d29f8aa0SZev Weiss {"EMC1414", I2CDeviceType{"emc1414", true}},
74d29f8aa0SZev Weiss {"HDC1080", I2CDeviceType{"hdc1080", false}},
75d29f8aa0SZev Weiss {"JC42", I2CDeviceType{"jc42", true}},
76d29f8aa0SZev Weiss {"LM75A", I2CDeviceType{"lm75a", true}},
77d29f8aa0SZev Weiss {"LM95234", I2CDeviceType{"lm95234", true}},
78d29f8aa0SZev Weiss {"MAX31725", I2CDeviceType{"max31725", true}},
79d29f8aa0SZev Weiss {"MAX31730", I2CDeviceType{"max31730", true}},
80d29f8aa0SZev Weiss {"MAX6581", I2CDeviceType{"max6581", true}},
81d29f8aa0SZev Weiss {"MAX6654", I2CDeviceType{"max6654", true}},
82f3862ee3SPatrick Rudolph {"MAX6639", I2CDeviceType{"max6639", true}},
8328b88233SPotin Lai {"MCP9600", I2CDeviceType{"mcp9600", false}},
84d29f8aa0SZev Weiss {"NCT6779", I2CDeviceType{"nct6779", true}},
85d29f8aa0SZev Weiss {"NCT7802", I2CDeviceType{"nct7802", true}},
86fb011cc4SPotin Lai {"PT5161L", I2CDeviceType{"pt5161l", true}},
87d29f8aa0SZev Weiss {"SBTSI", I2CDeviceType{"sbtsi", true}},
88d29f8aa0SZev Weiss {"SI7020", I2CDeviceType{"si7020", false}},
899b0a6f67STim Lee {"TMP100", I2CDeviceType{"tmp100", true}},
90d29f8aa0SZev Weiss {"TMP112", I2CDeviceType{"tmp112", true}},
91d29f8aa0SZev Weiss {"TMP175", I2CDeviceType{"tmp175", true}},
92d29f8aa0SZev Weiss {"TMP421", I2CDeviceType{"tmp421", true}},
93d29f8aa0SZev Weiss {"TMP441", I2CDeviceType{"tmp441", true}},
94f4c3fad0SKhang Kieu {"TMP461", I2CDeviceType{"tmp461", true}},
959a7db6afSHieu Huynh {"TMP464", I2CDeviceType{"tmp464", true}},
96d29f8aa0SZev Weiss {"TMP75", I2CDeviceType{"tmp75", true}},
97d29f8aa0SZev Weiss {"W83773G", I2CDeviceType{"w83773g", true}},
98d29f8aa0SZev Weiss };
996714a25aSJames Feist
100544e7dc5SBruce Mitchell static struct SensorParams
getSensorParameters(const std::filesystem::path & path)101544e7dc5SBruce Mitchell getSensorParameters(const std::filesystem::path& path)
102544e7dc5SBruce Mitchell {
103544e7dc5SBruce Mitchell // offset is to default to 0 and scale to 1, see lore
104544e7dc5SBruce Mitchell // https://lore.kernel.org/linux-iio/5c79425f-6e88-36b6-cdfe-4080738d039f@metafoo.de/
105779c96a2SPatrick Williams struct SensorParams tmpSensorParameters = {
106779c96a2SPatrick Williams .minValue = minValueTemperature,
107544e7dc5SBruce Mitchell .maxValue = maxValueTemperature,
108544e7dc5SBruce Mitchell .offsetValue = 0.0,
109544e7dc5SBruce Mitchell .scaleValue = 1.0,
110779c96a2SPatrick Williams .units = sensor_paths::unitDegreesC,
111544e7dc5SBruce Mitchell .typeName = "temperature"};
112544e7dc5SBruce Mitchell
113544e7dc5SBruce Mitchell // For IIO RAW sensors we get a raw_value, an offset, and scale
114544e7dc5SBruce Mitchell // to compute the value = (raw_value + offset) * scale
115544e7dc5SBruce Mitchell // with a _raw IIO device we need to get the
116544e7dc5SBruce Mitchell // offsetValue and scaleValue from the driver
117544e7dc5SBruce Mitchell // these are used to compute the reading in
118544e7dc5SBruce Mitchell // units that have yet to be scaled for D-Bus.
119544e7dc5SBruce Mitchell const std::string pathStr = path.string();
120544e7dc5SBruce Mitchell if (pathStr.ends_with("_raw"))
121544e7dc5SBruce Mitchell {
122779c96a2SPatrick Williams std::string pathOffsetStr = pathStr.substr(0, pathStr.size() - 4) +
123779c96a2SPatrick Williams "_offset";
124544e7dc5SBruce Mitchell std::optional<double> tmpOffsetValue = readFile(pathOffsetStr, 1.0);
125544e7dc5SBruce Mitchell // In case there is nothing to read skip this device
126544e7dc5SBruce Mitchell // This is not an error condition see lore
127544e7dc5SBruce Mitchell // https://lore.kernel.org/linux-iio/5c79425f-6e88-36b6-cdfe-4080738d039f@metafoo.de/
128544e7dc5SBruce Mitchell if (tmpOffsetValue)
129544e7dc5SBruce Mitchell {
130544e7dc5SBruce Mitchell tmpSensorParameters.offsetValue = *tmpOffsetValue;
131544e7dc5SBruce Mitchell }
132544e7dc5SBruce Mitchell
133779c96a2SPatrick Williams std::string pathScaleStr = pathStr.substr(0, pathStr.size() - 4) +
134779c96a2SPatrick Williams "_scale";
135544e7dc5SBruce Mitchell std::optional<double> tmpScaleValue = readFile(pathScaleStr, 1.0);
136544e7dc5SBruce Mitchell // In case there is nothing to read skip this device
137544e7dc5SBruce Mitchell // This is not an error condition see lore
138544e7dc5SBruce Mitchell // https://lore.kernel.org/linux-iio/5c79425f-6e88-36b6-cdfe-4080738d039f@metafoo.de/
139544e7dc5SBruce Mitchell if (tmpScaleValue)
140544e7dc5SBruce Mitchell {
141544e7dc5SBruce Mitchell tmpSensorParameters.scaleValue = *tmpScaleValue;
142544e7dc5SBruce Mitchell }
143544e7dc5SBruce Mitchell }
144544e7dc5SBruce Mitchell
145544e7dc5SBruce Mitchell // Temperatures are read in milli degrees Celsius, we need
146544e7dc5SBruce Mitchell // degrees Celsius. Pressures are read in kilopascal, we need
147544e7dc5SBruce Mitchell // Pascals. On D-Bus for Open BMC we use the International
148544e7dc5SBruce Mitchell // System of Units without prefixes. Links to the kernel
149544e7dc5SBruce Mitchell // documentation:
150544e7dc5SBruce Mitchell // https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
151544e7dc5SBruce Mitchell // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-iio
152544e7dc5SBruce Mitchell if (path.filename() == "in_pressure_input" ||
153544e7dc5SBruce Mitchell path.filename() == "in_pressure_raw")
154544e7dc5SBruce Mitchell {
155544e7dc5SBruce Mitchell tmpSensorParameters.minValue = minValuePressure;
156544e7dc5SBruce Mitchell tmpSensorParameters.maxValue = maxValuePressure;
157544e7dc5SBruce Mitchell // Pressures are read in kilopascal, we need Pascals.
158544e7dc5SBruce Mitchell tmpSensorParameters.scaleValue *= 1000.0;
159544e7dc5SBruce Mitchell tmpSensorParameters.typeName = "pressure";
1605a86e562SBruce Mitchell tmpSensorParameters.units = sensor_paths::unitPascals;
161544e7dc5SBruce Mitchell }
1623ec41c53SBruce Mitchell else if (path.filename() == "in_humidityrelative_input" ||
1633ec41c53SBruce Mitchell path.filename() == "in_humidityrelative_raw")
1643ec41c53SBruce Mitchell {
1653ec41c53SBruce Mitchell tmpSensorParameters.minValue = minValueRelativeHumidity;
1663ec41c53SBruce Mitchell tmpSensorParameters.maxValue = maxValueRelativeHumidity;
1673ec41c53SBruce Mitchell // Relative Humidity are read in milli-percent, we need percent.
1683ec41c53SBruce Mitchell tmpSensorParameters.scaleValue *= 0.001;
1693ec41c53SBruce Mitchell tmpSensorParameters.typeName = "humidity";
170a9c1bec4SPotin Lai tmpSensorParameters.units = sensor_paths::unitPercentRH;
1713ec41c53SBruce Mitchell }
172544e7dc5SBruce Mitchell else
173544e7dc5SBruce Mitchell {
174544e7dc5SBruce Mitchell // Temperatures are read in milli degrees Celsius,
175544e7dc5SBruce Mitchell // we need degrees Celsius.
176544e7dc5SBruce Mitchell tmpSensorParameters.scaleValue *= 0.001;
177544e7dc5SBruce Mitchell }
178544e7dc5SBruce Mitchell
179544e7dc5SBruce Mitchell return tmpSensorParameters;
180544e7dc5SBruce Mitchell }
181544e7dc5SBruce Mitchell
1829f3a74edSJayashree Dhanapal struct SensorConfigKey
1839f3a74edSJayashree Dhanapal {
1849f3a74edSJayashree Dhanapal uint64_t bus;
1859f3a74edSJayashree Dhanapal uint64_t addr;
operator <SensorConfigKey1869f3a74edSJayashree Dhanapal bool operator<(const SensorConfigKey& other) const
1879f3a74edSJayashree Dhanapal {
1889f3a74edSJayashree Dhanapal if (bus != other.bus)
1899f3a74edSJayashree Dhanapal {
1909f3a74edSJayashree Dhanapal return bus < other.bus;
1919f3a74edSJayashree Dhanapal }
1929f3a74edSJayashree Dhanapal return addr < other.addr;
1939f3a74edSJayashree Dhanapal }
1949f3a74edSJayashree Dhanapal };
1959f3a74edSJayashree Dhanapal
1969f3a74edSJayashree Dhanapal struct SensorConfig
1979f3a74edSJayashree Dhanapal {
1989f3a74edSJayashree Dhanapal std::string sensorPath;
1999f3a74edSJayashree Dhanapal SensorData sensorData;
2009f3a74edSJayashree Dhanapal std::string interface;
2019f3a74edSJayashree Dhanapal SensorBaseConfigMap config;
2029f3a74edSJayashree Dhanapal std::vector<std::string> name;
2039f3a74edSJayashree Dhanapal };
2049f3a74edSJayashree Dhanapal
2059f3a74edSJayashree Dhanapal using SensorConfigMap =
2069f3a74edSJayashree Dhanapal boost::container::flat_map<SensorConfigKey, SensorConfig>;
2079f3a74edSJayashree Dhanapal
2089f3a74edSJayashree Dhanapal static SensorConfigMap
buildSensorConfigMap(const ManagedObjectType & sensorConfigs)2099f3a74edSJayashree Dhanapal buildSensorConfigMap(const ManagedObjectType& sensorConfigs)
2109f3a74edSJayashree Dhanapal {
2119f3a74edSJayashree Dhanapal SensorConfigMap configMap;
212a1808330SZev Weiss for (const auto& [path, cfgData] : sensorConfigs)
2139f3a74edSJayashree Dhanapal {
214a1808330SZev Weiss for (const auto& [intf, cfg] : cfgData)
2159f3a74edSJayashree Dhanapal {
2169f3a74edSJayashree Dhanapal auto busCfg = cfg.find("Bus");
2179f3a74edSJayashree Dhanapal auto addrCfg = cfg.find("Address");
2189f3a74edSJayashree Dhanapal if ((busCfg == cfg.end()) || (addrCfg == cfg.end()))
2199f3a74edSJayashree Dhanapal {
2209f3a74edSJayashree Dhanapal continue;
2219f3a74edSJayashree Dhanapal }
2229f3a74edSJayashree Dhanapal
2232049bd26SEd Tanous if ((std::get_if<uint64_t>(&busCfg->second) == nullptr) ||
2242049bd26SEd Tanous (std::get_if<uint64_t>(&addrCfg->second) == nullptr))
2259f3a74edSJayashree Dhanapal {
226a1808330SZev Weiss std::cerr << path.str << " Bus or Address invalid\n";
2279f3a74edSJayashree Dhanapal continue;
2289f3a74edSJayashree Dhanapal }
2299f3a74edSJayashree Dhanapal
2309f3a74edSJayashree Dhanapal std::vector<std::string> hwmonNames;
2319f3a74edSJayashree Dhanapal auto nameCfg = cfg.find("Name");
2329f3a74edSJayashree Dhanapal if (nameCfg != cfg.end())
2339f3a74edSJayashree Dhanapal {
2349f3a74edSJayashree Dhanapal hwmonNames.push_back(std::get<std::string>(nameCfg->second));
2359f3a74edSJayashree Dhanapal size_t i = 1;
2369f3a74edSJayashree Dhanapal while (true)
2379f3a74edSJayashree Dhanapal {
2389f3a74edSJayashree Dhanapal auto sensorNameCfg = cfg.find("Name" + std::to_string(i));
2399f3a74edSJayashree Dhanapal if (sensorNameCfg == cfg.end())
2409f3a74edSJayashree Dhanapal {
2419f3a74edSJayashree Dhanapal break;
2429f3a74edSJayashree Dhanapal }
2439f3a74edSJayashree Dhanapal hwmonNames.push_back(
2449f3a74edSJayashree Dhanapal std::get<std::string>(sensorNameCfg->second));
2459f3a74edSJayashree Dhanapal i++;
2469f3a74edSJayashree Dhanapal }
2479f3a74edSJayashree Dhanapal }
2489f3a74edSJayashree Dhanapal
2499f3a74edSJayashree Dhanapal SensorConfigKey key = {std::get<uint64_t>(busCfg->second),
2509f3a74edSJayashree Dhanapal std::get<uint64_t>(addrCfg->second)};
251a1808330SZev Weiss SensorConfig val = {path.str, cfgData, intf, cfg, hwmonNames};
2529f3a74edSJayashree Dhanapal
2539f3a74edSJayashree Dhanapal auto [it, inserted] = configMap.emplace(key, std::move(val));
2549f3a74edSJayashree Dhanapal if (!inserted)
2559f3a74edSJayashree Dhanapal {
256a1808330SZev Weiss std::cerr << path.str << ": ignoring duplicate entry for {"
257a1808330SZev Weiss << key.bus << ", 0x" << std::hex << key.addr
258a1808330SZev Weiss << std::dec << "}\n";
2599f3a74edSJayashree Dhanapal }
2609f3a74edSJayashree Dhanapal }
2619f3a74edSJayashree Dhanapal }
2629f3a74edSJayashree Dhanapal return configMap;
2639f3a74edSJayashree Dhanapal }
2649f3a74edSJayashree Dhanapal
createSensors(boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,boost::container::flat_map<std::string,std::shared_ptr<HwmonTempSensor>> & sensors,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,const std::shared_ptr<boost::container::flat_set<std::string>> & sensorsChanged,bool activateOnly)2656714a25aSJames Feist void createSensors(
2661f978631SEd Tanous boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
267f3fd1915SYong Li boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>&
2686714a25aSJames Feist sensors,
2696714a25aSJames Feist std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
2705591cf08SJames Feist const std::shared_ptr<boost::container::flat_set<std::string>>&
271a1456c4aSZev Weiss sensorsChanged,
272a1456c4aSZev Weiss bool activateOnly)
2736714a25aSJames Feist {
274df515159SJames Feist auto getter = std::make_shared<GetSensorConfiguration>(
275df515159SJames Feist dbusConnection,
276a1456c4aSZev Weiss [&io, &objectServer, &sensors, &dbusConnection, sensorsChanged,
277a1456c4aSZev Weiss activateOnly](const ManagedObjectType& sensorConfigurations) {
2786714a25aSJames Feist bool firstScan = sensorsChanged == nullptr;
279df515159SJames Feist
280bb67932aSEd Tanous SensorConfigMap configMap = buildSensorConfigMap(sensorConfigurations);
2819f3a74edSJayashree Dhanapal
282c564eda5SMatt Simmering auto devices = instantiateDevices(sensorConfigurations, sensors,
283c564eda5SMatt Simmering sensorTypes);
284a1456c4aSZev Weiss
285544e7dc5SBruce Mitchell // IIO _raw devices look like this on sysfs:
286544e7dc5SBruce Mitchell // /sys/bus/iio/devices/iio:device0/in_temp_raw
287544e7dc5SBruce Mitchell // /sys/bus/iio/devices/iio:device0/in_temp_offset
288544e7dc5SBruce Mitchell // /sys/bus/iio/devices/iio:device0/in_temp_scale
289544e7dc5SBruce Mitchell //
290544e7dc5SBruce Mitchell // Other IIO devices look like this on sysfs:
291544e7dc5SBruce Mitchell // /sys/bus/iio/devices/iio:device1/in_temp_input
292544e7dc5SBruce Mitchell // /sys/bus/iio/devices/iio:device1/in_pressure_input
2936714a25aSJames Feist std::vector<fs::path> paths;
294544e7dc5SBruce Mitchell fs::path root("/sys/bus/iio/devices");
295544e7dc5SBruce Mitchell findFiles(root, R"(in_temp\d*_(input|raw))", paths);
296544e7dc5SBruce Mitchell findFiles(root, R"(in_pressure\d*_(input|raw))", paths);
2973ec41c53SBruce Mitchell findFiles(root, R"(in_humidityrelative\d*_(input|raw))", paths);
298544e7dc5SBruce Mitchell findFiles(fs::path("/sys/class/hwmon"), R"(temp\d+_input)", paths);
299544e7dc5SBruce Mitchell
300544e7dc5SBruce Mitchell // iterate through all found temp and pressure sensors,
301544e7dc5SBruce Mitchell // and try to match them with configuration
3026714a25aSJames Feist for (auto& path : paths)
3036714a25aSJames Feist {
3046714a25aSJames Feist std::smatch match;
305544e7dc5SBruce Mitchell const std::string pathStr = path.string();
3066714a25aSJames Feist auto directory = path.parent_path();
307544e7dc5SBruce Mitchell fs::path device;
3086714a25aSJames Feist
309544e7dc5SBruce Mitchell std::string deviceName;
310ae463966SZhikui Ren std::error_code ec;
311544e7dc5SBruce Mitchell if (pathStr.starts_with("/sys/bus/iio/devices"))
3126714a25aSJames Feist {
313ae463966SZhikui Ren device = fs::canonical(directory, ec);
314ae463966SZhikui Ren if (ec)
315ae463966SZhikui Ren {
316ae463966SZhikui Ren std::cerr << "Fail to find device in path [" << pathStr
317ae463966SZhikui Ren << "]\n";
318ae463966SZhikui Ren continue;
319ae463966SZhikui Ren }
320544e7dc5SBruce Mitchell deviceName = device.parent_path().stem();
32137266ca9SJames Feist }
322544e7dc5SBruce Mitchell else
323544e7dc5SBruce Mitchell {
324ae463966SZhikui Ren device = fs::canonical(directory / "device", ec);
325ae463966SZhikui Ren if (ec)
326ae463966SZhikui Ren {
327ae463966SZhikui Ren std::cerr << "Fail to find device in path [" << pathStr
328ae463966SZhikui Ren << "]\n";
329ae463966SZhikui Ren continue;
330ae463966SZhikui Ren }
331ae463966SZhikui Ren deviceName = device.stem();
332544e7dc5SBruce Mitchell }
33337266ca9SJames Feist
334278e177fSTom Tung uint64_t bus = 0;
335278e177fSTom Tung uint64_t addr = 0;
33603d333e0SAkshit Shah if (!getDeviceBusAddr(deviceName, bus, addr))
33737266ca9SJames Feist {
3386714a25aSJames Feist continue;
3396714a25aSJames Feist }
34037266ca9SJames Feist
341544e7dc5SBruce Mitchell auto thisSensorParameters = getSensorParameters(path);
3429f3a74edSJayashree Dhanapal auto findSensorCfg = configMap.find({bus, addr});
3439f3a74edSJayashree Dhanapal if (findSensorCfg == configMap.end())
34437266ca9SJames Feist {
34537266ca9SJames Feist continue;
34637266ca9SJames Feist }
34737266ca9SJames Feist
348bb67932aSEd Tanous const std::string& interfacePath = findSensorCfg->second.sensorPath;
3497627c860SZev Weiss auto findI2CDev = devices.find(interfacePath);
3507627c860SZev Weiss
351a1456c4aSZev Weiss std::shared_ptr<I2CDevice> i2cDev;
3527627c860SZev Weiss if (findI2CDev != devices.end())
353a1456c4aSZev Weiss {
3549bbeff75SJoseph Fu // If we're only looking to activate newly-instantiated i2c
3559bbeff75SJoseph Fu // devices and this sensor's underlying device was already there
3569bbeff75SJoseph Fu // before this call, there's nothing more to do here.
3579bbeff75SJoseph Fu if (activateOnly && !findI2CDev->second.second)
3589bbeff75SJoseph Fu {
3599bbeff75SJoseph Fu continue;
3609bbeff75SJoseph Fu }
3617627c860SZev Weiss i2cDev = findI2CDev->second.first;
362a1456c4aSZev Weiss }
363a1456c4aSZev Weiss
3649f3a74edSJayashree Dhanapal const SensorData& sensorData = findSensorCfg->second.sensorData;
3650489ec20SMatt Spinler std::string sensorType = findSensorCfg->second.interface;
3660489ec20SMatt Spinler auto pos = sensorType.find_last_of('.');
3670489ec20SMatt Spinler if (pos != std::string::npos)
3680489ec20SMatt Spinler {
3690489ec20SMatt Spinler sensorType = sensorType.substr(pos + 1);
3700489ec20SMatt Spinler }
3719f3a74edSJayashree Dhanapal const SensorBaseConfigMap& baseConfigMap =
3729f3a74edSJayashree Dhanapal findSensorCfg->second.config;
373bb67932aSEd Tanous std::vector<std::string>& hwmonName = findSensorCfg->second.name;
3746714a25aSJames Feist
375544e7dc5SBruce Mitchell // Temperature has "Name", pressure has "Name1"
3769f3a74edSJayashree Dhanapal auto findSensorName = baseConfigMap.find("Name");
377fefdbe7fSPotin Lai int index = 1;
3783ec41c53SBruce Mitchell if (thisSensorParameters.typeName == "pressure" ||
3793ec41c53SBruce Mitchell thisSensorParameters.typeName == "humidity")
380544e7dc5SBruce Mitchell {
3819f3a74edSJayashree Dhanapal findSensorName = baseConfigMap.find("Name1");
382fefdbe7fSPotin Lai index = 2;
383544e7dc5SBruce Mitchell }
384544e7dc5SBruce Mitchell
3859f3a74edSJayashree Dhanapal if (findSensorName == baseConfigMap.end())
3866714a25aSJames Feist {
3876714a25aSJames Feist std::cerr << "could not determine configuration name for "
38837266ca9SJames Feist << deviceName << "\n";
3896714a25aSJames Feist continue;
3906714a25aSJames Feist }
391df515159SJames Feist std::string sensorName =
392df515159SJames Feist std::get<std::string>(findSensorName->second);
3936714a25aSJames Feist // on rescans, only update sensors we were signaled by
3946714a25aSJames Feist auto findSensor = sensors.find(sensorName);
3956714a25aSJames Feist if (!firstScan && findSensor != sensors.end())
3966714a25aSJames Feist {
3976714a25aSJames Feist bool found = false;
398d653b75cSBruce Mitchell auto it = sensorsChanged->begin();
399d653b75cSBruce Mitchell while (it != sensorsChanged->end())
4006714a25aSJames Feist {
4016c106d66SZev Weiss if (it->ends_with(findSensor->second->name))
4026714a25aSJames Feist {
403d653b75cSBruce Mitchell it = sensorsChanged->erase(it);
4046714a25aSJames Feist findSensor->second = nullptr;
4056714a25aSJames Feist found = true;
4066714a25aSJames Feist break;
4076714a25aSJames Feist }
408d653b75cSBruce Mitchell ++it;
4096714a25aSJames Feist }
4106714a25aSJames Feist if (!found)
4116714a25aSJames Feist {
4126714a25aSJames Feist continue;
4136714a25aSJames Feist }
4146714a25aSJames Feist }
4155636d52bSMatt Spinler
4166714a25aSJames Feist std::vector<thresholds::Threshold> sensorThresholds;
4175636d52bSMatt Spinler
4189f3a74edSJayashree Dhanapal if (!parseThresholdsFromConfig(sensorData, sensorThresholds,
4195636d52bSMatt Spinler nullptr, &index))
4206714a25aSJames Feist {
421bb67932aSEd Tanous std::cerr << "error populating thresholds for " << sensorName
422bb67932aSEd Tanous << " index " << index << "\n";
4236714a25aSJames Feist }
42487bc67f7SJeff Lin
4258569bf2aSZev Weiss float pollRate = getPollRate(baseConfigMap, pollRateDefault);
426a4d2768cSZev Weiss PowerState readState = getPowerState(baseConfigMap);
427100c20bfSJason Ling
4289f3a74edSJayashree Dhanapal auto permitSet = getPermitSet(baseConfigMap);
4298b3f7d40SAlex Qiu auto& sensor = sensors[sensorName];
430a1456c4aSZev Weiss if (!activateOnly)
431a1456c4aSZev Weiss {
4328b3f7d40SAlex Qiu sensor = nullptr;
433a1456c4aSZev Weiss }
434779c96a2SPatrick Williams auto hwmonFile = getFullHwmonFilePath(directory.string(), "temp1",
435779c96a2SPatrick Williams permitSet);
436544e7dc5SBruce Mitchell if (pathStr.starts_with("/sys/bus/iio/devices"))
437544e7dc5SBruce Mitchell {
438544e7dc5SBruce Mitchell hwmonFile = pathStr;
439544e7dc5SBruce Mitchell }
440100c20bfSJason Ling if (hwmonFile)
441100c20bfSJason Ling {
442a1456c4aSZev Weiss if (sensor != nullptr)
443a1456c4aSZev Weiss {
444a1456c4aSZev Weiss sensor->activate(*hwmonFile, i2cDev);
445a1456c4aSZev Weiss }
446a1456c4aSZev Weiss else
447a1456c4aSZev Weiss {
448f3fd1915SYong Li sensor = std::make_shared<HwmonTempSensor>(
449a1456c4aSZev Weiss *hwmonFile, sensorType, objectServer, dbusConnection,
450a1456c4aSZev Weiss io, sensorName, std::move(sensorThresholds),
451a1456c4aSZev Weiss thisSensorParameters, pollRate, interfacePath,
452a1456c4aSZev Weiss readState, i2cDev);
453f3fd1915SYong Li sensor->setupRead();
4549eb0cc3dSWilly Tu }
455a1456c4aSZev Weiss }
4569f3a74edSJayashree Dhanapal hwmonName.erase(
4579f3a74edSJayashree Dhanapal remove(hwmonName.begin(), hwmonName.end(), sensorName),
4589f3a74edSJayashree Dhanapal hwmonName.end());
4599eb0cc3dSWilly Tu
4608b3f7d40SAlex Qiu // Looking for keys like "Name1" for temp2_input,
4618b3f7d40SAlex Qiu // "Name2" for temp3_input, etc.
4628b3f7d40SAlex Qiu int i = 0;
4638b3f7d40SAlex Qiu while (true)
46437266ca9SJames Feist {
4658b3f7d40SAlex Qiu ++i;
466bb67932aSEd Tanous auto findKey = baseConfigMap.find("Name" + std::to_string(i));
4679f3a74edSJayashree Dhanapal if (findKey == baseConfigMap.end())
4688b3f7d40SAlex Qiu {
4698b3f7d40SAlex Qiu break;
47037266ca9SJames Feist }
471bb67932aSEd Tanous std::string sensorName = std::get<std::string>(findKey->second);
472bb67932aSEd Tanous hwmonFile = getFullHwmonFilePath(directory.string(),
473bb67932aSEd Tanous "temp" + std::to_string(i + 1),
474100c20bfSJason Ling permitSet);
475544e7dc5SBruce Mitchell if (pathStr.starts_with("/sys/bus/iio/devices"))
476544e7dc5SBruce Mitchell {
477544e7dc5SBruce Mitchell continue;
478544e7dc5SBruce Mitchell }
479100c20bfSJason Ling if (hwmonFile)
480100c20bfSJason Ling {
4815636d52bSMatt Spinler // To look up thresholds for these additional sensors,
4825636d52bSMatt Spinler // match on the Index property in the threshold data
4835636d52bSMatt Spinler // where the index comes from the sysfs file we're on,
4845636d52bSMatt Spinler // i.e. index = 2 for temp2_input.
4855636d52bSMatt Spinler int index = i + 1;
4865636d52bSMatt Spinler std::vector<thresholds::Threshold> thresholds;
4875636d52bSMatt Spinler
4889f3a74edSJayashree Dhanapal if (!parseThresholdsFromConfig(sensorData, thresholds,
4895636d52bSMatt Spinler nullptr, &index))
4905636d52bSMatt Spinler {
4915636d52bSMatt Spinler std::cerr << "error populating thresholds for "
492bb67932aSEd Tanous << sensorName << " index " << index << "\n";
4935636d52bSMatt Spinler }
4945636d52bSMatt Spinler
4958b3f7d40SAlex Qiu auto& sensor = sensors[sensorName];
496a1456c4aSZev Weiss if (!activateOnly)
497a1456c4aSZev Weiss {
4988b3f7d40SAlex Qiu sensor = nullptr;
499a1456c4aSZev Weiss }
500a1456c4aSZev Weiss
501a1456c4aSZev Weiss if (sensor != nullptr)
502a1456c4aSZev Weiss {
503a1456c4aSZev Weiss sensor->activate(*hwmonFile, i2cDev);
504a1456c4aSZev Weiss }
505a1456c4aSZev Weiss else
506a1456c4aSZev Weiss {
507f3fd1915SYong Li sensor = std::make_shared<HwmonTempSensor>(
508a1456c4aSZev Weiss *hwmonFile, sensorType, objectServer,
509a1456c4aSZev Weiss dbusConnection, io, sensorName,
510a1456c4aSZev Weiss std::move(thresholds), thisSensorParameters,
511a1456c4aSZev Weiss pollRate, interfacePath, readState, i2cDev);
512f3fd1915SYong Li sensor->setupRead();
5138b3f7d40SAlex Qiu }
514a1456c4aSZev Weiss }
5159eb0cc3dSWilly Tu
5169eb0cc3dSWilly Tu hwmonName.erase(
5179eb0cc3dSWilly Tu remove(hwmonName.begin(), hwmonName.end(), sensorName),
5189eb0cc3dSWilly Tu hwmonName.end());
5196714a25aSJames Feist }
52031ec7dbbSMatt Spinler if (hwmonName.empty())
52131ec7dbbSMatt Spinler {
5229f3a74edSJayashree Dhanapal configMap.erase(findSensorCfg);
5239f3a74edSJayashree Dhanapal }
52431ec7dbbSMatt Spinler }
5258a17c303SEd Tanous });
526d29f8aa0SZev Weiss std::vector<std::string> types(sensorTypes.size());
527d29f8aa0SZev Weiss for (const auto& [type, dt] : sensorTypes)
528d29f8aa0SZev Weiss {
529d29f8aa0SZev Weiss types.push_back(type);
530d29f8aa0SZev Weiss }
531d29f8aa0SZev Weiss getter->getConfiguration(types);
5326714a25aSJames Feist }
5336714a25aSJames Feist
interfaceRemoved(sdbusplus::message_t & message,boost::container::flat_map<std::string,std::shared_ptr<HwmonTempSensor>> & sensors)53420bf2c1cSMatt Spinler void interfaceRemoved(
53592f8f515SPatrick Williams sdbusplus::message_t& message,
53620bf2c1cSMatt Spinler boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>&
53720bf2c1cSMatt Spinler sensors)
53820bf2c1cSMatt Spinler {
53920bf2c1cSMatt Spinler if (message.is_method_error())
54020bf2c1cSMatt Spinler {
54120bf2c1cSMatt Spinler std::cerr << "interfacesRemoved callback method error\n";
54220bf2c1cSMatt Spinler return;
54320bf2c1cSMatt Spinler }
54420bf2c1cSMatt Spinler
54520bf2c1cSMatt Spinler sdbusplus::message::object_path path;
54620bf2c1cSMatt Spinler std::vector<std::string> interfaces;
54720bf2c1cSMatt Spinler
54820bf2c1cSMatt Spinler message.read(path, interfaces);
54920bf2c1cSMatt Spinler
55020bf2c1cSMatt Spinler // If the xyz.openbmc_project.Confguration.X interface was removed
55120bf2c1cSMatt Spinler // for one or more sensors, delete those sensor objects.
55220bf2c1cSMatt Spinler auto sensorIt = sensors.begin();
55320bf2c1cSMatt Spinler while (sensorIt != sensors.end())
55420bf2c1cSMatt Spinler {
5556289f5a2SPatrick Rudolph if (sensorIt->second && (sensorIt->second->configurationPath == path) &&
55620bf2c1cSMatt Spinler (std::find(interfaces.begin(), interfaces.end(),
55755832f37SMatt Spinler sensorIt->second->configInterface) != interfaces.end()))
55820bf2c1cSMatt Spinler {
55920bf2c1cSMatt Spinler sensorIt = sensors.erase(sensorIt);
56020bf2c1cSMatt Spinler }
56120bf2c1cSMatt Spinler else
56220bf2c1cSMatt Spinler {
56320bf2c1cSMatt Spinler sensorIt++;
56420bf2c1cSMatt Spinler }
56520bf2c1cSMatt Spinler }
56620bf2c1cSMatt Spinler }
56720bf2c1cSMatt Spinler
powerStateChanged(PowerState type,bool newState,boost::container::flat_map<std::string,std::shared_ptr<HwmonTempSensor>> & sensors,boost::asio::io_context & io,sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & dbusConnection)568a1456c4aSZev Weiss static void powerStateChanged(
569a1456c4aSZev Weiss PowerState type, bool newState,
570a1456c4aSZev Weiss boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>&
571a1456c4aSZev Weiss sensors,
5721f978631SEd Tanous boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer,
573a1456c4aSZev Weiss std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
574a1456c4aSZev Weiss {
575a1456c4aSZev Weiss if (newState)
576a1456c4aSZev Weiss {
577a1456c4aSZev Weiss createSensors(io, objectServer, sensors, dbusConnection, nullptr, true);
578a1456c4aSZev Weiss }
579a1456c4aSZev Weiss else
580a1456c4aSZev Weiss {
581a1456c4aSZev Weiss for (auto& [path, sensor] : sensors)
582a1456c4aSZev Weiss {
583a1456c4aSZev Weiss if (sensor != nullptr && sensor->readState == type)
584a1456c4aSZev Weiss {
585a1456c4aSZev Weiss sensor->deactivate();
586a1456c4aSZev Weiss }
587a1456c4aSZev Weiss }
588a1456c4aSZev Weiss }
589a1456c4aSZev Weiss }
590a1456c4aSZev Weiss
main()591b6c0b914SJames Feist int main()
5926714a25aSJames Feist {
5931f978631SEd Tanous boost::asio::io_context io;
5946714a25aSJames Feist auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
59514ed5e99SEd Tanous sdbusplus::asio::object_server objectServer(systemBus, true);
59614ed5e99SEd Tanous objectServer.add_manager("/xyz/openbmc_project/sensors");
5976714a25aSJames Feist systemBus->request_name("xyz.openbmc_project.HwmonTempSensor");
59814ed5e99SEd Tanous
599f3fd1915SYong Li boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>
6006714a25aSJames Feist sensors;
6015591cf08SJames Feist auto sensorsChanged =
6025591cf08SJames Feist std::make_shared<boost::container::flat_set<std::string>>();
6036714a25aSJames Feist
604a1456c4aSZev Weiss auto powerCallBack = [&sensors, &io, &objectServer,
605a1456c4aSZev Weiss &systemBus](PowerState type, bool state) {
606a1456c4aSZev Weiss powerStateChanged(type, state, sensors, io, objectServer, systemBus);
607a1456c4aSZev Weiss };
608a1456c4aSZev Weiss setupPowerMatchCallback(systemBus, powerCallBack);
609a1456c4aSZev Weiss
61083db50caSEd Tanous boost::asio::post(io, [&]() {
611a1456c4aSZev Weiss createSensors(io, objectServer, sensors, systemBus, nullptr, false);
6126714a25aSJames Feist });
6136714a25aSJames Feist
6149b4a20e9SEd Tanous boost::asio::steady_timer filterTimer(io);
61592f8f515SPatrick Williams std::function<void(sdbusplus::message_t&)> eventHandler =
61692f8f515SPatrick Williams [&](sdbusplus::message_t& message) {
6176714a25aSJames Feist if (message.is_method_error())
6186714a25aSJames Feist {
6196714a25aSJames Feist std::cerr << "callback method error\n";
6206714a25aSJames Feist return;
6216714a25aSJames Feist }
6226714a25aSJames Feist sensorsChanged->insert(message.get_path());
6236714a25aSJames Feist // this implicitly cancels the timer
62483db50caSEd Tanous filterTimer.expires_after(std::chrono::seconds(1));
6256714a25aSJames Feist
6266714a25aSJames Feist filterTimer.async_wait([&](const boost::system::error_code& ec) {
6276714a25aSJames Feist if (ec == boost::asio::error::operation_aborted)
6286714a25aSJames Feist {
6296714a25aSJames Feist /* we were canceled*/
6306714a25aSJames Feist return;
6316714a25aSJames Feist }
6328a57ec09SEd Tanous if (ec)
6336714a25aSJames Feist {
6346714a25aSJames Feist std::cerr << "timer error\n";
6356714a25aSJames Feist return;
6366714a25aSJames Feist }
637a1456c4aSZev Weiss createSensors(io, objectServer, sensors, systemBus, sensorsChanged,
638a1456c4aSZev Weiss false);
6396714a25aSJames Feist });
6406714a25aSJames Feist };
6416714a25aSJames Feist
642214d9717SZev Weiss std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches =
643214d9717SZev Weiss setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler);
6441263c3daSBruce Lee setupManufacturingModeMatch(*systemBus);
64520bf2c1cSMatt Spinler
64620bf2c1cSMatt Spinler // Watch for entity-manager to remove configuration interfaces
64720bf2c1cSMatt Spinler // so the corresponding sensors can be removed.
64892f8f515SPatrick Williams auto ifaceRemovedMatch = std::make_unique<sdbusplus::bus::match_t>(
64992f8f515SPatrick Williams static_cast<sdbusplus::bus_t&>(*systemBus),
65020bf2c1cSMatt Spinler "type='signal',member='InterfacesRemoved',arg0path='" +
65120bf2c1cSMatt Spinler std::string(inventoryPath) + "/'",
65292f8f515SPatrick Williams [&sensors](sdbusplus::message_t& msg) {
65320bf2c1cSMatt Spinler interfaceRemoved(msg, sensors);
65420bf2c1cSMatt Spinler });
65520bf2c1cSMatt Spinler
65620bf2c1cSMatt Spinler matches.emplace_back(std::move(ifaceRemovedMatch));
65720bf2c1cSMatt Spinler
6586714a25aSJames Feist io.run();
6596714a25aSJames Feist }
660