/* // Copyright (c) 2019 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include "DeviceMgmt.hpp" #include "PSUEvent.hpp" #include "PSUSensor.hpp" #include "PwmSensor.hpp" #include "SensorPaths.hpp" #include "Thresholds.hpp" #include "Utils.hpp" #include "VariantVisitors.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static constexpr bool debug = false; static const I2CDeviceTypeMap sensorTypes{ {"ADC128D818", I2CDeviceType{"adc128d818", true}}, {"ADM1266", I2CDeviceType{"adm1266", true}}, {"ADM1272", I2CDeviceType{"adm1272", true}}, {"ADM1275", I2CDeviceType{"adm1275", true}}, {"ADM1278", I2CDeviceType{"adm1278", true}}, {"ADM1293", I2CDeviceType{"adm1293", true}}, {"ADS1015", I2CDeviceType{"ads1015", true}}, {"ADS7830", I2CDeviceType{"ads7830", true}}, {"AHE50DC_FAN", I2CDeviceType{"ahe50dc_fan", true}}, {"BMR490", I2CDeviceType{"bmr490", true}}, {"cffps", I2CDeviceType{"cffps", true}}, {"cffps1", I2CDeviceType{"cffps", true}}, {"cffps2", I2CDeviceType{"cffps", true}}, {"cffps3", I2CDeviceType{"cffps", true}}, {"DPS800", I2CDeviceType{"dps800", true}}, {"INA219", I2CDeviceType{"ina219", true}}, {"INA230", I2CDeviceType{"ina230", true}}, {"INA238", I2CDeviceType{"ina238", true}}, {"IPSPS1", I2CDeviceType{"ipsps1", true}}, {"IR38060", I2CDeviceType{"ir38060", true}}, {"IR38164", I2CDeviceType{"ir38164", true}}, {"IR38263", I2CDeviceType{"ir38263", true}}, {"ISL68137", I2CDeviceType{"isl68137", true}}, {"ISL68220", I2CDeviceType{"isl68220", true}}, {"ISL68223", I2CDeviceType{"isl68223", true}}, {"ISL69225", I2CDeviceType{"isl69225", true}}, {"ISL69243", I2CDeviceType{"isl69243", true}}, {"ISL69260", I2CDeviceType{"isl69260", true}}, {"LM25066", I2CDeviceType{"lm25066", true}}, {"LTC2945", I2CDeviceType{"ltc2945", true}}, {"LTC4286", I2CDeviceType{"ltc4286", true}}, {"LTC4287", I2CDeviceType{"ltc4287", true}}, {"MAX5970", I2CDeviceType{"max5970", true}}, {"MAX11607", I2CDeviceType{"max11607", false}}, {"MAX11617", I2CDeviceType{"max11617", false}}, {"MAX16601", I2CDeviceType{"max16601", true}}, {"MAX20710", I2CDeviceType{"max20710", true}}, {"MAX20730", I2CDeviceType{"max20730", true}}, {"MAX20734", I2CDeviceType{"max20734", true}}, {"MAX20796", I2CDeviceType{"max20796", true}}, {"MAX34451", I2CDeviceType{"max34451", true}}, {"MP2856", I2CDeviceType{"mp2856", true}}, {"MP2857", I2CDeviceType{"mp2857", true}}, {"MP2971", I2CDeviceType{"mp2971", true}}, {"MP2973", I2CDeviceType{"mp2973", true}}, {"MP2975", I2CDeviceType{"mp2975", true}}, {"MP5023", I2CDeviceType{"mp5023", true}}, {"MP5990", I2CDeviceType{"mp5990", true}}, {"MPQ8785", I2CDeviceType{"mpq8785", true}}, {"NCP4200", I2CDeviceType{"ncp4200", true}}, {"PLI1209BC", I2CDeviceType{"pli1209bc", true}}, {"pmbus", I2CDeviceType{"pmbus", true}}, {"PXE1610", I2CDeviceType{"pxe1610", true}}, {"RAA228000", I2CDeviceType{"raa228000", true}}, {"RAA228228", I2CDeviceType{"raa228228", true}}, {"RAA228620", I2CDeviceType{"raa228620", true}}, {"RAA229001", I2CDeviceType{"raa229001", true}}, {"RAA229004", I2CDeviceType{"raa229004", true}}, {"RAA229126", I2CDeviceType{"raa229126", true}}, {"SBRMI", I2CDeviceType{"sbrmi", true}}, {"TDA38640", I2CDeviceType{"tda38640", true}}, {"TPS53679", I2CDeviceType{"tps53679", true}}, {"TPS546D24", I2CDeviceType{"tps546d24", true}}, {"XDP710", I2CDeviceType{"xdp710", true}}, {"XDPE11280", I2CDeviceType{"xdpe11280", true}}, {"XDPE12284", I2CDeviceType{"xdpe12284", true}}, {"XDPE152C4", I2CDeviceType{"xdpe152c4", true}}, }; enum class DevTypes { Unknown = 0, HWMON, IIO }; struct DevParams { unsigned int matchIndex = 0; std::string matchRegEx; std::string nameRegEx; }; namespace fs = std::filesystem; static boost::container::flat_map> sensors; static boost::container::flat_map> combineEvents; static boost::container::flat_map> pwmSensors; static boost::container::flat_map sensorTable; static boost::container::flat_map labelMatch; static EventPathList eventMatch; static EventPathList limitEventMatch; static boost::container::flat_map cpuPresence; static boost::container::flat_map devParamMap; // Function CheckEvent will check each attribute from eventMatch table in the // sysfs. If the attributes exists in sysfs, then store the complete path // of the attribute into eventPathList. void checkEvent(const std::string& directory, const EventPathList& eventMatch, EventPathList& eventPathList) { for (const auto& match : eventMatch) { const std::vector& eventAttrs = match.second; const std::string& eventName = match.first; for (const auto& eventAttr : eventAttrs) { std::string eventPath = directory; eventPath += "/"; eventPath += eventAttr; std::ifstream eventFile(eventPath); if (!eventFile.good()) { continue; } eventPathList[eventName].push_back(eventPath); } } } // Check Group Events which contains more than one targets in each combine // events. void checkGroupEvent(const std::string& directory, GroupEventPathList& groupEventPathList) { EventPathList pathList; std::vector eventPaths; if (!findFiles(fs::path(directory), R"(fan\d+_(alarm|fault))", eventPaths)) { return; } for (const auto& eventPath : eventPaths) { std::string attrName = eventPath.filename(); pathList[attrName.substr(0, attrName.find('_'))].push_back(eventPath); } groupEventPathList["FanFault"] = pathList; } // Function checkEventLimits will check all the psu related xxx_input attributes // in sysfs to see if xxx_crit_alarm xxx_lcrit_alarm xxx_max_alarm // xxx_min_alarm exist, then store the existing paths of the alarm attributes // to eventPathList. void checkEventLimits(const std::string& sensorPathStr, const EventPathList& limitEventMatch, EventPathList& eventPathList) { auto attributePartPos = sensorPathStr.find_last_of('_'); if (attributePartPos == std::string::npos) { // There is no '_' in the string, skip it return; } auto attributePart = std::string_view(sensorPathStr).substr(attributePartPos + 1); if (attributePart != "input") { // If the sensor is not xxx_input, skip it return; } auto prefixPart = sensorPathStr.substr(0, attributePartPos + 1); for (const auto& limitMatch : limitEventMatch) { const std::vector& limitEventAttrs = limitMatch.second; const std::string& eventName = limitMatch.first; for (const auto& limitEventAttr : limitEventAttrs) { auto limitEventPath = prefixPart + limitEventAttr; std::ifstream eventFile(limitEventPath); if (!eventFile.good()) { continue; } eventPathList[eventName].push_back(limitEventPath); } } } static void checkPWMSensor(const fs::path& sensorPath, std::string& labelHead, const std::string& interfacePath, std::shared_ptr& dbusConnection, sdbusplus::asio::object_server& objectServer, const std::string& psuName) { if (!labelHead.starts_with("fan")) { return; } std::string labelHeadIndex = labelHead.substr(3); const std::string& sensorPathStr = sensorPath.string(); const std::string& pwmPathStr = boost::replace_all_copy(sensorPathStr, "input", "target"); std::ifstream pwmFile(pwmPathStr); if (!pwmFile.good()) { return; } auto findPWMSensor = pwmSensors.find(psuName + labelHead); if (findPWMSensor != pwmSensors.end()) { return; } std::string name = "Pwm_"; name += psuName; name += "_Fan_"; name += labelHeadIndex; std::string objPath = interfacePath; objPath += "_Fan_"; objPath += labelHeadIndex; pwmSensors[psuName + labelHead] = std::make_unique( name, pwmPathStr, dbusConnection, objectServer, objPath, "PSU"); } static void createSensorsCallback( boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer, std::shared_ptr& dbusConnection, const ManagedObjectType& sensorConfigs, const std::shared_ptr>& sensorsChanged, bool activateOnly) { int numCreated = 0; bool firstScan = sensorsChanged == nullptr; auto devices = instantiateDevices(sensorConfigs, sensors, sensorTypes); std::vector pmbusPaths; findFiles(fs::path("/sys/bus/iio/devices"), "name", pmbusPaths); findFiles(fs::path("/sys/class/hwmon"), "name", pmbusPaths); if (pmbusPaths.empty()) { std::cerr << "No PSU sensors in system\n"; return; } boost::container::flat_set directories; for (const auto& pmbusPath : pmbusPaths) { EventPathList eventPathList; GroupEventPathList groupEventPathList; std::ifstream nameFile(pmbusPath); if (!nameFile.good()) { std::cerr << "Failure finding pmbus path " << pmbusPath << "\n"; continue; } std::string pmbusName; std::getline(nameFile, pmbusName); nameFile.close(); if (sensorTypes.find(pmbusName) == sensorTypes.end()) { // To avoid this error message, add your driver name to // the pmbusNames vector at the top of this file. std::cerr << "Driver name " << pmbusName << " not found in sensor whitelist\n"; continue; } auto directory = pmbusPath.parent_path(); auto ret = directories.insert(directory.string()); if (!ret.second) { std::cerr << "Duplicate path " << directory.string() << "\n"; continue; // check if path has already been searched } DevTypes devType = DevTypes::HWMON; std::string deviceName; if (directory.parent_path() == "/sys/class/hwmon") { deviceName = fs::canonical(directory / "device").stem(); } else { deviceName = fs::canonical(directory).parent_path().stem(); devType = DevTypes::IIO; } size_t bus = 0; size_t addr = 0; if (!getDeviceBusAddr(deviceName, bus, addr)) { continue; } const SensorBaseConfigMap* baseConfig = nullptr; const SensorData* sensorData = nullptr; const std::string* interfacePath = nullptr; std::string sensorType; size_t thresholdConfSize = 0; for (const auto& [path, cfgData] : sensorConfigs) { sensorData = &cfgData; for (const auto& [type, dt] : sensorTypes) { auto sensorBase = sensorData->find(configInterfaceName(type)); if (sensorBase != sensorData->end()) { baseConfig = &sensorBase->second; sensorType = type; break; } } if (baseConfig == nullptr) { std::cerr << "error finding base configuration for " << deviceName << "\n"; continue; } auto configBus = baseConfig->find("Bus"); auto configAddress = baseConfig->find("Address"); if (configBus == baseConfig->end() || configAddress == baseConfig->end()) { std::cerr << "error finding necessary entry in configuration\n"; continue; } const uint64_t* confBus = std::get_if(&(configBus->second)); const uint64_t* confAddr = std::get_if(&(configAddress->second)); if (confBus == nullptr || confAddr == nullptr) { std::cerr << "Cannot get bus or address, invalid configuration\n"; continue; } if ((*confBus != bus) || (*confAddr != addr)) { if constexpr (debug) { std::cerr << "Configuration skipping " << *confBus << "-" << *confAddr << " because not " << bus << "-" << addr << "\n"; } continue; } std::vector confThresholds; if (!parseThresholdsFromConfig(*sensorData, confThresholds)) { std::cerr << "error populating total thresholds\n"; } thresholdConfSize = confThresholds.size(); interfacePath = &path.str; break; } if (interfacePath == nullptr) { // To avoid this error message, add your export map entry, // from Entity Manager, to sensorTypes at the top of this file. std::cerr << "failed to find match for " << deviceName << "\n"; continue; } auto findI2CDev = devices.find(*interfacePath); std::shared_ptr i2cDev; if (findI2CDev != devices.end()) { if (activateOnly && !findI2CDev->second.second) { continue; } i2cDev = findI2CDev->second.first; } auto findPSUName = baseConfig->find("Name"); if (findPSUName == baseConfig->end()) { std::cerr << "could not determine configuration name for " << deviceName << "\n"; continue; } const std::string* psuName = std::get_if(&(findPSUName->second)); if (psuName == nullptr) { std::cerr << "Cannot find psu name, invalid configuration\n"; continue; } auto findCPU = baseConfig->find("CPURequired"); if (findCPU != baseConfig->end()) { size_t index = std::visit(VariantToIntVisitor(), findCPU->second); auto presenceFind = cpuPresence.find(index); if (presenceFind == cpuPresence.end() || !presenceFind->second) { continue; } } // on rescans, only update sensors we were signaled by if (!firstScan) { std::string psuNameStr = "/" + escapeName(*psuName); auto it = std::find_if(sensorsChanged->begin(), sensorsChanged->end(), [psuNameStr](std::string& s) { return s.ends_with(psuNameStr); }); if (it == sensorsChanged->end()) { continue; } sensorsChanged->erase(it); } checkEvent(directory.string(), eventMatch, eventPathList); checkGroupEvent(directory.string(), groupEventPathList); PowerState readState = getPowerState(*baseConfig); /* Check if there are more sensors in the same interface */ int i = 1; std::vector psuNames; do { // Individual string fields: Name, Name1, Name2, Name3, ... psuNames.push_back( escapeName(std::get(findPSUName->second))); findPSUName = baseConfig->find("Name" + std::to_string(i++)); } while (findPSUName != baseConfig->end()); std::vector sensorPaths; if (!findFiles(directory, devParamMap[devType].matchRegEx, sensorPaths, 0)) { std::cerr << "No PSU non-label sensor in PSU\n"; continue; } /* read max value in sysfs for in, curr, power, temp, ... */ if (!findFiles(directory, R"(\w\d+_max$)", sensorPaths, 0)) { if constexpr (debug) { std::cerr << "No max name in PSU \n"; } } float pollRate = getPollRate(*baseConfig, PSUSensor::defaultSensorPoll); /* Find array of labels to be exposed if it is defined in config */ std::vector findLabels; auto findLabelObj = baseConfig->find("Labels"); if (findLabelObj != baseConfig->end()) { findLabels = std::get>(findLabelObj->second); } std::regex sensorNameRegEx(devParamMap[devType].nameRegEx); std::smatch matches; for (const auto& sensorPath : sensorPaths) { bool maxLabel = false; std::string labelHead; std::string sensorPathStr = sensorPath.string(); std::string sensorNameStr = sensorPath.filename(); std::string sensorNameSubStr; if (std::regex_search(sensorNameStr, matches, sensorNameRegEx)) { // hwmon *_input filename without number: // in, curr, power, temp, ... // iio in_*_raw filename without number: // voltage, temp, pressure, ... sensorNameSubStr = matches[devParamMap[devType].matchIndex]; } else { std::cerr << "Could not extract the alpha prefix from " << sensorNameStr; continue; } std::string labelPath; if (devType == DevTypes::HWMON) { /* find and differentiate _max and _input to replace "label" */ size_t pos = sensorPathStr.find('_'); if (pos != std::string::npos) { std::string sensorPathStrMax = sensorPathStr.substr(pos); if (sensorPathStrMax == "_max") { labelPath = boost::replace_all_copy(sensorPathStr, "max", "label"); maxLabel = true; } else { labelPath = boost::replace_all_copy(sensorPathStr, "input", "label"); maxLabel = false; } } else { continue; } std::ifstream labelFile(labelPath); if (!labelFile.good()) { if constexpr (debug) { std::cerr << "Input file " << sensorPath << " has no corresponding label file\n"; } // hwmon *_input filename with number: // temp1, temp2, temp3, ... labelHead = sensorNameStr.substr(0, sensorNameStr.find('_')); } else { std::string label; std::getline(labelFile, label); labelFile.close(); auto findSensor = sensors.find(label); if (findSensor != sensors.end()) { continue; } // hwmon corresponding *_label file contents: // vin1, vout1, ... labelHead = label.substr(0, label.find(' ')); } /* append "max" for labelMatch */ if (maxLabel) { labelHead.insert(0, "max"); } checkPWMSensor(sensorPath, labelHead, *interfacePath, dbusConnection, objectServer, psuNames[0]); } else if (devType == DevTypes::IIO) { auto findIIOHyphen = sensorNameStr.find_last_of('_'); labelHead = sensorNameStr.substr(0, findIIOHyphen); } if constexpr (debug) { std::cerr << "Sensor type=\"" << sensorNameSubStr << "\" label=\"" << labelHead << "\"\n"; } if (!findLabels.empty()) { /* Check if this labelHead is enabled in config file */ if (std::find(findLabels.begin(), findLabels.end(), labelHead) == findLabels.end()) { if constexpr (debug) { std::cerr << "could not find " << labelHead << " in the Labels list\n"; } continue; } } auto findProperty = labelMatch.find(sensorNameSubStr); if (findProperty == labelMatch.end()) { if constexpr (debug) { std::cerr << "Could not find matching default property for " << sensorNameSubStr << "\n"; } continue; } // Protect the hardcoded labelMatch list from changes, // by making a copy and modifying that instead. // Avoid bleedthrough of one device's customizations to // the next device, as each should be independently customizable. PSUProperty psuProperty = findProperty->second; // Use label head as prefix for reading from config file, // example if temp1: temp1_Name, temp1_Scale, temp1_Min, ... std::string keyName = labelHead + "_Name"; std::string keyScale = labelHead + "_Scale"; std::string keyMin = labelHead + "_Min"; std::string keyMax = labelHead + "_Max"; std::string keyOffset = labelHead + "_Offset"; std::string keyPowerState = labelHead + "_PowerState"; bool customizedName = false; auto findCustomName = baseConfig->find(keyName); if (findCustomName != baseConfig->end()) { try { psuProperty.labelTypeName = std::visit( VariantToStringVisitor(), findCustomName->second); } catch (const std::invalid_argument&) { std::cerr << "Unable to parse " << keyName << "\n"; continue; } // All strings are valid, including empty string customizedName = true; } bool customizedScale = false; auto findCustomScale = baseConfig->find(keyScale); if (findCustomScale != baseConfig->end()) { try { psuProperty.sensorScaleFactor = std::visit( VariantToUnsignedIntVisitor(), findCustomScale->second); } catch (const std::invalid_argument&) { std::cerr << "Unable to parse " << keyScale << "\n"; continue; } // Avoid later division by zero if (psuProperty.sensorScaleFactor > 0) { customizedScale = true; } else { std::cerr << "Unable to accept " << keyScale << "\n"; continue; } } auto findCustomMin = baseConfig->find(keyMin); if (findCustomMin != baseConfig->end()) { try { psuProperty.minReading = std::visit( VariantToDoubleVisitor(), findCustomMin->second); } catch (const std::invalid_argument&) { std::cerr << "Unable to parse " << keyMin << "\n"; continue; } } auto findCustomMax = baseConfig->find(keyMax); if (findCustomMax != baseConfig->end()) { try { psuProperty.maxReading = std::visit( VariantToDoubleVisitor(), findCustomMax->second); } catch (const std::invalid_argument&) { std::cerr << "Unable to parse " << keyMax << "\n"; continue; } } auto findCustomOffset = baseConfig->find(keyOffset); if (findCustomOffset != baseConfig->end()) { try { psuProperty.sensorOffset = std::visit( VariantToDoubleVisitor(), findCustomOffset->second); } catch (const std::invalid_argument&) { std::cerr << "Unable to parse " << keyOffset << "\n"; continue; } } // if we find label head power state set ,override the powerstate. auto findPowerState = baseConfig->find(keyPowerState); if (findPowerState != baseConfig->end()) { std::string powerState = std::visit(VariantToStringVisitor(), findPowerState->second); setReadState(powerState, readState); } if (!(psuProperty.minReading < psuProperty.maxReading)) { std::cerr << "Min must be less than Max\n"; continue; } // If the sensor name is being customized by config file, // then prefix/suffix composition becomes not necessary, // and in fact not wanted, because it gets in the way. std::string psuNameFromIndex; std::string nameIndexStr = "1"; if (!customizedName) { /* Find out sensor name index for this label */ std::regex rgx("[A-Za-z]+([0-9]+)"); size_t nameIndex{0}; if (std::regex_search(labelHead, matches, rgx)) { nameIndexStr = matches[1]; nameIndex = std::stoi(nameIndexStr); // Decrement to preserve alignment, because hwmon // human-readable filenames and labels use 1-based // numbering, but the "Name", "Name1", "Name2", etc. naming // convention (the psuNames vector) uses 0-based numbering. if (nameIndex > 0) { --nameIndex; } } else { nameIndex = 0; } if (psuNames.size() <= nameIndex) { std::cerr << "Could not pair " << labelHead << " with a Name field\n"; continue; } psuNameFromIndex = psuNames[nameIndex]; if constexpr (debug) { std::cerr << "Sensor label head " << labelHead << " paired with " << psuNameFromIndex << " at index " << nameIndex << "\n"; } } if (devType == DevTypes::HWMON) { checkEventLimits(sensorPathStr, limitEventMatch, eventPathList); } // Similarly, if sensor scaling factor is being customized, // then the below power-of-10 constraint becomes unnecessary, // as config should be able to specify an arbitrary divisor. unsigned int factor = psuProperty.sensorScaleFactor; if (!customizedScale) { // Preserve existing usage of hardcoded labelMatch table below factor = std::pow(10.0, factor); /* Change first char of substring to uppercase */ char firstChar = static_cast(std::toupper(sensorNameSubStr[0])); std::string strScaleFactor = firstChar + sensorNameSubStr.substr(1) + "ScaleFactor"; // Preserve existing configs by accepting earlier syntax, // example CurrScaleFactor, PowerScaleFactor, ... auto findScaleFactor = baseConfig->find(strScaleFactor); if (findScaleFactor != baseConfig->end()) { factor = std::visit(VariantToIntVisitor(), findScaleFactor->second); } if constexpr (debug) { std::cerr << "Sensor scaling factor " << factor << " string " << strScaleFactor << "\n"; } } std::vector sensorThresholds; if (!parseThresholdsFromConfig(*sensorData, sensorThresholds, &labelHead)) { std::cerr << "error populating thresholds for " << sensorNameSubStr << "\n"; } auto findSensorUnit = sensorTable.find(sensorNameSubStr); if (findSensorUnit == sensorTable.end()) { std::cerr << sensorNameSubStr << " is not a recognized sensor type\n"; continue; } if constexpr (debug) { std::cerr << "Sensor properties: Name \"" << psuProperty.labelTypeName << "\" Scale " << psuProperty.sensorScaleFactor << " Min " << psuProperty.minReading << " Max " << psuProperty.maxReading << " Offset " << psuProperty.sensorOffset << "\n"; } std::string sensorName = psuProperty.labelTypeName; if (customizedName) { if (sensorName.empty()) { // Allow selective disabling of an individual sensor, // by customizing its name to an empty string. std::cerr << "Sensor disabled, empty string\n"; continue; } } else { // Sensor name not customized, do prefix/suffix composition, // preserving default behavior by using psuNameFromIndex. sensorName = psuNameFromIndex + " " + psuProperty.labelTypeName; // The labelTypeName of a fan can be: // "Fan Speed 1", "Fan Speed 2", "Fan Speed 3" ... if (labelHead == "fan" + nameIndexStr) { sensorName += nameIndexStr; } } if constexpr (debug) { std::cerr << "Sensor name \"" << sensorName << "\" path \"" << sensorPathStr << "\" type \"" << sensorType << "\"\n"; } // destruct existing one first if already created auto& sensor = sensors[sensorName]; if (!activateOnly) { sensor = nullptr; } if (sensor != nullptr) { sensor->activate(sensorPathStr, i2cDev); } else { sensors[sensorName] = std::make_shared( sensorPathStr, sensorType, objectServer, dbusConnection, io, sensorName, std::move(sensorThresholds), *interfacePath, readState, findSensorUnit->second, factor, psuProperty.maxReading, psuProperty.minReading, psuProperty.sensorOffset, labelHead, thresholdConfSize, pollRate, i2cDev); sensors[sensorName]->setupRead(); ++numCreated; if constexpr (debug) { std::cerr << "Created " << numCreated << " sensors so far\n"; } } } if (devType == DevTypes::HWMON) { // OperationalStatus event combineEvents[*psuName + "OperationalStatus"] = nullptr; combineEvents[*psuName + "OperationalStatus"] = std::make_unique( objectServer, dbusConnection, io, *psuName, readState, eventPathList, groupEventPathList, "OperationalStatus", pollRate); } } if constexpr (debug) { std::cerr << "Created total of " << numCreated << " sensors\n"; } } static void getPresentCpus(std::shared_ptr& dbusConnection) { static const int depth = 2; static const int numKeys = 1; GetSubTreeType cpuSubTree; try { auto getItems = dbusConnection->new_method_call( mapper::busName, mapper::path, mapper::interface, mapper::subtree); getItems.append(cpuInventoryPath, static_cast(depth), std::array{ "xyz.openbmc_project.Inventory.Item"}); auto getItemsResp = dbusConnection->call(getItems); getItemsResp.read(cpuSubTree); } catch (sdbusplus::exception_t& e) { std::cerr << "error getting inventory item subtree: " << e.what() << "\n"; return; } for (const auto& [path, objDict] : cpuSubTree) { auto obj = sdbusplus::message::object_path(path).filename(); if (!obj.starts_with("cpu") || objDict.empty()) { continue; } const std::string& owner = objDict.begin()->first; std::variant respValue; try { auto getPresence = dbusConnection->new_method_call( owner.c_str(), path.c_str(), "org.freedesktop.DBus.Properties", "Get"); getPresence.append("xyz.openbmc_project.Inventory.Item", "Present"); auto resp = dbusConnection->call(getPresence); resp.read(respValue); } catch (sdbusplus::exception_t& e) { std::cerr << "Error in getting CPU presence: " << e.what() << "\n"; continue; } auto* present = std::get_if(&respValue); if (present != nullptr && *present) { int cpuIndex = 0; try { cpuIndex = std::stoi(obj.substr(obj.find_last_of("cpu") + 1)); } catch (const std::exception& e) { std::cerr << "Error converting CPU index, " << e.what() << '\n'; continue; } cpuPresence[cpuIndex + 1] = *present; } } } void createSensors( boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer, std::shared_ptr& dbusConnection, const std::shared_ptr>& sensorsChanged, bool activateOnly) { auto getter = std::make_shared( dbusConnection, [&io, &objectServer, &dbusConnection, sensorsChanged, activateOnly](const ManagedObjectType& sensorConfigs) { createSensorsCallback(io, objectServer, dbusConnection, sensorConfigs, sensorsChanged, activateOnly); }); std::vector types(sensorTypes.size()); for (const auto& [type, dt] : sensorTypes) { types.push_back(type); } getter->getConfiguration(types); } void propertyInitialize() { sensorTable = {{"power", sensor_paths::unitWatts}, {"curr", sensor_paths::unitAmperes}, {"temp", sensor_paths::unitDegreesC}, {"in", sensor_paths::unitVolts}, {"voltage", sensor_paths::unitVolts}, {"fan", sensor_paths::unitRPMs}}; labelMatch = { {"pin", PSUProperty("Input Power", 3000, 0, 6, 0)}, {"pout", PSUProperty("Output Power", 3000, 0, 6, 0)}, {"power", PSUProperty("Output Power", 3000, 0, 6, 0)}, {"maxpin", PSUProperty("Max Input Power", 3000, 0, 6, 0)}, {"vin", PSUProperty("Input Voltage", 300, 0, 3, 0)}, {"maxvin", PSUProperty("Max Input Voltage", 300, 0, 3, 0)}, {"in_voltage", PSUProperty("Output Voltage", 255, 0, 3, 0)}, {"vout", PSUProperty("Output Voltage", 255, 0, 3, 0)}, {"vmon", PSUProperty("Auxiliary Input Voltage", 255, 0, 3, 0)}, {"in", PSUProperty("Output Voltage", 255, 0, 3, 0)}, {"iin", PSUProperty("Input Current", 20, 0, 3, 0)}, {"iout", PSUProperty("Output Current", 255, 0, 3, 0)}, {"curr", PSUProperty("Output Current", 255, 0, 3, 0)}, {"maxiout", PSUProperty("Max Output Current", 255, 0, 3, 0)}, {"temp", PSUProperty("Temperature", 127, -128, 3, 0)}, {"maxtemp", PSUProperty("Max Temperature", 127, -128, 3, 0)}, {"fan", PSUProperty("Fan Speed ", 30000, 0, 0, 0)}}; limitEventMatch = {{"PredictiveFailure", {"max_alarm", "min_alarm"}}, {"Failure", {"crit_alarm", "lcrit_alarm"}}}; eventMatch = {{"PredictiveFailure", {"power1_alarm"}}, {"Failure", {"in2_alarm"}}, {"ACLost", {"in1_beep"}}, {"ConfigureError", {"in1_fault"}}}; devParamMap = { {DevTypes::HWMON, {1, R"(\w\d+_input$)", "([A-Za-z]+)[0-9]*_"}}, {DevTypes::IIO, {2, R"(\w+_(raw|input)$)", "^(in|out)_([A-Za-z]+)[0-9]*_"}}}; } static void powerStateChanged( PowerState type, bool newState, boost::container::flat_map>& sensors, boost::asio::io_context& io, sdbusplus::asio::object_server& objectServer, std::shared_ptr& dbusConnection) { if (newState) { createSensors(io, objectServer, dbusConnection, nullptr, true); } else { for (auto& [path, sensor] : sensors) { if (sensor != nullptr && sensor->readState == type) { sensor->deactivate(); } } } } int main() { boost::asio::io_context io; auto systemBus = std::make_shared(io); sdbusplus::asio::object_server objectServer(systemBus, true); objectServer.add_manager("/xyz/openbmc_project/sensors"); objectServer.add_manager("/xyz/openbmc_project/control"); systemBus->request_name("xyz.openbmc_project.PSUSensor"); auto sensorsChanged = std::make_shared>(); propertyInitialize(); auto powerCallBack = [&io, &objectServer, &systemBus](PowerState type, bool state) { powerStateChanged(type, state, sensors, io, objectServer, systemBus); }; setupPowerMatchCallback(systemBus, powerCallBack); boost::asio::post(io, [&]() { createSensors(io, objectServer, systemBus, nullptr, false); }); boost::asio::steady_timer filterTimer(io); std::function eventHandler = [&](sdbusplus::message_t& message) { if (message.is_method_error()) { std::cerr << "callback method error\n"; return; } sensorsChanged->insert(message.get_path()); filterTimer.expires_after(std::chrono::seconds(3)); filterTimer.async_wait([&](const boost::system::error_code& ec) { if (ec == boost::asio::error::operation_aborted) { return; } if (ec) { std::cerr << "timer error\n"; } createSensors(io, objectServer, systemBus, sensorsChanged, false); }); }; boost::asio::steady_timer cpuFilterTimer(io); std::function cpuPresenceHandler = [&](sdbusplus::message_t& message) { std::string path = message.get_path(); boost::to_lower(path); sdbusplus::message::object_path cpuPath(path); std::string cpuName = cpuPath.filename(); if (!cpuName.starts_with("cpu")) { return; } size_t index = 0; try { index = std::stoi(path.substr(path.size() - 1)); } catch (const std::invalid_argument&) { std::cerr << "Found invalid path " << path << "\n"; return; } std::string objectName; boost::container::flat_map> values; message.read(objectName, values); auto findPresence = values.find("Present"); try { cpuPresence[index] = std::get(findPresence->second); } catch (const std::bad_variant_access& err) { return; } if (!cpuPresence[index]) { return; } cpuFilterTimer.expires_after(std::chrono::seconds(1)); cpuFilterTimer.async_wait([&](const boost::system::error_code& ec) { if (ec == boost::asio::error::operation_aborted) { return; } if (ec) { std::cerr << "timer error\n"; return; } createSensors(io, objectServer, systemBus, nullptr, false); }); }; std::vector> matches = setupPropertiesChangedMatches(*systemBus, sensorTypes, eventHandler); matches.emplace_back(std::make_unique( static_cast(*systemBus), "type='signal',member='PropertiesChanged',path_namespace='" + std::string(cpuInventoryPath) + "',arg0namespace='xyz.openbmc_project.Inventory.Item'", cpuPresenceHandler)); getPresentCpus(systemBus); setupManufacturingModeMatch(*systemBus); io.run(); }