blob: 6d94bd214d442c5d0ff32e4b39b252f9e1d54566 [file] [log] [blame]
Yuan Li445efe32019-06-14 22:58:32 +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#include "MCUTempSensor.hpp"
17
18#include "Utils.hpp"
19#include "VariantVisitors.hpp"
20
21#include <math.h>
22
23#include <boost/algorithm/string.hpp>
24#include <boost/algorithm/string/predicate.hpp>
25#include <boost/algorithm/string/replace.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070026#include <boost/container/flat_map.hpp>
James Feist38fb5982020-05-28 10:09:54 -070027#include <sdbusplus/asio/connection.hpp>
28#include <sdbusplus/asio/object_server.hpp>
29#include <sdbusplus/bus/match.hpp>
30
Yuan Li445efe32019-06-14 22:58:32 +080031#include <chrono>
Patrick Venture96e97db2019-10-31 13:44:38 -070032#include <functional>
Yuan Li445efe32019-06-14 22:58:32 +080033#include <iostream>
34#include <limits>
Patrick Venture96e97db2019-10-31 13:44:38 -070035#include <memory>
Yuan Li445efe32019-06-14 22:58:32 +080036#include <numeric>
Patrick Venture96e97db2019-10-31 13:44:38 -070037#include <string>
Yuan Li445efe32019-06-14 22:58:32 +080038#include <vector>
39
James Feist38fb5982020-05-28 10:09:54 -070040extern "C"
41{
Yuan Li445efe32019-06-14 22:58:32 +080042#include <i2c/smbus.h>
43#include <linux/i2c-dev.h>
44}
45
46constexpr const bool debug = false;
47
48constexpr const char* configInterface =
49 "xyz.openbmc_project.Configuration.MCUTempSensor";
50static constexpr double mcuTempMaxReading = 0xFF;
51static constexpr double mcuTempMinReading = 0;
52
53boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>> sensors;
54
55MCUTempSensor::MCUTempSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
56 boost::asio::io_service& io,
57 const std::string& sensorName,
58 const std::string& sensorConfiguration,
59 sdbusplus::asio::object_server& objectServer,
60 std::vector<thresholds::Threshold>&& thresholdData,
61 uint8_t busId, uint8_t mcuAddress,
62 uint8_t tempReg) :
63 Sensor(boost::replace_all_copy(sensorName, " ", "_"),
64 std::move(thresholdData), sensorConfiguration,
65 "xyz.openbmc_project.Configuration.ExitAirTemp", mcuTempMaxReading,
James Feiste3338522020-09-15 15:40:30 -070066 mcuTempMinReading, conn),
Brad Bishopfbb44ad2019-11-08 09:42:37 -050067 busId(busId), mcuAddress(mcuAddress), tempReg(tempReg),
James Feiste3338522020-09-15 15:40:30 -070068 objectServer(objectServer), waitTimer(io)
Yuan Li445efe32019-06-14 22:58:32 +080069{
70 sensorInterface = objectServer.add_interface(
71 "/xyz/openbmc_project/sensors/temperature/" + name,
72 "xyz.openbmc_project.Sensor.Value");
73
74 if (thresholds::hasWarningInterface(thresholds))
75 {
76 thresholdInterfaceWarning = objectServer.add_interface(
77 "/xyz/openbmc_project/sensors/temperature/" + name,
78 "xyz.openbmc_project.Sensor.Threshold.Warning");
79 }
80 if (thresholds::hasCriticalInterface(thresholds))
81 {
82 thresholdInterfaceCritical = objectServer.add_interface(
83 "/xyz/openbmc_project/sensors/temperature/" + name,
84 "xyz.openbmc_project.Sensor.Threshold.Critical");
85 }
86 association = objectServer.add_interface(
87 "/xyz/openbmc_project/sensors/temperature/" + name,
James Feist2adc95c2019-09-30 14:55:28 -070088 association::interface);
Yuan Li445efe32019-06-14 22:58:32 +080089}
90
91MCUTempSensor::~MCUTempSensor()
92{
93 waitTimer.cancel();
94 objectServer.remove_interface(thresholdInterfaceWarning);
95 objectServer.remove_interface(thresholdInterfaceCritical);
96 objectServer.remove_interface(sensorInterface);
97 objectServer.remove_interface(association);
98}
99
100void MCUTempSensor::init(void)
101{
102 setInitialProperties(dbusConnection);
103 read();
104}
105
106void MCUTempSensor::checkThresholds(void)
107{
108 thresholds::checkThresholds(this);
109}
110
111int MCUTempSensor::getMCURegsInfoWord(uint8_t regs, int16_t* pu16data)
112{
113 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
114 int fd = open(i2cBus.c_str(), O_RDWR);
Yuan Li445efe32019-06-14 22:58:32 +0800115
116 if (fd < 0)
117 {
118 std::cerr << " unable to open i2c device" << i2cBus << " err=" << fd
119 << "\n";
120 return -1;
121 }
122
123 if (ioctl(fd, I2C_SLAVE_FORCE, mcuAddress) < 0)
124 {
125 std::cerr << " unable to set device address\n";
126 close(fd);
127 return -1;
128 }
129
130 unsigned long funcs = 0;
131 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
132 {
133 std::cerr << " not support I2C_FUNCS\n";
134 close(fd);
135 return -1;
136 }
137
138 if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
139 {
140 std::cerr << " not support I2C_FUNC_SMBUS_READ_WORD_DATA\n";
141 close(fd);
142 return -1;
143 }
144
145 *pu16data = i2c_smbus_read_word_data(fd, regs);
146 close(fd);
147
148 if (*pu16data < 0)
149 {
150 std::cerr << " read word data failed at " << static_cast<int>(regs)
151 << "\n";
152 return -1;
153 }
154
155 return 0;
156}
157
158void MCUTempSensor::read(void)
159{
160 static constexpr size_t pollTime = 1; // in seconds
161
162 waitTimer.expires_from_now(boost::posix_time::seconds(pollTime));
163 waitTimer.async_wait([this](const boost::system::error_code& ec) {
164 if (ec == boost::asio::error::operation_aborted)
165 {
166 return; // we're being cancelled
167 }
168 // read timer error
169 else if (ec)
170 {
171 std::cerr << "timer error\n";
172 return;
173 }
174 int16_t temp;
175 int ret = getMCURegsInfoWord(tempReg, &temp);
176 if (ret >= 0)
177 {
178 double v = static_cast<double>(temp) / 1000;
179 if constexpr (debug)
180 {
James Feistb6c0b912019-07-09 12:21:44 -0700181 std::cerr << "Value update to " << v << "raw reading "
Yuan Li445efe32019-06-14 22:58:32 +0800182 << static_cast<int>(temp) << "\n";
183 }
184 updateValue(v);
185 }
186 else
187 {
188 std::cerr << "Invalid read getMCURegsInfoWord\n";
James Feist961bf092020-07-01 16:38:12 -0700189 incrementError();
Yuan Li445efe32019-06-14 22:58:32 +0800190 }
191 read();
192 });
193}
194
195void createSensors(
196 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
197 boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>>&
198 sensors,
199 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
200{
201 if (!dbusConnection)
202 {
203 std::cerr << "Connection not created\n";
204 return;
205 }
206
207 dbusConnection->async_method_call(
208 [&io, &objectServer, &dbusConnection, &sensors](
209 boost::system::error_code ec, const ManagedObjectType& resp) {
210 if (ec)
211 {
212 std::cerr << "Error contacting entity manager\n";
213 return;
214 }
215 for (const auto& pathPair : resp)
216 {
217 for (const auto& entry : pathPair.second)
218 {
219 if (entry.first != configInterface)
220 {
221 continue;
222 }
223 std::string name =
224 loadVariant<std::string>(entry.second, "Name");
225
226 std::vector<thresholds::Threshold> sensorThresholds;
227 if (!parseThresholdsFromConfig(pathPair.second,
228 sensorThresholds))
229 {
230 std::cerr << "error populating thresholds for " << name
231 << "\n";
232 }
233
234 uint8_t busId = loadVariant<uint8_t>(entry.second, "Bus");
235
236 uint8_t mcuAddress =
237 loadVariant<uint8_t>(entry.second, "Address");
238
239 uint8_t tempReg = loadVariant<uint8_t>(entry.second, "Reg");
240
241 std::string sensorClass =
242 loadVariant<std::string>(entry.second, "Class");
243
244 if constexpr (debug)
245 {
246 std::cerr
247 << "Configuration parsed for \n\t" << entry.first
248 << "\n"
249 << "with\n"
250 << "\tName: " << name << "\n"
251 << "\tBus: " << static_cast<int>(busId) << "\n"
252 << "\tAddress: " << static_cast<int>(mcuAddress)
253 << "\n"
254 << "\tReg: " << static_cast<int>(tempReg) << "\n"
255 << "\tClass: " << sensorClass << "\n";
256 }
257
258 auto& sensor = sensors[name];
259
260 sensor = std::make_unique<MCUTempSensor>(
261 dbusConnection, io, name, pathPair.first, objectServer,
262 std::move(sensorThresholds), busId, mcuAddress,
263 tempReg);
264
265 sensor->init();
266 }
267 }
268 },
269 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
270 "GetManagedObjects");
271}
272
James Feistb6c0b912019-07-09 12:21:44 -0700273int main()
Yuan Li445efe32019-06-14 22:58:32 +0800274{
275 boost::asio::io_service io;
276 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
277 systemBus->request_name("xyz.openbmc_project.MCUTempSensor");
278 sdbusplus::asio::object_server objectServer(systemBus);
279
280 io.post([&]() { createSensors(io, objectServer, sensors, systemBus); });
281
282 boost::asio::deadline_timer configTimer(io);
283
284 std::function<void(sdbusplus::message::message&)> eventHandler =
James Feistb6c0b912019-07-09 12:21:44 -0700285 [&](sdbusplus::message::message&) {
Yuan Li445efe32019-06-14 22:58:32 +0800286 configTimer.expires_from_now(boost::posix_time::seconds(1));
287 // create a timer because normally multiple properties change
288 configTimer.async_wait([&](const boost::system::error_code& ec) {
289 if (ec == boost::asio::error::operation_aborted)
290 {
291 return; // we're being canceled
292 }
293 // config timer error
294 else if (ec)
295 {
296 std::cerr << "timer error\n";
297 return;
298 }
299 createSensors(io, objectServer, sensors, systemBus);
300 if (sensors.empty())
301 {
302 std::cout << "Configuration not detected\n";
303 }
304 });
305 };
306
307 sdbusplus::bus::match::match configMatch(
308 static_cast<sdbusplus::bus::bus&>(*systemBus),
309 "type='signal',member='PropertiesChanged',"
310 "path_namespace='" +
311 std::string(inventoryPath) +
312 "',"
313 "arg0namespace='" +
314 configInterface + "'",
315 eventHandler);
316
317 io.run();
318}