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