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