blob: af1f725faa20b37c7ab3df4fb8dfa08b94f1d562 [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 }
James Feistfc94b212019-02-06 16:14:51 -0800169 else if (readState == PowerState::biosPost && !hasBiosPost())
170 {
171 return;
172 }
James Feist6ef20402019-01-07 16:45:08 -0800173 thresholds::checkThresholds(this);
174}
175
176void IpmbSensor::read(void)
177{
178 static constexpr size_t pollTime = 1; // in seconds
179
180 waitTimer.expires_from_now(boost::posix_time::seconds(pollTime));
181 waitTimer.async_wait([this](const boost::system::error_code& ec) {
182 if (ec == boost::asio::error::operation_aborted)
183 {
184 return; // we're being canceled
185 }
186 if (!isPowerOn() && readState == PowerState::on)
187 {
188 updateValue(0);
189 read();
190 return;
191 }
192 dbusConnection->async_method_call(
193 [this](boost::system::error_code ec,
194 const IpmbMethodType& response) {
195 const int& status = std::get<0>(response);
196 if (ec || status)
197 {
198 std::cerr << "Error reading from device: " << name << "\n";
199 updateValue(0);
200 read();
201 return;
202 }
203 if (!isPowerOn() && readState == PowerState::on)
204 {
205 updateValue(0);
206 read();
207 return;
208 }
209 const std::vector<uint8_t>& data = std::get<5>(response);
210 if constexpr (debug)
211 {
212 std::cout << name << ": ";
213 for (size_t d : data)
214 {
215 std::cout << d << " ";
216 }
217 std::cout << "\n";
218 }
219 uint16_t value = 0;
220 if (type == IpmbType::meSensor)
221 {
222 if (data.empty())
223 {
224 std::cerr << "Invalid data from device: " << name
225 << "\n";
226 read();
227 return;
228 }
229 value = data[0];
230 }
231 else if (type == IpmbType::PXE1410CVR ||
232 type == IpmbType::IR38363VR)
233 {
234 if (data.size() < 4)
235 {
236 std::cerr << "Invalid data from device: " << name
237 << "\n";
238 read();
239 return;
240 }
241 // format based on the 11 bit linear data format
242 value = ((data[4] << 8) | data[3]) >> 3;
243 }
244 else if (type == IpmbType::mpsVR)
245 {
246 if (data.size() < 4)
247 {
248 std::cerr << "Invalid data from device: " << name
249 << "\n";
250 read();
251 return;
252 }
253 value = data[3];
254 }
255 else
256 {
257 throw std::runtime_error("Invalid sensor type");
258 }
259 updateValue(value);
260 read();
261 },
262 "xyz.openbmc_project.Ipmi.Channel.Ipmb",
263 "/xyz/openbmc_project/Ipmi/Channel/Ipmb", "org.openbmc.Ipmb",
264 "sendRequest", commandAddress, netfn, lun, command, commandData);
265 });
266}
267void createSensors(
268 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
269 boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>>&
270 sensors,
271 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
272{
273 if (!dbusConnection)
274 {
275 std::cerr << "Connection not created\n";
276 return;
277 }
278 dbusConnection->async_method_call(
279 [&](boost::system::error_code ec, const ManagedObjectType& resp) {
280 if (ec)
281 {
282 std::cerr << "Error contacting entity manager\n";
283 return;
284 }
285 for (const auto& pathPair : resp)
286 {
287 for (const auto& entry : pathPair.second)
288 {
289 if (entry.first != configInterface)
290 {
291 continue;
292 }
293 std::string name =
294 loadVariant<std::string>(entry.second, "Name");
295
296 std::vector<thresholds::Threshold> sensorThresholds;
297 if (!parseThresholdsFromConfig(pathPair.second,
298 sensorThresholds))
299 {
300 std::cerr << "error populating thresholds for " << name
301 << "\n";
302 }
303 uint8_t deviceAddress =
304 loadVariant<uint8_t>(entry.second, "Address");
305
306 std::string sensorClass =
307 loadVariant<std::string>(entry.second, "Class");
308 auto& sensor = sensors[name];
309 sensor = std::make_unique<IpmbSensor>(
310 dbusConnection, io, name, pathPair.first, objectServer,
311 std::move(sensorThresholds), deviceAddress);
312
James Feistfc94b212019-02-06 16:14:51 -0800313 auto findPowerState = entry.second.find("PowerState");
314
315 if (findPowerState != entry.second.end())
316 {
317 std::string powerState = std::visit(
318 VariantToStringVisitor(), findPowerState->second);
319
320 setReadState(powerState, sensor->readState);
321 }
322
James Feist6ef20402019-01-07 16:45:08 -0800323 if (sensorClass == "PxeBridgeTemp")
324 {
325 sensor->type = IpmbType::PXE1410CVR;
326 }
327 else if (sensorClass == "IRBridgeTemp")
328 {
329 sensor->type = IpmbType::IR38363VR;
330 }
331 else if (sensorClass == "MpsBridgeTemp")
332 {
333 sensor->type = IpmbType::mpsVR;
334 }
335 else if (sensorClass == "METemp")
336 {
337 sensor->type = IpmbType::meSensor;
338 }
339 else
340 {
341 std::cerr << "Invalid class " << sensorClass << "\n";
342 continue;
343 }
344 sensor->init();
345 }
346 }
347 },
348 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
349 "GetManagedObjects");
350}
351
352int main(int argc, char** argv)
353{
354
355 boost::asio::io_service io;
356 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
357 systemBus->request_name("xyz.openbmc_project.IpmbSensor");
358 sdbusplus::asio::object_server objectServer(systemBus);
359 boost::container::flat_map<std::string, std::unique_ptr<IpmbSensor>>
360 sensors;
361
362 io.post([&]() { createSensors(io, objectServer, sensors, systemBus); });
363
364 boost::asio::deadline_timer configTimer(io);
365
366 std::function<void(sdbusplus::message::message&)> eventHandler =
367 [&](sdbusplus::message::message& message) {
368 configTimer.expires_from_now(boost::posix_time::seconds(1));
369 // create a timer because normally multiple properties change
370 configTimer.async_wait([&](const boost::system::error_code& ec) {
371 if (ec == boost::asio::error::operation_aborted)
372 {
373 return; // we're being canceled
374 }
375 createSensors(io, objectServer, sensors, systemBus);
376 if (sensors.empty())
377 {
378 std::cout << "Configuration not detected\n";
379 }
380 });
381 };
382
383 sdbusplus::bus::match::match match(
384 static_cast<sdbusplus::bus::bus&>(*systemBus),
385 "type='signal',member='PropertiesChanged',path_namespace='" +
386 std::string(inventoryPath) + "',arg0namespace='" + configInterface +
387 "'",
388 eventHandler);
389
390 io.run();
391}