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