blob: 71d4a5b3dd5078fee1f2d2cb04dba5eb80741e1f [file] [log] [blame]
James Feist6ef20402019-01-07 16:45:08 -08001/*
2// Copyright (c) 2019 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 "IpmbSensor.hpp"
18
19#include "Utils.hpp"
20#include "VariantVisitors.hpp"
21
22#include <math.h>
23
24#include <boost/algorithm/string.hpp>
25#include <boost/algorithm/string/predicate.hpp>
26#include <boost/algorithm/string/replace.hpp>
27#include <chrono>
28#include <iostream>
29#include <limits>
30#include <numeric>
31#include <sdbusplus/asio/connection.hpp>
32#include <sdbusplus/asio/object_server.hpp>
33#include <vector>
34
35constexpr const bool debug = false;
36
37constexpr const char* configInterface =
38 "xyz.openbmc_project.Configuration.IpmbSensor";
39static constexpr double ipmbMaxReading = 0xFF;
40static constexpr double ipmbMinReading = 0;
41
42static constexpr uint8_t meAddress = 1;
43static constexpr uint8_t lun = 0;
44
45using IpmbMethodType =
46 std::tuple<int, uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>;
47
48IpmbSensor::IpmbSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
49 boost::asio::io_service& io,
50 const std::string& sensorName,
51 const std::string& sensorConfiguration,
52 sdbusplus::asio::object_server& objectServer,
53 std::vector<thresholds::Threshold>&& thresholdData,
54 uint8_t deviceAddress) :
55 Sensor(boost::replace_all_copy(sensorName, " ", "_"),
56 "" /* todo: remove arg from base*/, std::move(thresholdData),
57 sensorConfiguration, "xyz.openbmc_project.Configuration.ExitAirTemp",
58 ipmbMaxReading, ipmbMinReading),
59 objectServer(objectServer), dbusConnection(conn), waitTimer(io),
60 deviceAddress(deviceAddress)
61{
62 sensorInterface = objectServer.add_interface(
63 "/xyz/openbmc_project/sensors/temperature/" + name,
64 "xyz.openbmc_project.Sensor.Value");
65
66 if (thresholds::hasWarningInterface(thresholds))
67 {
68 thresholdInterfaceWarning = objectServer.add_interface(
69 "/xyz/openbmc_project/sensors/temperature/" + name,
70 "xyz.openbmc_project.Sensor.Threshold.Warning");
71 }
72 if (thresholds::hasCriticalInterface(thresholds))
73 {
74 thresholdInterfaceCritical = objectServer.add_interface(
75 "/xyz/openbmc_project/sensors/temperature/" + name,
76 "xyz.openbmc_project.Sensor.Threshold.Critical");
77 }
78 setupPowerMatch(conn);
79}
80
81IpmbSensor::~IpmbSensor()
82{
83 waitTimer.cancel();
84 objectServer.remove_interface(thresholdInterfaceWarning);
85 objectServer.remove_interface(thresholdInterfaceCritical);
86 objectServer.remove_interface(sensorInterface);
87}
88
89void IpmbSensor::init(void)
90{
91 setInitialProperties(dbusConnection);
92 loadDefaults();
93 if (initCommand)
94 {
95 dbusConnection->async_method_call(
96 [this](boost::system::error_code ec,
97 const IpmbMethodType& response) {
98 const int& status = std::get<0>(response);
99
100 if (ec || status)
101 {
102 std::cerr
103 << "Error setting init command for device: " << name
104 << "\n";
105 }
106 read();
107 },
108 "xyz.openbmc_project.Ipmi.Channel.Ipmb",
109 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
110 "sendRequest", commandAddress, netfn, lun, *initCommand, initData);
111 }
112 else
113 {
114 read();
115 }
116}
117
118void IpmbSensor::loadDefaults()
119{
120 if (type == IpmbType::meSensor)
121 {
122 commandAddress = meAddress;
123 netfn = 0x4; // sensor
124 command = 0x2d; // get sensor reading
125 commandData = {deviceAddress};
126 }
127 else if (type == IpmbType::PXE1410CVR)
128 {
129 commandAddress = meAddress;
130 netfn = 0x2e; // me bridge
131 command = 0xd9; // send raw pmbus
132 initCommand = 0xd9; // send raw pmbus
133 commandData = {0x57, 0x01, 0x00, 0x16, 0x03, deviceAddress, 00,
134 0x00, 0x00, 0x00, 0x01, 0x02, 0x29};
135 initData = {0x57, 0x01, 0x00, 0x14, 0x03, deviceAddress, 0x00,
136 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x60};
137 }
138 else if (type == IpmbType::IR38363VR)
139 {
140 commandAddress = meAddress;
141 netfn = 0x2e; // me bridge
142 command = 0xd9; // send raw pmbus
143 commandData = {0x57, 0x01, 0x00, 0x16, 0x03, deviceAddress, 00,
144 0x00, 0x00, 0x00, 0x01, 0x02, 0x8D};
145 }
146 else if (type == IpmbType::mpsVR)
147 {
148 commandAddress = meAddress;
149 netfn = 0x2e; // me bridge
150 command = 0xd9; // send raw pmbus
151 initCommand = 0xd9; // send raw pmbus
152 commandData = {0x57, 0x01, 0x00, 0x16, 0x3, deviceAddress, 0x00,
153 0x00, 0x00, 0x00, 0x01, 0x02, 0x8d};
154 initData = {0x57, 0x01, 0x00, 0x14, 0x03, deviceAddress, 0x00,
155 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00};
156 }
157 else
158 {
159 throw std::runtime_error("Invalid sensor type");
160 }
161}
162
163void IpmbSensor::checkThresholds(void)
164{
165 if (readState == PowerState::on && !isPowerOn())
166 {
167 return;
168 }
169 thresholds::checkThresholds(this);
170}
171
172void IpmbSensor::read(void)
173{
174 static constexpr size_t pollTime = 1; // in seconds
175
176 waitTimer.expires_from_now(boost::posix_time::seconds(pollTime));
177 waitTimer.async_wait([this](const boost::system::error_code& ec) {
178 if (ec == boost::asio::error::operation_aborted)
179 {
180 return; // we're being canceled
181 }
182 if (!isPowerOn() && readState == PowerState::on)
183 {
184 updateValue(0);
185 read();
186 return;
187 }
188 dbusConnection->async_method_call(
189 [this](boost::system::error_code ec,
190 const IpmbMethodType& response) {
191 const int& status = std::get<0>(response);
192 if (ec || status)
193 {
194 std::cerr << "Error reading from device: " << name << "\n";
195 updateValue(0);
196 read();
197 return;
198 }
199 if (!isPowerOn() && readState == PowerState::on)
200 {
201 updateValue(0);
202 read();
203 return;
204 }
205 const std::vector<uint8_t>& data = std::get<5>(response);
206 if constexpr (debug)
207 {
208 std::cout << name << ": ";
209 for (size_t d : data)
210 {
211 std::cout << d << " ";
212 }
213 std::cout << "\n";
214 }
215 uint16_t value = 0;
216 if (type == IpmbType::meSensor)
217 {
218 if (data.empty())
219 {
220 std::cerr << "Invalid data from device: " << name
221 << "\n";
222 read();
223 return;
224 }
225 value = data[0];
226 }
227 else if (type == IpmbType::PXE1410CVR ||
228 type == IpmbType::IR38363VR)
229 {
230 if (data.size() < 4)
231 {
232 std::cerr << "Invalid data from device: " << name
233 << "\n";
234 read();
235 return;
236 }
237 // format based on the 11 bit linear data format
238 value = ((data[4] << 8) | data[3]) >> 3;
239 }
240 else if (type == IpmbType::mpsVR)
241 {
242 if (data.size() < 4)
243 {
244 std::cerr << "Invalid data from device: " << name
245 << "\n";
246 read();
247 return;
248 }
249 value = data[3];
250 }
251 else
252 {
253 throw std::runtime_error("Invalid sensor type");
254 }
255 updateValue(value);
256 read();
257 },
258 "xyz.openbmc_project.Ipmi.Channel.Ipmb",
259 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
260 "sendRequest", commandAddress, netfn, lun, command, commandData);
261 });
262}
263void createSensors(
264 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
265 boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>>&
266 sensors,
267 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
268{
269 if (!dbusConnection)
270 {
271 std::cerr << "Connection not created\n";
272 return;
273 }
274 dbusConnection->async_method_call(
275 [&](boost::system::error_code ec, const ManagedObjectType& resp) {
276 if (ec)
277 {
278 std::cerr << "Error contacting entity manager\n";
279 return;
280 }
281 for (const auto& pathPair : resp)
282 {
283 for (const auto& entry : pathPair.second)
284 {
285 if (entry.first != configInterface)
286 {
287 continue;
288 }
289 std::string name =
290 loadVariant<std::string>(entry.second, "Name");
291
292 std::vector<thresholds::Threshold> sensorThresholds;
293 if (!parseThresholdsFromConfig(pathPair.second,
294 sensorThresholds))
295 {
296 std::cerr << "error populating thresholds for " << name
297 << "\n";
298 }
299 uint8_t deviceAddress =
300 loadVariant<uint8_t>(entry.second, "Address");
301
302 std::string sensorClass =
303 loadVariant<std::string>(entry.second, "Class");
304 auto& sensor = sensors[name];
305 sensor = std::make_unique<IpmbSensor>(
306 dbusConnection, io, name, pathPair.first, objectServer,
307 std::move(sensorThresholds), deviceAddress);
308
309 if (sensorClass == "PxeBridgeTemp")
310 {
311 sensor->type = IpmbType::PXE1410CVR;
312 }
313 else if (sensorClass == "IRBridgeTemp")
314 {
315 sensor->type = IpmbType::IR38363VR;
316 }
317 else if (sensorClass == "MpsBridgeTemp")
318 {
319 sensor->type = IpmbType::mpsVR;
320 }
321 else if (sensorClass == "METemp")
322 {
323 sensor->type = IpmbType::meSensor;
324 }
325 else
326 {
327 std::cerr << "Invalid class " << sensorClass << "\n";
328 continue;
329 }
330 sensor->init();
331 }
332 }
333 },
334 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
335 "GetManagedObjects");
336}
337
338int main(int argc, char** argv)
339{
340
341 boost::asio::io_service io;
342 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
343 systemBus->request_name("xyz.openbmc_project.IpmbSensor");
344 sdbusplus::asio::object_server objectServer(systemBus);
345 boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>>
346 sensors;
347
348 io.post([&]() { createSensors(io, objectServer, sensors, systemBus); });
349
350 boost::asio::deadline_timer configTimer(io);
351
352 std::function<void(sdbusplus::message::message&)> eventHandler =
353 [&](sdbusplus::message::message& message) {
354 configTimer.expires_from_now(boost::posix_time::seconds(1));
355 // create a timer because normally multiple properties change
356 configTimer.async_wait([&](const boost::system::error_code& ec) {
357 if (ec == boost::asio::error::operation_aborted)
358 {
359 return; // we're being canceled
360 }
361 createSensors(io, objectServer, sensors, systemBus);
362 if (sensors.empty())
363 {
364 std::cout << "Configuration not detected\n";
365 }
366 });
367 };
368
369 sdbusplus::bus::match::match match(
370 static_cast<sdbusplus::bus::bus&>(*systemBus),
371 "type='signal',member='PropertiesChanged',path_namespace='" +
372 std::string(inventoryPath) + "',arg0namespace='" + configInterface +
373 "'",
374 eventHandler);
375
376 io.run();
377}