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}}, 76*9a7db6afSHieu Huynh {"TMP464", I2CDeviceType{"tmp464", true}}, 77d29f8aa0SZev Weiss {"TMP75", I2CDeviceType{"tmp75", true}}, 78d29f8aa0SZev Weiss {"W83773G", I2CDeviceType{"w83773g", true}}, 79d29f8aa0SZev Weiss }; 806714a25aSJames Feist 81544e7dc5SBruce Mitchell static struct SensorParams 82544e7dc5SBruce Mitchell getSensorParameters(const std::filesystem::path& path) 83544e7dc5SBruce Mitchell { 84544e7dc5SBruce Mitchell // offset is to default to 0 and scale to 1, see lore 85544e7dc5SBruce Mitchell // https://lore.kernel.org/linux-iio/5c79425f-6e88-36b6-cdfe-4080738d039f@metafoo.de/ 86779c96a2SPatrick Williams struct SensorParams tmpSensorParameters = { 87779c96a2SPatrick Williams .minValue = minValueTemperature, 88544e7dc5SBruce Mitchell .maxValue = maxValueTemperature, 89544e7dc5SBruce Mitchell .offsetValue = 0.0, 90544e7dc5SBruce Mitchell .scaleValue = 1.0, 91779c96a2SPatrick Williams .units = sensor_paths::unitDegreesC, 92544e7dc5SBruce Mitchell .typeName = "temperature"}; 93544e7dc5SBruce Mitchell 94544e7dc5SBruce Mitchell // For IIO RAW sensors we get a raw_value, an offset, and scale 95544e7dc5SBruce Mitchell // to compute the value = (raw_value + offset) * scale 96544e7dc5SBruce Mitchell // with a _raw IIO device we need to get the 97544e7dc5SBruce Mitchell // offsetValue and scaleValue from the driver 98544e7dc5SBruce Mitchell // these are used to compute the reading in 99544e7dc5SBruce Mitchell // units that have yet to be scaled for D-Bus. 100544e7dc5SBruce Mitchell const std::string pathStr = path.string(); 101544e7dc5SBruce Mitchell if (pathStr.ends_with("_raw")) 102544e7dc5SBruce Mitchell { 103779c96a2SPatrick Williams std::string pathOffsetStr = pathStr.substr(0, pathStr.size() - 4) + 104779c96a2SPatrick Williams "_offset"; 105544e7dc5SBruce Mitchell std::optional<double> tmpOffsetValue = readFile(pathOffsetStr, 1.0); 106544e7dc5SBruce Mitchell // In case there is nothing to read skip this device 107544e7dc5SBruce Mitchell // This is not an error condition see lore 108544e7dc5SBruce Mitchell // https://lore.kernel.org/linux-iio/5c79425f-6e88-36b6-cdfe-4080738d039f@metafoo.de/ 109544e7dc5SBruce Mitchell if (tmpOffsetValue) 110544e7dc5SBruce Mitchell { 111544e7dc5SBruce Mitchell tmpSensorParameters.offsetValue = *tmpOffsetValue; 112544e7dc5SBruce Mitchell } 113544e7dc5SBruce Mitchell 114779c96a2SPatrick Williams std::string pathScaleStr = pathStr.substr(0, pathStr.size() - 4) + 115779c96a2SPatrick Williams "_scale"; 116544e7dc5SBruce Mitchell std::optional<double> tmpScaleValue = readFile(pathScaleStr, 1.0); 117544e7dc5SBruce Mitchell // In case there is nothing to read skip this device 118544e7dc5SBruce Mitchell // This is not an error condition see lore 119544e7dc5SBruce Mitchell // https://lore.kernel.org/linux-iio/5c79425f-6e88-36b6-cdfe-4080738d039f@metafoo.de/ 120544e7dc5SBruce Mitchell if (tmpScaleValue) 121544e7dc5SBruce Mitchell { 122544e7dc5SBruce Mitchell tmpSensorParameters.scaleValue = *tmpScaleValue; 123544e7dc5SBruce Mitchell } 124544e7dc5SBruce Mitchell } 125544e7dc5SBruce Mitchell 126544e7dc5SBruce Mitchell // Temperatures are read in milli degrees Celsius, we need 127544e7dc5SBruce Mitchell // degrees Celsius. Pressures are read in kilopascal, we need 128544e7dc5SBruce Mitchell // Pascals. On D-Bus for Open BMC we use the International 129544e7dc5SBruce Mitchell // System of Units without prefixes. Links to the kernel 130544e7dc5SBruce Mitchell // documentation: 131544e7dc5SBruce Mitchell // https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface 132544e7dc5SBruce Mitchell // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-iio 133544e7dc5SBruce Mitchell if (path.filename() == "in_pressure_input" || 134544e7dc5SBruce Mitchell path.filename() == "in_pressure_raw") 135544e7dc5SBruce Mitchell { 136544e7dc5SBruce Mitchell tmpSensorParameters.minValue = minValuePressure; 137544e7dc5SBruce Mitchell tmpSensorParameters.maxValue = maxValuePressure; 138544e7dc5SBruce Mitchell // Pressures are read in kilopascal, we need Pascals. 139544e7dc5SBruce Mitchell tmpSensorParameters.scaleValue *= 1000.0; 140544e7dc5SBruce Mitchell tmpSensorParameters.typeName = "pressure"; 1415a86e562SBruce Mitchell tmpSensorParameters.units = sensor_paths::unitPascals; 142544e7dc5SBruce Mitchell } 1433ec41c53SBruce Mitchell else if (path.filename() == "in_humidityrelative_input" || 1443ec41c53SBruce Mitchell path.filename() == "in_humidityrelative_raw") 1453ec41c53SBruce Mitchell { 1463ec41c53SBruce Mitchell tmpSensorParameters.minValue = minValueRelativeHumidity; 1473ec41c53SBruce Mitchell tmpSensorParameters.maxValue = maxValueRelativeHumidity; 1483ec41c53SBruce Mitchell // Relative Humidity are read in milli-percent, we need percent. 1493ec41c53SBruce Mitchell tmpSensorParameters.scaleValue *= 0.001; 1503ec41c53SBruce Mitchell tmpSensorParameters.typeName = "humidity"; 1513ec41c53SBruce Mitchell tmpSensorParameters.units = "PercentRH"; 1523ec41c53SBruce Mitchell } 153544e7dc5SBruce Mitchell else 154544e7dc5SBruce Mitchell { 155544e7dc5SBruce Mitchell // Temperatures are read in milli degrees Celsius, 156544e7dc5SBruce Mitchell // we need degrees Celsius. 157544e7dc5SBruce Mitchell tmpSensorParameters.scaleValue *= 0.001; 158544e7dc5SBruce Mitchell } 159544e7dc5SBruce Mitchell 160544e7dc5SBruce Mitchell return tmpSensorParameters; 161544e7dc5SBruce Mitchell } 162544e7dc5SBruce Mitchell 1639f3a74edSJayashree Dhanapal struct SensorConfigKey 1649f3a74edSJayashree Dhanapal { 1659f3a74edSJayashree Dhanapal uint64_t bus; 1669f3a74edSJayashree Dhanapal uint64_t addr; 1679f3a74edSJayashree Dhanapal bool operator<(const SensorConfigKey& other) const 1689f3a74edSJayashree Dhanapal { 1699f3a74edSJayashree Dhanapal if (bus != other.bus) 1709f3a74edSJayashree Dhanapal { 1719f3a74edSJayashree Dhanapal return bus < other.bus; 1729f3a74edSJayashree Dhanapal } 1739f3a74edSJayashree Dhanapal return addr < other.addr; 1749f3a74edSJayashree Dhanapal } 1759f3a74edSJayashree Dhanapal }; 1769f3a74edSJayashree Dhanapal 1779f3a74edSJayashree Dhanapal struct SensorConfig 1789f3a74edSJayashree Dhanapal { 1799f3a74edSJayashree Dhanapal std::string sensorPath; 1809f3a74edSJayashree Dhanapal SensorData sensorData; 1819f3a74edSJayashree Dhanapal std::string interface; 1829f3a74edSJayashree Dhanapal SensorBaseConfigMap config; 1839f3a74edSJayashree Dhanapal std::vector<std::string> name; 1849f3a74edSJayashree Dhanapal }; 1859f3a74edSJayashree Dhanapal 1869f3a74edSJayashree Dhanapal using SensorConfigMap = 1879f3a74edSJayashree Dhanapal boost::container::flat_map<SensorConfigKey, SensorConfig>; 1889f3a74edSJayashree Dhanapal 1899f3a74edSJayashree Dhanapal static SensorConfigMap 1909f3a74edSJayashree Dhanapal buildSensorConfigMap(const ManagedObjectType& sensorConfigs) 1919f3a74edSJayashree Dhanapal { 1929f3a74edSJayashree Dhanapal SensorConfigMap configMap; 193a1808330SZev Weiss for (const auto& [path, cfgData] : sensorConfigs) 1949f3a74edSJayashree Dhanapal { 195a1808330SZev Weiss for (const auto& [intf, cfg] : cfgData) 1969f3a74edSJayashree Dhanapal { 1979f3a74edSJayashree Dhanapal auto busCfg = cfg.find("Bus"); 1989f3a74edSJayashree Dhanapal auto addrCfg = cfg.find("Address"); 1999f3a74edSJayashree Dhanapal if ((busCfg == cfg.end()) || (addrCfg == cfg.end())) 2009f3a74edSJayashree Dhanapal { 2019f3a74edSJayashree Dhanapal continue; 2029f3a74edSJayashree Dhanapal } 2039f3a74edSJayashree Dhanapal 2042049bd26SEd Tanous if ((std::get_if<uint64_t>(&busCfg->second) == nullptr) || 2052049bd26SEd Tanous (std::get_if<uint64_t>(&addrCfg->second) == nullptr)) 2069f3a74edSJayashree Dhanapal { 207a1808330SZev Weiss std::cerr << path.str << " Bus or Address invalid\n"; 2089f3a74edSJayashree Dhanapal continue; 2099f3a74edSJayashree Dhanapal } 2109f3a74edSJayashree Dhanapal 2119f3a74edSJayashree Dhanapal std::vector<std::string> hwmonNames; 2129f3a74edSJayashree Dhanapal auto nameCfg = cfg.find("Name"); 2139f3a74edSJayashree Dhanapal if (nameCfg != cfg.end()) 2149f3a74edSJayashree Dhanapal { 2159f3a74edSJayashree Dhanapal hwmonNames.push_back(std::get<std::string>(nameCfg->second)); 2169f3a74edSJayashree Dhanapal size_t i = 1; 2179f3a74edSJayashree Dhanapal while (true) 2189f3a74edSJayashree Dhanapal { 2199f3a74edSJayashree Dhanapal auto sensorNameCfg = cfg.find("Name" + std::to_string(i)); 2209f3a74edSJayashree Dhanapal if (sensorNameCfg == cfg.end()) 2219f3a74edSJayashree Dhanapal { 2229f3a74edSJayashree Dhanapal break; 2239f3a74edSJayashree Dhanapal } 2249f3a74edSJayashree Dhanapal hwmonNames.push_back( 2259f3a74edSJayashree Dhanapal std::get<std::string>(sensorNameCfg->second)); 2269f3a74edSJayashree Dhanapal i++; 2279f3a74edSJayashree Dhanapal } 2289f3a74edSJayashree Dhanapal } 2299f3a74edSJayashree Dhanapal 2309f3a74edSJayashree Dhanapal SensorConfigKey key = {std::get<uint64_t>(busCfg->second), 2319f3a74edSJayashree Dhanapal std::get<uint64_t>(addrCfg->second)}; 232a1808330SZev Weiss SensorConfig val = {path.str, cfgData, intf, cfg, hwmonNames}; 2339f3a74edSJayashree Dhanapal 2349f3a74edSJayashree Dhanapal auto [it, inserted] = configMap.emplace(key, std::move(val)); 2359f3a74edSJayashree Dhanapal if (!inserted) 2369f3a74edSJayashree Dhanapal { 237a1808330SZev Weiss std::cerr << path.str << ": ignoring duplicate entry for {" 238a1808330SZev Weiss << key.bus << ", 0x" << std::hex << key.addr 239a1808330SZev Weiss << std::dec << "}\n"; 2409f3a74edSJayashree Dhanapal } 2419f3a74edSJayashree Dhanapal } 2429f3a74edSJayashree Dhanapal } 2439f3a74edSJayashree Dhanapal return configMap; 2449f3a74edSJayashree Dhanapal } 2459f3a74edSJayashree Dhanapal 2467627c860SZev Weiss // returns a {path: <I2CDevice, is_new>} map. is_new indicates if the I2CDevice 2477627c860SZev Weiss // is newly instantiated by this call (true) or was already there (false). 2487627c860SZev Weiss boost::container::flat_map<std::string, 2497627c860SZev Weiss std::pair<std::shared_ptr<I2CDevice>, bool>> 250a1456c4aSZev Weiss instantiateDevices( 251a1456c4aSZev Weiss const ManagedObjectType& sensorConfigs, 252a1456c4aSZev Weiss const boost::container::flat_map< 253a1456c4aSZev Weiss std::string, std::shared_ptr<HwmonTempSensor>>& sensors) 254a1456c4aSZev Weiss { 2557627c860SZev Weiss boost::container::flat_map<std::string, 2567627c860SZev Weiss std::pair<std::shared_ptr<I2CDevice>, bool>> 2577627c860SZev Weiss devices; 258a1456c4aSZev Weiss for (const auto& [path, sensor] : sensorConfigs) 259a1456c4aSZev Weiss { 260a1456c4aSZev Weiss for (const auto& [name, cfg] : sensor) 261a1456c4aSZev Weiss { 262a1456c4aSZev Weiss PowerState powerState = getPowerState(cfg); 263a1456c4aSZev Weiss if (!readingStateGood(powerState)) 264a1456c4aSZev Weiss { 265a1456c4aSZev Weiss continue; 266a1456c4aSZev Weiss } 267a1456c4aSZev Weiss 268a1456c4aSZev Weiss auto findSensorName = cfg.find("Name"); 269a1456c4aSZev Weiss if (findSensorName == cfg.end()) 270a1456c4aSZev Weiss { 271a1456c4aSZev Weiss continue; 272a1456c4aSZev Weiss } 273a1456c4aSZev Weiss std::string sensorName = 274a1456c4aSZev Weiss std::get<std::string>(findSensorName->second); 275a1456c4aSZev Weiss 276a1456c4aSZev Weiss auto findSensor = sensors.find(sensorName); 277a1456c4aSZev Weiss if (findSensor != sensors.end() && findSensor->second != nullptr && 278a1456c4aSZev Weiss findSensor->second->isActive()) 279a1456c4aSZev Weiss { 2807627c860SZev Weiss devices.emplace( 2817627c860SZev Weiss path.str, 2827627c860SZev Weiss std::make_pair(findSensor->second->getI2CDevice(), false)); 283a1456c4aSZev Weiss continue; 284a1456c4aSZev Weiss } 285a1456c4aSZev Weiss 286a1456c4aSZev Weiss std::optional<I2CDeviceParams> params = 287a1456c4aSZev Weiss getI2CDeviceParams(sensorTypes, cfg); 288a1456c4aSZev Weiss if (params.has_value() && !params->deviceStatic()) 289a1456c4aSZev Weiss { 290a1456c4aSZev Weiss // There exist error cases in which a sensor device that we 291a1456c4aSZev Weiss // need is already instantiated, but needs to be destroyed and 292a1456c4aSZev Weiss // re-created in order to be useful (for example if we crash 293a1456c4aSZev Weiss // after instantiating a device and the sensor device's power 294a1456c4aSZev Weiss // is cut before we get restarted, leaving it "present" but 295a1456c4aSZev Weiss // not really usable). To be on the safe side, instantiate a 296a1456c4aSZev Weiss // temporary device that's immediately destroyed so as to 297a1456c4aSZev Weiss // ensure that we end up with a fresh instance of it. 298a1456c4aSZev Weiss if (params->devicePresent()) 299a1456c4aSZev Weiss { 300a1456c4aSZev Weiss std::cerr << "Clearing out previous instance for " 301a1456c4aSZev Weiss << path.str << "\n"; 302a1456c4aSZev Weiss I2CDevice tmp(*params); 303a1456c4aSZev Weiss } 304a1456c4aSZev Weiss 305a1456c4aSZev Weiss try 306a1456c4aSZev Weiss { 3077627c860SZev Weiss devices.emplace( 3087627c860SZev Weiss path.str, 3097627c860SZev Weiss std::make_pair(std::make_shared<I2CDevice>(*params), 3107627c860SZev Weiss true)); 311a1456c4aSZev Weiss } 312a1456c4aSZev Weiss catch (std::runtime_error&) 313a1456c4aSZev Weiss { 314a1456c4aSZev Weiss std::cerr << "Failed to instantiate " << params->type->name 315a1456c4aSZev Weiss << " at address " << params->address << " on bus " 316a1456c4aSZev Weiss << params->bus << "\n"; 317a1456c4aSZev Weiss } 318a1456c4aSZev Weiss } 319a1456c4aSZev Weiss } 320a1456c4aSZev Weiss } 3217627c860SZev Weiss return devices; 322a1456c4aSZev Weiss } 323a1456c4aSZev Weiss 3246714a25aSJames Feist void createSensors( 3251f978631SEd Tanous boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer, 326f3fd1915SYong Li boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>& 3276714a25aSJames Feist sensors, 3286714a25aSJames Feist std::shared_ptr<sdbusplus::asio::connection>& dbusConnection, 3295591cf08SJames Feist const std::shared_ptr<boost::container::flat_set<std::string>>& 330a1456c4aSZev Weiss sensorsChanged, 331a1456c4aSZev Weiss bool activateOnly) 3326714a25aSJames Feist { 333df515159SJames Feist auto getter = std::make_shared<GetSensorConfiguration>( 334df515159SJames Feist dbusConnection, 335a1456c4aSZev Weiss [&io, &objectServer, &sensors, &dbusConnection, sensorsChanged, 336a1456c4aSZev Weiss activateOnly](const ManagedObjectType& sensorConfigurations) { 3376714a25aSJames Feist bool firstScan = sensorsChanged == nullptr; 338df515159SJames Feist 339bb67932aSEd Tanous SensorConfigMap configMap = buildSensorConfigMap(sensorConfigurations); 3409f3a74edSJayashree Dhanapal 3417627c860SZev Weiss auto devices = instantiateDevices(sensorConfigurations, sensors); 342a1456c4aSZev Weiss 343544e7dc5SBruce Mitchell // IIO _raw devices look like this on sysfs: 344544e7dc5SBruce Mitchell // /sys/bus/iio/devices/iio:device0/in_temp_raw 345544e7dc5SBruce Mitchell // /sys/bus/iio/devices/iio:device0/in_temp_offset 346544e7dc5SBruce Mitchell // /sys/bus/iio/devices/iio:device0/in_temp_scale 347544e7dc5SBruce Mitchell // 348544e7dc5SBruce Mitchell // Other IIO devices look like this on sysfs: 349544e7dc5SBruce Mitchell // /sys/bus/iio/devices/iio:device1/in_temp_input 350544e7dc5SBruce Mitchell // /sys/bus/iio/devices/iio:device1/in_pressure_input 3516714a25aSJames Feist std::vector<fs::path> paths; 352544e7dc5SBruce Mitchell fs::path root("/sys/bus/iio/devices"); 353544e7dc5SBruce Mitchell findFiles(root, R"(in_temp\d*_(input|raw))", paths); 354544e7dc5SBruce Mitchell findFiles(root, R"(in_pressure\d*_(input|raw))", paths); 3553ec41c53SBruce Mitchell findFiles(root, R"(in_humidityrelative\d*_(input|raw))", paths); 356544e7dc5SBruce Mitchell findFiles(fs::path("/sys/class/hwmon"), R"(temp\d+_input)", paths); 357544e7dc5SBruce Mitchell 358544e7dc5SBruce Mitchell // iterate through all found temp and pressure sensors, 359544e7dc5SBruce Mitchell // and try to match them with configuration 3606714a25aSJames Feist for (auto& path : paths) 3616714a25aSJames Feist { 3626714a25aSJames Feist std::smatch match; 363544e7dc5SBruce Mitchell const std::string pathStr = path.string(); 3646714a25aSJames Feist auto directory = path.parent_path(); 365544e7dc5SBruce Mitchell fs::path device; 3666714a25aSJames Feist 367544e7dc5SBruce Mitchell std::string deviceName; 368544e7dc5SBruce Mitchell if (pathStr.starts_with("/sys/bus/iio/devices")) 3696714a25aSJames Feist { 370544e7dc5SBruce Mitchell device = fs::canonical(directory); 371544e7dc5SBruce Mitchell deviceName = device.parent_path().stem(); 37237266ca9SJames Feist } 373544e7dc5SBruce Mitchell else 374544e7dc5SBruce Mitchell { 375544e7dc5SBruce Mitchell device = directory / "device"; 376544e7dc5SBruce Mitchell deviceName = fs::canonical(device).stem(); 377544e7dc5SBruce Mitchell } 3788a57ec09SEd Tanous auto findHyphen = deviceName.find('-'); 37937266ca9SJames Feist if (findHyphen == std::string::npos) 38037266ca9SJames Feist { 38137266ca9SJames Feist std::cerr << "found bad device " << deviceName << "\n"; 3826714a25aSJames Feist continue; 3836714a25aSJames Feist } 38437266ca9SJames Feist std::string busStr = deviceName.substr(0, findHyphen); 38537266ca9SJames Feist std::string addrStr = deviceName.substr(findHyphen + 1); 38637266ca9SJames Feist 3877dd6443bSJae Hyun Yoo uint64_t bus = 0; 3887dd6443bSJae Hyun Yoo uint64_t addr = 0; 3892049bd26SEd Tanous std::from_chars_result res{}; 390bb67932aSEd Tanous res = std::from_chars(busStr.data(), busStr.data() + busStr.size(), 391bb67932aSEd Tanous bus); 3927dd6443bSJae Hyun Yoo if (res.ec != std::errc{}) 3936714a25aSJames Feist { 3947dd6443bSJae Hyun Yoo continue; 39537266ca9SJames Feist } 396bb67932aSEd Tanous res = std::from_chars(addrStr.data(), 397bb67932aSEd Tanous addrStr.data() + addrStr.size(), addr, 16); 3987dd6443bSJae Hyun Yoo if (res.ec != std::errc{}) 39937266ca9SJames Feist { 4006714a25aSJames Feist continue; 4016714a25aSJames Feist } 40237266ca9SJames Feist 403544e7dc5SBruce Mitchell auto thisSensorParameters = getSensorParameters(path); 4049f3a74edSJayashree Dhanapal auto findSensorCfg = configMap.find({bus, addr}); 4059f3a74edSJayashree Dhanapal if (findSensorCfg == configMap.end()) 40637266ca9SJames Feist { 40737266ca9SJames Feist continue; 40837266ca9SJames Feist } 40937266ca9SJames Feist 410bb67932aSEd Tanous const std::string& interfacePath = findSensorCfg->second.sensorPath; 4117627c860SZev Weiss auto findI2CDev = devices.find(interfacePath); 4127627c860SZev Weiss 413a1456c4aSZev Weiss std::shared_ptr<I2CDevice> i2cDev; 4147627c860SZev Weiss if (findI2CDev != devices.end()) 415a1456c4aSZev Weiss { 4169bbeff75SJoseph Fu // If we're only looking to activate newly-instantiated i2c 4179bbeff75SJoseph Fu // devices and this sensor's underlying device was already there 4189bbeff75SJoseph Fu // before this call, there's nothing more to do here. 4199bbeff75SJoseph Fu if (activateOnly && !findI2CDev->second.second) 4209bbeff75SJoseph Fu { 4219bbeff75SJoseph Fu continue; 4229bbeff75SJoseph Fu } 4237627c860SZev Weiss i2cDev = findI2CDev->second.first; 424a1456c4aSZev Weiss } 425a1456c4aSZev Weiss 4269f3a74edSJayashree Dhanapal const SensorData& sensorData = findSensorCfg->second.sensorData; 4270489ec20SMatt Spinler std::string sensorType = findSensorCfg->second.interface; 4280489ec20SMatt Spinler auto pos = sensorType.find_last_of('.'); 4290489ec20SMatt Spinler if (pos != std::string::npos) 4300489ec20SMatt Spinler { 4310489ec20SMatt Spinler sensorType = sensorType.substr(pos + 1); 4320489ec20SMatt Spinler } 4339f3a74edSJayashree Dhanapal const SensorBaseConfigMap& baseConfigMap = 4349f3a74edSJayashree Dhanapal findSensorCfg->second.config; 435bb67932aSEd Tanous std::vector<std::string>& hwmonName = findSensorCfg->second.name; 4366714a25aSJames Feist 437544e7dc5SBruce Mitchell // Temperature has "Name", pressure has "Name1" 4389f3a74edSJayashree Dhanapal auto findSensorName = baseConfigMap.find("Name"); 439fefdbe7fSPotin Lai int index = 1; 4403ec41c53SBruce Mitchell if (thisSensorParameters.typeName == "pressure" || 4413ec41c53SBruce Mitchell thisSensorParameters.typeName == "humidity") 442544e7dc5SBruce Mitchell { 4439f3a74edSJayashree Dhanapal findSensorName = baseConfigMap.find("Name1"); 444fefdbe7fSPotin Lai index = 2; 445544e7dc5SBruce Mitchell } 446544e7dc5SBruce Mitchell 4479f3a74edSJayashree Dhanapal if (findSensorName == baseConfigMap.end()) 4486714a25aSJames Feist { 4496714a25aSJames Feist std::cerr << "could not determine configuration name for " 45037266ca9SJames Feist << deviceName << "\n"; 4516714a25aSJames Feist continue; 4526714a25aSJames Feist } 453df515159SJames Feist std::string sensorName = 454df515159SJames Feist std::get<std::string>(findSensorName->second); 4556714a25aSJames Feist // on rescans, only update sensors we were signaled by 4566714a25aSJames Feist auto findSensor = sensors.find(sensorName); 4576714a25aSJames Feist if (!firstScan && findSensor != sensors.end()) 4586714a25aSJames Feist { 4596714a25aSJames Feist bool found = false; 460d653b75cSBruce Mitchell auto it = sensorsChanged->begin(); 461d653b75cSBruce Mitchell while (it != sensorsChanged->end()) 4626714a25aSJames Feist { 4636c106d66SZev Weiss if (it->ends_with(findSensor->second->name)) 4646714a25aSJames Feist { 465d653b75cSBruce Mitchell it = sensorsChanged->erase(it); 4666714a25aSJames Feist findSensor->second = nullptr; 4676714a25aSJames Feist found = true; 4686714a25aSJames Feist break; 4696714a25aSJames Feist } 470d653b75cSBruce Mitchell ++it; 4716714a25aSJames Feist } 4726714a25aSJames Feist if (!found) 4736714a25aSJames Feist { 4746714a25aSJames Feist continue; 4756714a25aSJames Feist } 4766714a25aSJames Feist } 4775636d52bSMatt Spinler 4786714a25aSJames Feist std::vector<thresholds::Threshold> sensorThresholds; 4795636d52bSMatt Spinler 4809f3a74edSJayashree Dhanapal if (!parseThresholdsFromConfig(sensorData, sensorThresholds, 4815636d52bSMatt Spinler nullptr, &index)) 4826714a25aSJames Feist { 483bb67932aSEd Tanous std::cerr << "error populating thresholds for " << sensorName 484bb67932aSEd Tanous << " index " << index << "\n"; 4856714a25aSJames Feist } 48687bc67f7SJeff Lin 4878569bf2aSZev Weiss float pollRate = getPollRate(baseConfigMap, pollRateDefault); 488a4d2768cSZev Weiss PowerState readState = getPowerState(baseConfigMap); 489100c20bfSJason Ling 4909f3a74edSJayashree Dhanapal auto permitSet = getPermitSet(baseConfigMap); 4918b3f7d40SAlex Qiu auto& sensor = sensors[sensorName]; 492a1456c4aSZev Weiss if (!activateOnly) 493a1456c4aSZev Weiss { 4948b3f7d40SAlex Qiu sensor = nullptr; 495a1456c4aSZev Weiss } 496779c96a2SPatrick Williams auto hwmonFile = getFullHwmonFilePath(directory.string(), "temp1", 497779c96a2SPatrick Williams permitSet); 498544e7dc5SBruce Mitchell if (pathStr.starts_with("/sys/bus/iio/devices")) 499544e7dc5SBruce Mitchell { 500544e7dc5SBruce Mitchell hwmonFile = pathStr; 501544e7dc5SBruce Mitchell } 502100c20bfSJason Ling if (hwmonFile) 503100c20bfSJason Ling { 504a1456c4aSZev Weiss if (sensor != nullptr) 505a1456c4aSZev Weiss { 506a1456c4aSZev Weiss sensor->activate(*hwmonFile, i2cDev); 507a1456c4aSZev Weiss } 508a1456c4aSZev Weiss else 509a1456c4aSZev Weiss { 510f3fd1915SYong Li sensor = std::make_shared<HwmonTempSensor>( 511a1456c4aSZev Weiss *hwmonFile, sensorType, objectServer, dbusConnection, 512a1456c4aSZev Weiss io, sensorName, std::move(sensorThresholds), 513a1456c4aSZev Weiss thisSensorParameters, pollRate, interfacePath, 514a1456c4aSZev Weiss readState, i2cDev); 515f3fd1915SYong Li sensor->setupRead(); 5169eb0cc3dSWilly Tu } 517a1456c4aSZev Weiss } 5189f3a74edSJayashree Dhanapal hwmonName.erase( 5199f3a74edSJayashree Dhanapal remove(hwmonName.begin(), hwmonName.end(), sensorName), 5209f3a74edSJayashree Dhanapal hwmonName.end()); 5219eb0cc3dSWilly Tu 5228b3f7d40SAlex Qiu // Looking for keys like "Name1" for temp2_input, 5238b3f7d40SAlex Qiu // "Name2" for temp3_input, etc. 5248b3f7d40SAlex Qiu int i = 0; 5258b3f7d40SAlex Qiu while (true) 52637266ca9SJames Feist { 5278b3f7d40SAlex Qiu ++i; 528bb67932aSEd Tanous auto findKey = baseConfigMap.find("Name" + std::to_string(i)); 5299f3a74edSJayashree Dhanapal if (findKey == baseConfigMap.end()) 5308b3f7d40SAlex Qiu { 5318b3f7d40SAlex Qiu break; 53237266ca9SJames Feist } 533bb67932aSEd Tanous std::string sensorName = std::get<std::string>(findKey->second); 534bb67932aSEd Tanous hwmonFile = getFullHwmonFilePath(directory.string(), 535bb67932aSEd Tanous "temp" + std::to_string(i + 1), 536100c20bfSJason Ling permitSet); 537544e7dc5SBruce Mitchell if (pathStr.starts_with("/sys/bus/iio/devices")) 538544e7dc5SBruce Mitchell { 539544e7dc5SBruce Mitchell continue; 540544e7dc5SBruce Mitchell } 541100c20bfSJason Ling if (hwmonFile) 542100c20bfSJason Ling { 5435636d52bSMatt Spinler // To look up thresholds for these additional sensors, 5445636d52bSMatt Spinler // match on the Index property in the threshold data 5455636d52bSMatt Spinler // where the index comes from the sysfs file we're on, 5465636d52bSMatt Spinler // i.e. index = 2 for temp2_input. 5475636d52bSMatt Spinler int index = i + 1; 5485636d52bSMatt Spinler std::vector<thresholds::Threshold> thresholds; 5495636d52bSMatt Spinler 5509f3a74edSJayashree Dhanapal if (!parseThresholdsFromConfig(sensorData, thresholds, 5515636d52bSMatt Spinler nullptr, &index)) 5525636d52bSMatt Spinler { 5535636d52bSMatt Spinler std::cerr << "error populating thresholds for " 554bb67932aSEd Tanous << sensorName << " index " << index << "\n"; 5555636d52bSMatt Spinler } 5565636d52bSMatt Spinler 5578b3f7d40SAlex Qiu auto& sensor = sensors[sensorName]; 558a1456c4aSZev Weiss if (!activateOnly) 559a1456c4aSZev Weiss { 5608b3f7d40SAlex Qiu sensor = nullptr; 561a1456c4aSZev Weiss } 562a1456c4aSZev Weiss 563a1456c4aSZev Weiss if (sensor != nullptr) 564a1456c4aSZev Weiss { 565a1456c4aSZev Weiss sensor->activate(*hwmonFile, i2cDev); 566a1456c4aSZev Weiss } 567a1456c4aSZev Weiss else 568a1456c4aSZev Weiss { 569f3fd1915SYong Li sensor = std::make_shared<HwmonTempSensor>( 570a1456c4aSZev Weiss *hwmonFile, sensorType, objectServer, 571a1456c4aSZev Weiss dbusConnection, io, sensorName, 572a1456c4aSZev Weiss std::move(thresholds), thisSensorParameters, 573a1456c4aSZev Weiss pollRate, interfacePath, readState, i2cDev); 574f3fd1915SYong Li sensor->setupRead(); 5758b3f7d40SAlex Qiu } 576a1456c4aSZev Weiss } 5779eb0cc3dSWilly Tu 5789eb0cc3dSWilly Tu hwmonName.erase( 5799eb0cc3dSWilly Tu remove(hwmonName.begin(), hwmonName.end(), sensorName), 5809eb0cc3dSWilly Tu hwmonName.end()); 5816714a25aSJames Feist } 58231ec7dbbSMatt Spinler if (hwmonName.empty()) 58331ec7dbbSMatt Spinler { 5849f3a74edSJayashree Dhanapal configMap.erase(findSensorCfg); 5859f3a74edSJayashree Dhanapal } 58631ec7dbbSMatt Spinler } 5878a17c303SEd Tanous }); 588d29f8aa0SZev Weiss std::vector<std::string> types(sensorTypes.size()); 589d29f8aa0SZev Weiss for (const auto& [type, dt] : sensorTypes) 590d29f8aa0SZev Weiss { 591d29f8aa0SZev Weiss types.push_back(type); 592d29f8aa0SZev Weiss } 593d29f8aa0SZev Weiss getter->getConfiguration(types); 5946714a25aSJames Feist } 5956714a25aSJames Feist 59620bf2c1cSMatt Spinler void interfaceRemoved( 59792f8f515SPatrick Williams sdbusplus::message_t& message, 59820bf2c1cSMatt Spinler boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>& 59920bf2c1cSMatt Spinler sensors) 60020bf2c1cSMatt Spinler { 60120bf2c1cSMatt Spinler if (message.is_method_error()) 60220bf2c1cSMatt Spinler { 60320bf2c1cSMatt Spinler std::cerr << "interfacesRemoved callback method error\n"; 60420bf2c1cSMatt Spinler return; 60520bf2c1cSMatt Spinler } 60620bf2c1cSMatt Spinler 60720bf2c1cSMatt Spinler sdbusplus::message::object_path path; 60820bf2c1cSMatt Spinler std::vector<std::string> interfaces; 60920bf2c1cSMatt Spinler 61020bf2c1cSMatt Spinler message.read(path, interfaces); 61120bf2c1cSMatt Spinler 61220bf2c1cSMatt Spinler // If the xyz.openbmc_project.Confguration.X interface was removed 61320bf2c1cSMatt Spinler // for one or more sensors, delete those sensor objects. 61420bf2c1cSMatt Spinler auto sensorIt = sensors.begin(); 61520bf2c1cSMatt Spinler while (sensorIt != sensors.end()) 61620bf2c1cSMatt Spinler { 61720bf2c1cSMatt Spinler if ((sensorIt->second->configurationPath == path) && 61820bf2c1cSMatt Spinler (std::find(interfaces.begin(), interfaces.end(), 61955832f37SMatt Spinler sensorIt->second->configInterface) != interfaces.end())) 62020bf2c1cSMatt Spinler { 62120bf2c1cSMatt Spinler sensorIt = sensors.erase(sensorIt); 62220bf2c1cSMatt Spinler } 62320bf2c1cSMatt Spinler else 62420bf2c1cSMatt Spinler { 62520bf2c1cSMatt Spinler sensorIt++; 62620bf2c1cSMatt Spinler } 62720bf2c1cSMatt Spinler } 62820bf2c1cSMatt Spinler } 62920bf2c1cSMatt Spinler 630a1456c4aSZev Weiss static void powerStateChanged( 631a1456c4aSZev Weiss PowerState type, bool newState, 632a1456c4aSZev Weiss boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>>& 633a1456c4aSZev Weiss sensors, 6341f978631SEd Tanous boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer, 635a1456c4aSZev Weiss std::shared_ptr<sdbusplus::asio::connection>& dbusConnection) 636a1456c4aSZev Weiss { 637a1456c4aSZev Weiss if (newState) 638a1456c4aSZev Weiss { 639a1456c4aSZev Weiss createSensors(io, objectServer, sensors, dbusConnection, nullptr, true); 640a1456c4aSZev Weiss } 641a1456c4aSZev Weiss else 642a1456c4aSZev Weiss { 643a1456c4aSZev Weiss for (auto& [path, sensor] : sensors) 644a1456c4aSZev Weiss { 645a1456c4aSZev Weiss if (sensor != nullptr && sensor->readState == type) 646a1456c4aSZev Weiss { 647a1456c4aSZev Weiss sensor->deactivate(); 648a1456c4aSZev Weiss } 649a1456c4aSZev Weiss } 650a1456c4aSZev Weiss } 651a1456c4aSZev Weiss } 652a1456c4aSZev Weiss 653b6c0b914SJames Feist int main() 6546714a25aSJames Feist { 6551f978631SEd Tanous boost::asio::io_context io; 6566714a25aSJames Feist auto systemBus = std::make_shared<sdbusplus::asio::connection>(io); 65714ed5e99SEd Tanous sdbusplus::asio::object_server objectServer(systemBus, true); 65814ed5e99SEd Tanous objectServer.add_manager("/xyz/openbmc_project/sensors"); 6596714a25aSJames Feist systemBus->request_name("xyz.openbmc_project.HwmonTempSensor"); 66014ed5e99SEd Tanous 661f3fd1915SYong Li boost::container::flat_map<std::string, std::shared_ptr<HwmonTempSensor>> 6626714a25aSJames Feist sensors; 6635591cf08SJames Feist auto sensorsChanged = 6645591cf08SJames Feist std::make_shared<boost::container::flat_set<std::string>>(); 6656714a25aSJames Feist 666a1456c4aSZev Weiss auto powerCallBack = [&sensors, &io, &objectServer, 667a1456c4aSZev Weiss &systemBus](PowerState type, bool state) { 668a1456c4aSZev Weiss powerStateChanged(type, state, sensors, io, objectServer, systemBus); 669a1456c4aSZev Weiss }; 670a1456c4aSZev Weiss setupPowerMatchCallback(systemBus, powerCallBack); 671a1456c4aSZev Weiss 67283db50caSEd Tanous boost::asio::post(io, [&]() { 673a1456c4aSZev Weiss createSensors(io, objectServer, sensors, systemBus, nullptr, false); 6746714a25aSJames Feist }); 6756714a25aSJames Feist 6769b4a20e9SEd Tanous boost::asio::steady_timer filterTimer(io); 67792f8f515SPatrick Williams std::function<void(sdbusplus::message_t&)> eventHandler = 67892f8f515SPatrick Williams [&](sdbusplus::message_t& message) { 6796714a25aSJames Feist if (message.is_method_error()) 6806714a25aSJames Feist { 6816714a25aSJames Feist std::cerr << "callback method error\n"; 6826714a25aSJames Feist return; 6836714a25aSJames Feist } 6846714a25aSJames Feist sensorsChanged->insert(message.get_path()); 6856714a25aSJames Feist // this implicitly cancels the timer 68683db50caSEd Tanous filterTimer.expires_after(std::chrono::seconds(1)); 6876714a25aSJames Feist 6886714a25aSJames Feist filterTimer.async_wait([&](const boost::system::error_code& ec) { 6896714a25aSJames Feist if (ec == boost::asio::error::operation_aborted) 6906714a25aSJames Feist { 6916714a25aSJames Feist /* we were canceled*/ 6926714a25aSJames Feist return; 6936714a25aSJames Feist } 6948a57ec09SEd Tanous if (ec) 6956714a25aSJames Feist { 6966714a25aSJames Feist std::cerr << "timer error\n"; 6976714a25aSJames Feist return; 6986714a25aSJames Feist } 699a1456c4aSZev Weiss createSensors(io, objectServer, sensors, systemBus, sensorsChanged, 700a1456c4aSZev Weiss false); 7016714a25aSJames Feist }); 7026714a25aSJames Feist }; 7036714a25aSJames Feist 704214d9717SZev Weiss std::vector<std::unique_ptr<sdbusplus::bus::match_t>> matches = 705214d9717SZev Weiss setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler); 7061263c3daSBruce Lee setupManufacturingModeMatch(*systemBus); 70720bf2c1cSMatt Spinler 70820bf2c1cSMatt Spinler // Watch for entity-manager to remove configuration interfaces 70920bf2c1cSMatt Spinler // so the corresponding sensors can be removed. 71092f8f515SPatrick Williams auto ifaceRemovedMatch = std::make_unique<sdbusplus::bus::match_t>( 71192f8f515SPatrick Williams static_cast<sdbusplus::bus_t&>(*systemBus), 71220bf2c1cSMatt Spinler "type='signal',member='InterfacesRemoved',arg0path='" + 71320bf2c1cSMatt Spinler std::string(inventoryPath) + "/'", 71492f8f515SPatrick Williams [&sensors](sdbusplus::message_t& msg) { 71520bf2c1cSMatt Spinler interfaceRemoved(msg, sensors); 71620bf2c1cSMatt Spinler }); 71720bf2c1cSMatt Spinler 71820bf2c1cSMatt Spinler matches.emplace_back(std::move(ifaceRemovedMatch)); 71920bf2c1cSMatt Spinler 7206714a25aSJames Feist io.run(); 7216714a25aSJames Feist } 722