1 /* 2 // Copyright (c) 2017 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 "ADCSensor.hpp" 18 19 #include "SensorPaths.hpp" 20 #include "Thresholds.hpp" 21 #include "Utils.hpp" 22 #include "sensor.hpp" 23 24 #include <fcntl.h> 25 26 #include <boost/asio/error.hpp> 27 #include <boost/asio/io_context.hpp> 28 #include <boost/asio/read_until.hpp> 29 #include <boost/asio/streambuf.hpp> 30 #include <sdbusplus/asio/connection.hpp> 31 #include <sdbusplus/asio/object_server.hpp> 32 33 #include <chrono> 34 #include <cmath> 35 #include <cstddef> 36 #include <iostream> 37 #include <memory> 38 #include <optional> 39 #include <stdexcept> 40 #include <string> 41 #include <utility> 42 #include <vector> 43 44 // scaling factor from hwmon 45 static constexpr unsigned int sensorScaleFactor = 1000; 46 47 static constexpr double roundFactor = 10000; // 3 decimal places 48 static constexpr double maxVoltageReading = 1.8; // pre sensor scaling 49 static constexpr double minVoltageReading = 0; 50 51 ADCSensor::ADCSensor( 52 const std::string& path, sdbusplus::asio::object_server& objectServer, 53 std::shared_ptr<sdbusplus::asio::connection>& conn, 54 boost::asio::io_context& io, const std::string& sensorName, 55 std::vector<thresholds::Threshold>&& thresholdsIn, const double scaleFactor, 56 const float pollRate, PowerState readState, 57 const std::string& sensorConfiguration, 58 std::optional<BridgeGpio>&& bridgeGpio) : 59 Sensor(escapeName(sensorName), std::move(thresholdsIn), sensorConfiguration, 60 "ADC", false, false, maxVoltageReading / scaleFactor, 61 minVoltageReading / scaleFactor, conn, readState), 62 objServer(objectServer), inputDev(io), waitTimer(io), path(path), 63 scaleFactor(scaleFactor), 64 sensorPollMs(static_cast<unsigned int>(pollRate * 1000)), 65 bridgeGpio(std::move(bridgeGpio)), thresholdTimer(io) 66 { 67 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 68 int fd = open(path.c_str(), O_RDONLY); 69 if (fd < 0) 70 { 71 std::cerr << "unable to open acd device \n"; 72 } 73 74 inputDev.assign(fd); 75 76 sensorInterface = objectServer.add_interface( 77 "/xyz/openbmc_project/sensors/voltage/" + name, 78 "xyz.openbmc_project.Sensor.Value"); 79 for (const auto& threshold : thresholds) 80 { 81 std::string interface = thresholds::getInterface(threshold.level); 82 thresholdInterfaces[static_cast<size_t>(threshold.level)] = 83 objectServer.add_interface( 84 "/xyz/openbmc_project/sensors/voltage/" + name, interface); 85 } 86 association = objectServer.add_interface( 87 "/xyz/openbmc_project/sensors/voltage/" + name, association::interface); 88 setInitialProperties(sensor_paths::unitVolts); 89 } 90 91 ADCSensor::~ADCSensor() 92 { 93 // close the input dev to cancel async operations 94 inputDev.close(); 95 waitTimer.cancel(); 96 97 for (const auto& iface : thresholdInterfaces) 98 { 99 objServer.remove_interface(iface); 100 } 101 objServer.remove_interface(sensorInterface); 102 objServer.remove_interface(association); 103 } 104 105 void ADCSensor::setupRead() 106 { 107 std::shared_ptr<boost::asio::streambuf> buffer = 108 std::make_shared<boost::asio::streambuf>(); 109 110 std::weak_ptr<ADCSensor> weakRef = weak_from_this(); 111 112 if (bridgeGpio.has_value()) 113 { 114 (*bridgeGpio).set(1); 115 // In case a channel has a bridge circuit,we have to turn the bridge on 116 // prior to reading a value at least for one scan cycle to get a valid 117 // value. Guarantee that the HW signal can be stable, the HW signal 118 // could be instability. 119 waitTimer.expires_after( 120 std::chrono::milliseconds(bridgeGpio->setupTimeMs)); 121 waitTimer.async_wait( 122 [weakRef, buffer](const boost::system::error_code& ec) { 123 std::shared_ptr<ADCSensor> self = weakRef.lock(); 124 if (ec == boost::asio::error::operation_aborted) 125 { 126 return; // we're being canceled 127 } 128 129 if (self) 130 { 131 boost::asio::async_read_until( 132 self->inputDev, *buffer, '\n', 133 [weakRef, buffer](const boost::system::error_code& ec, 134 std::size_t /*bytes_transfered*/) { 135 std::shared_ptr<ADCSensor> self = weakRef.lock(); 136 if (self) 137 { 138 self->readBuf = buffer; 139 self->handleResponse(ec); 140 } 141 }); 142 } 143 }); 144 } 145 else 146 { 147 boost::asio::async_read_until( 148 inputDev, *buffer, '\n', 149 [weakRef, buffer](const boost::system::error_code& ec, 150 std::size_t /*bytes_transfered*/) { 151 std::shared_ptr<ADCSensor> self = weakRef.lock(); 152 if (self) 153 { 154 self->readBuf = buffer; 155 self->handleResponse(ec); 156 } 157 }); 158 } 159 } 160 161 void ADCSensor::handleResponse(const boost::system::error_code& err) 162 { 163 std::weak_ptr<ADCSensor> weakRef = weak_from_this(); 164 165 if (err == boost::system::errc::bad_file_descriptor) 166 { 167 return; // we're being destroyed 168 } 169 std::istream responseStream(readBuf.get()); 170 171 if (!err) 172 { 173 std::string response; 174 std::getline(responseStream, response); 175 176 // todo read scaling factors from configuration 177 try 178 { 179 rawValue = std::stod(response); 180 double nvalue = (rawValue / sensorScaleFactor) / scaleFactor; 181 nvalue = std::round(nvalue * roundFactor) / roundFactor; 182 updateValue(nvalue); 183 } 184 catch (const std::invalid_argument&) 185 { 186 incrementError(); 187 } 188 } 189 else 190 { 191 incrementError(); 192 } 193 194 responseStream.clear(); 195 inputDev.close(); 196 if (bridgeGpio.has_value()) 197 { 198 (*bridgeGpio).set(0); 199 } 200 201 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) 202 int fd = open(path.c_str(), O_RDONLY); 203 if (fd < 0) 204 { 205 std::cerr << "adcsensor " << name << " failed to open " << path << "\n"; 206 return; // we're no longer valid 207 } 208 inputDev.assign(fd); 209 waitTimer.expires_after(std::chrono::milliseconds(sensorPollMs)); 210 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) { 211 std::shared_ptr<ADCSensor> self = weakRef.lock(); 212 if (ec == boost::asio::error::operation_aborted) 213 { 214 if (self) 215 { 216 std::cerr << "adcsensor " << self->name << " read cancelled\n"; 217 } 218 else 219 { 220 std::cerr << "adcsensor read cancelled no self\n"; 221 } 222 return; // we're being canceled 223 } 224 225 if (self) 226 { 227 self->setupRead(); 228 } 229 else 230 { 231 std::cerr << "adcsensor weakref no self\n"; 232 } 233 }); 234 } 235 236 void ADCSensor::checkThresholds() 237 { 238 if (!readingStateGood()) 239 { 240 return; 241 } 242 243 thresholds::checkThresholdsPowerDelay(weak_from_this(), thresholdTimer); 244 } 245