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