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