blob: 79a2959ff00b799bdb6fcd1291b9c3cba84235d2 [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>
Patrick Venture96e97db2019-10-31 13:44:38 -070020#include <boost/container/flat_map.hpp>
James Feist38fb5982020-05-28 10:09:54 -070021#include <sdbusplus/asio/connection.hpp>
22#include <sdbusplus/asio/object_server.hpp>
23#include <sdbusplus/bus/match.hpp>
24
Yuan Li445efe32019-06-14 22:58:32 +080025#include <chrono>
Ed Tanous8a57ec02020-10-09 12:46:52 -070026#include <cmath>
Patrick Venture96e97db2019-10-31 13:44:38 -070027#include <functional>
Yuan Li445efe32019-06-14 22:58:32 +080028#include <iostream>
29#include <limits>
Patrick Venture96e97db2019-10-31 13:44:38 -070030#include <memory>
Yuan Li445efe32019-06-14 22:58:32 +080031#include <numeric>
Patrick Venture96e97db2019-10-31 13:44:38 -070032#include <string>
Yuan Li445efe32019-06-14 22:58:32 +080033#include <vector>
34
James Feist38fb5982020-05-28 10:09:54 -070035extern "C"
36{
Yuan Li445efe32019-06-14 22:58:32 +080037#include <i2c/smbus.h>
38#include <linux/i2c-dev.h>
39}
40
41constexpr const bool debug = false;
42
43constexpr const char* configInterface =
44 "xyz.openbmc_project.Configuration.MCUTempSensor";
45static constexpr double mcuTempMaxReading = 0xFF;
46static constexpr double mcuTempMinReading = 0;
47
48boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>> sensors;
49
50MCUTempSensor::MCUTempSensor(std::shared_ptr<sdbusplus::asio::connection>& conn,
51 boost::asio::io_service& io,
52 const std::string& sensorName,
53 const std::string& sensorConfiguration,
54 sdbusplus::asio::object_server& objectServer,
55 std::vector<thresholds::Threshold>&& thresholdData,
56 uint8_t busId, uint8_t mcuAddress,
57 uint8_t tempReg) :
Zhikui Renda98f092021-11-01 09:41:08 -070058 Sensor(escapeName(sensorName), std::move(thresholdData),
59 sensorConfiguration, "xyz.openbmc_project.Configuration.ExitAirTemp",
60 false, false, mcuTempMaxReading, mcuTempMinReading, conn),
Brad Bishopfbb44ad2019-11-08 09:42:37 -050061 busId(busId), mcuAddress(mcuAddress), tempReg(tempReg),
James Feiste3338522020-09-15 15:40:30 -070062 objectServer(objectServer), waitTimer(io)
Yuan Li445efe32019-06-14 22:58:32 +080063{
64 sensorInterface = objectServer.add_interface(
65 "/xyz/openbmc_project/sensors/temperature/" + name,
66 "xyz.openbmc_project.Sensor.Value");
67
Jayashree Dhanapal56678082022-01-04 17:27:20 +053068 for (const auto& threshold : thresholds)
Yuan Li445efe32019-06-14 22:58:32 +080069 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +053070 std::string interface = thresholds::getInterface(threshold.level);
71 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
72 objectServer.add_interface(
73 "/xyz/openbmc_project/sensors/temperature/" + name, interface);
Yuan Li445efe32019-06-14 22:58:32 +080074 }
75 association = objectServer.add_interface(
76 "/xyz/openbmc_project/sensors/temperature/" + name,
James Feist2adc95c2019-09-30 14:55:28 -070077 association::interface);
Yuan Li445efe32019-06-14 22:58:32 +080078}
79
80MCUTempSensor::~MCUTempSensor()
81{
82 waitTimer.cancel();
Jayashree Dhanapal56678082022-01-04 17:27:20 +053083 for (const auto& iface : thresholdInterfaces)
84 {
85 objectServer.remove_interface(iface);
86 }
Yuan Li445efe32019-06-14 22:58:32 +080087 objectServer.remove_interface(sensorInterface);
88 objectServer.remove_interface(association);
89}
90
91void MCUTempSensor::init(void)
92{
Zev Weiss6b6891c2021-04-22 02:46:21 -050093 setInitialProperties(dbusConnection, sensor_paths::unitDegreesC);
Yuan Li445efe32019-06-14 22:58:32 +080094 read();
95}
96
97void MCUTempSensor::checkThresholds(void)
98{
99 thresholds::checkThresholds(this);
100}
101
102int MCUTempSensor::getMCURegsInfoWord(uint8_t regs, int16_t* pu16data)
103{
104 std::string i2cBus = "/dev/i2c-" + std::to_string(busId);
105 int fd = open(i2cBus.c_str(), O_RDWR);
Yuan Li445efe32019-06-14 22:58:32 +0800106
107 if (fd < 0)
108 {
109 std::cerr << " unable to open i2c device" << i2cBus << " err=" << fd
110 << "\n";
111 return -1;
112 }
113
114 if (ioctl(fd, I2C_SLAVE_FORCE, mcuAddress) < 0)
115 {
116 std::cerr << " unable to set device address\n";
117 close(fd);
118 return -1;
119 }
120
121 unsigned long funcs = 0;
122 if (ioctl(fd, I2C_FUNCS, &funcs) < 0)
123 {
124 std::cerr << " not support I2C_FUNCS\n";
125 close(fd);
126 return -1;
127 }
128
129 if (!(funcs & I2C_FUNC_SMBUS_READ_WORD_DATA))
130 {
131 std::cerr << " not support I2C_FUNC_SMBUS_READ_WORD_DATA\n";
132 close(fd);
133 return -1;
134 }
135
136 *pu16data = i2c_smbus_read_word_data(fd, regs);
137 close(fd);
138
139 if (*pu16data < 0)
140 {
141 std::cerr << " read word data failed at " << static_cast<int>(regs)
142 << "\n";
143 return -1;
144 }
145
146 return 0;
147}
148
149void MCUTempSensor::read(void)
150{
151 static constexpr size_t pollTime = 1; // in seconds
152
153 waitTimer.expires_from_now(boost::posix_time::seconds(pollTime));
154 waitTimer.async_wait([this](const boost::system::error_code& ec) {
155 if (ec == boost::asio::error::operation_aborted)
156 {
157 return; // we're being cancelled
158 }
159 // read timer error
Ed Tanous8a57ec02020-10-09 12:46:52 -0700160 if (ec)
Yuan Li445efe32019-06-14 22:58:32 +0800161 {
162 std::cerr << "timer error\n";
163 return;
164 }
165 int16_t temp;
166 int ret = getMCURegsInfoWord(tempReg, &temp);
167 if (ret >= 0)
168 {
169 double v = static_cast<double>(temp) / 1000;
170 if constexpr (debug)
171 {
James Feistb6c0b912019-07-09 12:21:44 -0700172 std::cerr << "Value update to " << v << "raw reading "
Yuan Li445efe32019-06-14 22:58:32 +0800173 << static_cast<int>(temp) << "\n";
174 }
175 updateValue(v);
176 }
177 else
178 {
179 std::cerr << "Invalid read getMCURegsInfoWord\n";
James Feist961bf092020-07-01 16:38:12 -0700180 incrementError();
Yuan Li445efe32019-06-14 22:58:32 +0800181 }
182 read();
183 });
184}
185
186void createSensors(
187 boost::asio::io_service& io, sdbusplus::asio::object_server& objectServer,
188 boost::container::flat_map<std::string, std::unique_ptr<MCUTempSensor>>&
189 sensors,
190 std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
191{
192 if (!dbusConnection)
193 {
194 std::cerr << "Connection not created\n";
195 return;
196 }
197
198 dbusConnection->async_method_call(
199 [&io, &objectServer, &dbusConnection, &sensors](
200 boost::system::error_code ec, const ManagedObjectType& resp) {
201 if (ec)
202 {
203 std::cerr << "Error contacting entity manager\n";
204 return;
205 }
206 for (const auto& pathPair : resp)
207 {
208 for (const auto& entry : pathPair.second)
209 {
210 if (entry.first != configInterface)
211 {
212 continue;
213 }
214 std::string name =
215 loadVariant<std::string>(entry.second, "Name");
216
217 std::vector<thresholds::Threshold> sensorThresholds;
218 if (!parseThresholdsFromConfig(pathPair.second,
219 sensorThresholds))
220 {
221 std::cerr << "error populating thresholds for " << name
222 << "\n";
223 }
224
225 uint8_t busId = loadVariant<uint8_t>(entry.second, "Bus");
226
227 uint8_t mcuAddress =
228 loadVariant<uint8_t>(entry.second, "Address");
229
230 uint8_t tempReg = loadVariant<uint8_t>(entry.second, "Reg");
231
232 std::string sensorClass =
233 loadVariant<std::string>(entry.second, "Class");
234
235 if constexpr (debug)
236 {
237 std::cerr
238 << "Configuration parsed for \n\t" << entry.first
239 << "\n"
240 << "with\n"
241 << "\tName: " << name << "\n"
242 << "\tBus: " << static_cast<int>(busId) << "\n"
243 << "\tAddress: " << static_cast<int>(mcuAddress)
244 << "\n"
245 << "\tReg: " << static_cast<int>(tempReg) << "\n"
246 << "\tClass: " << sensorClass << "\n";
247 }
248
249 auto& sensor = sensors[name];
250
251 sensor = std::make_unique<MCUTempSensor>(
252 dbusConnection, io, name, pathPair.first, objectServer,
253 std::move(sensorThresholds), busId, mcuAddress,
254 tempReg);
255
256 sensor->init();
257 }
258 }
259 },
260 entityManagerName, "/", "org.freedesktop.DBus.ObjectManager",
261 "GetManagedObjects");
262}
263
James Feistb6c0b912019-07-09 12:21:44 -0700264int main()
Yuan Li445efe32019-06-14 22:58:32 +0800265{
266 boost::asio::io_service io;
267 auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
268 systemBus->request_name("xyz.openbmc_project.MCUTempSensor");
269 sdbusplus::asio::object_server objectServer(systemBus);
270
271 io.post([&]() { createSensors(io, objectServer, sensors, systemBus); });
272
273 boost::asio::deadline_timer configTimer(io);
274
275 std::function<void(sdbusplus::message::message&)> eventHandler =
James Feistb6c0b912019-07-09 12:21:44 -0700276 [&](sdbusplus::message::message&) {
Yuan Li445efe32019-06-14 22:58:32 +0800277 configTimer.expires_from_now(boost::posix_time::seconds(1));
278 // create a timer because normally multiple properties change
279 configTimer.async_wait([&](const boost::system::error_code& ec) {
280 if (ec == boost::asio::error::operation_aborted)
281 {
282 return; // we're being canceled
283 }
284 // config timer error
Ed Tanous8a57ec02020-10-09 12:46:52 -0700285 if (ec)
Yuan Li445efe32019-06-14 22:58:32 +0800286 {
287 std::cerr << "timer error\n";
288 return;
289 }
290 createSensors(io, objectServer, sensors, systemBus);
291 if (sensors.empty())
292 {
293 std::cout << "Configuration not detected\n";
294 }
295 });
296 };
297
298 sdbusplus::bus::match::match configMatch(
299 static_cast<sdbusplus::bus::bus&>(*systemBus),
300 "type='signal',member='PropertiesChanged',"
301 "path_namespace='" +
302 std::string(inventoryPath) +
303 "',"
304 "arg0namespace='" +
305 configInterface + "'",
306 eventHandler);
307
Bruce Lee1263c3d2021-06-04 15:16:33 +0800308 setupManufacturingModeMatch(*systemBus);
Yuan Li445efe32019-06-14 22:58:32 +0800309 io.run();
Zhikui Ren8685b172021-06-29 15:16:52 -0700310 return 0;
Yuan Li445efe32019-06-14 22:58:32 +0800311}