1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 17 #include <boost/algorithm/string.hpp> 18 #include <boost/bimap.hpp> 19 #include <boost/container/flat_map.hpp> 20 #include <cstring> 21 #include <phosphor-logging/log.hpp> 22 #include <sdbusplus/bus/match.hpp> 23 24 #pragma once 25 26 struct CmpStrVersion 27 { 28 bool operator()(std::string a, std::string b) const 29 { 30 return strverscmp(a.c_str(), b.c_str()) < 0; 31 } 32 }; 33 34 using SensorSubTree = boost::container::flat_map< 35 std::string, 36 boost::container::flat_map<std::string, std::vector<std::string>>, 37 CmpStrVersion>; 38 39 using SensorNumMap = boost::bimap<int, std::string>; 40 41 namespace details 42 { 43 inline static bool getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree) 44 { 45 static std::shared_ptr<SensorSubTree> sensorTreePtr; 46 sd_bus* bus = NULL; 47 int ret = sd_bus_default_system(&bus); 48 if (ret < 0) 49 { 50 phosphor::logging::log<phosphor::logging::level::ERR>( 51 "Failed to connect to system bus", 52 phosphor::logging::entry("ERRNO=0x%X", -ret)); 53 sd_bus_unref(bus); 54 return false; 55 } 56 sdbusplus::bus::bus dbus(bus); 57 static sdbusplus::bus::match::match sensorAdded( 58 dbus, 59 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/" 60 "sensors/'", 61 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); }); 62 63 static sdbusplus::bus::match::match sensorRemoved( 64 dbus, 65 "type='signal',member='InterfacesRemoved',arg0path='/xyz/" 66 "openbmc_project/sensors/'", 67 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); }); 68 69 bool sensorTreeUpdated = false; 70 if (sensorTreePtr) 71 { 72 subtree = sensorTreePtr; 73 return sensorTreeUpdated; 74 } 75 76 sensorTreePtr = std::make_shared<SensorSubTree>(); 77 78 auto mapperCall = 79 dbus.new_method_call("xyz.openbmc_project.ObjectMapper", 80 "/xyz/openbmc_project/object_mapper", 81 "xyz.openbmc_project.ObjectMapper", "GetSubTree"); 82 static constexpr const auto depth = 2; 83 static constexpr std::array<const char*, 3> interfaces = { 84 "xyz.openbmc_project.Sensor.Value", 85 "xyz.openbmc_project.Sensor.Threshold.Warning", 86 "xyz.openbmc_project.Sensor.Threshold.Critical"}; 87 mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces); 88 89 try 90 { 91 auto mapperReply = dbus.call(mapperCall); 92 mapperReply.read(*sensorTreePtr); 93 } 94 catch (sdbusplus::exception_t& e) 95 { 96 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 97 return sensorTreeUpdated; 98 } 99 subtree = sensorTreePtr; 100 sensorTreeUpdated = true; 101 return sensorTreeUpdated; 102 } 103 104 inline static bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap) 105 { 106 static std::shared_ptr<SensorNumMap> sensorNumMapPtr; 107 bool sensorNumMapUpated = false; 108 109 std::shared_ptr<SensorSubTree> sensorTree; 110 bool sensorTreeUpdated = details::getSensorSubtree(sensorTree); 111 if (!sensorTree) 112 { 113 return sensorNumMapUpated; 114 } 115 116 if (!sensorTreeUpdated && sensorNumMapPtr) 117 { 118 sensorNumMap = sensorNumMapPtr; 119 return sensorNumMapUpated; 120 } 121 122 sensorNumMapPtr = std::make_shared<SensorNumMap>(); 123 124 uint8_t sensorNum = 1; 125 for (const auto& sensor : *sensorTree) 126 { 127 sensorNumMapPtr->insert( 128 SensorNumMap::value_type(sensorNum++, sensor.first)); 129 } 130 sensorNumMap = sensorNumMapPtr; 131 sensorNumMapUpated = true; 132 return sensorNumMapUpated; 133 } 134 } // namespace details 135 136 inline static bool getSensorSubtree(SensorSubTree& subtree) 137 { 138 std::shared_ptr<SensorSubTree> sensorTree; 139 details::getSensorSubtree(sensorTree); 140 if (!sensorTree) 141 { 142 return false; 143 } 144 145 subtree = *sensorTree; 146 return true; 147 } 148 149 struct CmpStr 150 { 151 bool operator()(const char* a, const char* b) const 152 { 153 return std::strcmp(a, b) < 0; 154 } 155 }; 156 157 enum class SensorTypeCodes : uint8_t 158 { 159 reserved = 0x0, 160 temperature = 0x1, 161 voltage = 0x2, 162 current = 0x3, 163 fan = 0x4, 164 other = 0xB, 165 }; 166 167 const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr> 168 sensorTypes{{{"temperature", SensorTypeCodes::temperature}, 169 {"voltage", SensorTypeCodes::voltage}, 170 {"current", SensorTypeCodes::current}, 171 {"fan_tach", SensorTypeCodes::fan}, 172 {"fan_pwm", SensorTypeCodes::fan}, 173 {"power", SensorTypeCodes::other}}}; 174 175 inline static std::string getSensorTypeStringFromPath(const std::string& path) 176 { 177 // get sensor type string from path, path is defined as 178 // /xyz/openbmc_project/sensors/<type>/label 179 size_t typeEnd = path.rfind("/"); 180 if (typeEnd == std::string::npos) 181 { 182 return path; 183 } 184 size_t typeStart = path.rfind("/", typeEnd - 1); 185 if (typeStart == std::string::npos) 186 { 187 return path; 188 } 189 // Start at the character after the '/' 190 typeStart++; 191 return path.substr(typeStart, typeEnd - typeStart); 192 } 193 194 inline static uint8_t getSensorTypeFromPath(const std::string& path) 195 { 196 uint8_t sensorType = 0; 197 std::string type = getSensorTypeStringFromPath(path); 198 auto findSensor = sensorTypes.find(type.c_str()); 199 if (findSensor != sensorTypes.end()) 200 { 201 sensorType = static_cast<uint8_t>(findSensor->second); 202 } // else default 0x0 RESERVED 203 204 return sensorType; 205 } 206 207 inline static uint8_t getSensorNumberFromPath(const std::string& path) 208 { 209 std::shared_ptr<SensorNumMap> sensorNumMapPtr; 210 details::getSensorNumMap(sensorNumMapPtr); 211 if (!sensorNumMapPtr) 212 { 213 return 0xFF; 214 } 215 216 try 217 { 218 return sensorNumMapPtr->right.at(path); 219 } 220 catch (std::out_of_range& e) 221 { 222 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 223 return 0xFF; 224 } 225 } 226 227 inline static uint8_t getSensorEventTypeFromPath(const std::string& path) 228 { 229 // TODO: Add support for additional reading types as needed 230 return 0x1; // reading type = threshold 231 } 232 233 inline static std::string getPathFromSensorNumber(uint8_t sensorNum) 234 { 235 std::shared_ptr<SensorNumMap> sensorNumMapPtr; 236 details::getSensorNumMap(sensorNumMapPtr); 237 if (!sensorNumMapPtr) 238 { 239 return std::string(); 240 } 241 242 try 243 { 244 return sensorNumMapPtr->left.at(sensorNum); 245 } 246 catch (std::out_of_range& e) 247 { 248 phosphor::logging::log<phosphor::logging::level::ERR>(e.what()); 249 return std::string(); 250 } 251 } 252