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 <unistd.h>
18 
19 #include <HwmonTempSensor.hpp>
20 #include <boost/asio/read_until.hpp>
21 #include <boost/date_time/posix_time/posix_time.hpp>
22 #include <sdbusplus/asio/connection.hpp>
23 #include <sdbusplus/asio/object_server.hpp>
24 
25 #include <charconv>
26 #include <iostream>
27 #include <istream>
28 #include <limits>
29 #include <memory>
30 #include <string>
31 #include <vector>
32 
33 // Temperatures are read in milli degrees Celsius, we need degrees Celsius.
34 // Pressures are read in kilopascal, we need Pascals.  On D-Bus for Open BMC
35 // we use the International System of Units without prefixes.
36 // Links to the kernel documentation:
37 // https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface
38 // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-iio
39 // For IIO RAW sensors we get a raw_value, an offset, and scale to compute
40 // the value = (raw_value + offset) * scale
41 
42 HwmonTempSensor::HwmonTempSensor(
43     const std::string& path, const std::string& objectType,
44     sdbusplus::asio::object_server& objectServer,
45     std::shared_ptr<sdbusplus::asio::connection>& conn,
46     boost::asio::io_service& io, const std::string& sensorName,
47     std::vector<thresholds::Threshold>&& thresholdsIn,
48     const struct SensorParams& thisSensorParameters, const float pollRate,
49     const std::string& sensorConfiguration, const PowerState powerState) :
50     Sensor(boost::replace_all_copy(sensorName, " ", "_"),
51            std::move(thresholdsIn), sensorConfiguration, objectType, false,
52            false, thisSensorParameters.maxValue, thisSensorParameters.minValue,
53            conn, powerState),
54     objServer(objectServer),
55     inputDev(io, path, boost::asio::random_access_file::read_only),
56     waitTimer(io), path(path), offsetValue(thisSensorParameters.offsetValue),
57     scaleValue(thisSensorParameters.scaleValue),
58     sensorPollMs(static_cast<unsigned int>(pollRate * 1000))
59 {
60     sensorInterface = objectServer.add_interface(
61         "/xyz/openbmc_project/sensors/" + thisSensorParameters.typeName + "/" +
62             name,
63         "xyz.openbmc_project.Sensor.Value");
64 
65     for (const auto& threshold : thresholds)
66     {
67         std::string interface = thresholds::getInterface(threshold.level);
68         thresholdInterfaces[static_cast<size_t>(threshold.level)] =
69             objectServer.add_interface("/xyz/openbmc_project/sensors/" +
70                                            thisSensorParameters.typeName + "/" +
71                                            name,
72                                        interface);
73     }
74     association = objectServer.add_interface("/xyz/openbmc_project/sensors/" +
75                                                  thisSensorParameters.typeName +
76                                                  "/" + name,
77                                              association::interface);
78     setInitialProperties(thisSensorParameters.units);
79 }
80 
81 HwmonTempSensor::~HwmonTempSensor()
82 {
83     // close the input dev to cancel async operations
84     inputDev.close();
85     waitTimer.cancel();
86     for (const auto& iface : thresholdInterfaces)
87     {
88         objServer.remove_interface(iface);
89     }
90     objServer.remove_interface(sensorInterface);
91     objServer.remove_interface(association);
92 }
93 
94 void HwmonTempSensor::setupRead(void)
95 {
96     if (!readingStateGood())
97     {
98         markAvailable(false);
99         updateValue(std::numeric_limits<double>::quiet_NaN());
100         restartRead();
101         return;
102     }
103 
104     std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this();
105     inputDev.async_read_some_at(
106         0, boost::asio::buffer(readBuf),
107         [weakRef](const boost::system::error_code& ec, std::size_t bytesRead) {
108         std::shared_ptr<HwmonTempSensor> self = weakRef.lock();
109         if (self)
110         {
111             self->handleResponse(ec, bytesRead);
112         }
113         });
114 }
115 
116 void HwmonTempSensor::restartRead()
117 {
118     std::weak_ptr<HwmonTempSensor> weakRef = weak_from_this();
119     waitTimer.expires_from_now(boost::posix_time::milliseconds(sensorPollMs));
120     waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
121         if (ec == boost::asio::error::operation_aborted)
122         {
123             return; // we're being canceled
124         }
125         std::shared_ptr<HwmonTempSensor> self = weakRef.lock();
126         if (!self)
127         {
128             return;
129         }
130         self->setupRead();
131     });
132 }
133 
134 void HwmonTempSensor::handleResponse(const boost::system::error_code& err,
135                                      size_t bytesRead)
136 {
137     if ((err == boost::system::errc::bad_file_descriptor) ||
138         (err == boost::asio::error::misc_errors::not_found))
139     {
140         std::cerr << "Hwmon temp sensor " << name << " removed " << path
141                   << "\n";
142         return; // we're being destroyed
143     }
144 
145     if (!err)
146     {
147         const char* bufEnd = readBuf.data() + bytesRead;
148         int nvalue = 0;
149         std::from_chars_result ret =
150             std::from_chars(readBuf.data(), bufEnd, nvalue);
151         if (ret.ec != std::errc())
152         {
153             incrementError();
154         }
155         else
156         {
157             updateValue((nvalue + offsetValue) * scaleValue);
158         }
159     }
160     else
161     {
162         incrementError();
163     }
164 
165     restartRead();
166 }
167 
168 void HwmonTempSensor::checkThresholds(void)
169 {
170     thresholds::checkThresholds(this);
171 }
172