blob: a88c248fd958c28d93a82388c4cec36d416dfccf [file] [log] [blame]
James Feist6714a252018-09-10 15:26:18 -07001/*
2// Copyright (c) 2018 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 <TachSensor.hpp>
20#include <Utils.hpp>
21#include <boost/algorithm/string/predicate.hpp>
22#include <boost/algorithm/string/replace.hpp>
23#include <boost/date_time/posix_time/posix_time.hpp>
James Feist7bc2bab2018-10-26 14:09:45 -070024#include <fstream>
James Feist6714a252018-09-10 15:26:18 -070025#include <iostream>
26#include <limits>
27#include <sdbusplus/asio/connection.hpp>
28#include <sdbusplus/asio/object_server.hpp>
29#include <string>
30
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070031static constexpr unsigned int pwmPollMs = 500;
32static constexpr size_t warnAfterErrorCount = 10;
James Feist6714a252018-09-10 15:26:18 -070033
James Feistd8705872019-02-08 13:26:09 -080034TachSensor::TachSensor(const std::string& path, const std::string& objectType,
35 sdbusplus::asio::object_server& objectServer,
36 std::shared_ptr<sdbusplus::asio::connection>& conn,
37 std::unique_ptr<PresenceSensor>&& presence,
38 const std::shared_ptr<RedundancySensor>& redundancy,
39 boost::asio::io_service& io, const std::string& fanName,
40 std::vector<thresholds::Threshold>&& _thresholds,
41 const std::string& sensorConfiguration,
42 const std::pair<size_t, size_t>& limits) :
James Feistdc6c55f2018-10-31 12:53:20 -070043 Sensor(boost::replace_all_copy(fanName, " ", "_"), path,
James Feist87d713a2018-12-06 16:06:24 -080044 std::move(_thresholds), sensorConfiguration, objectType,
45 limits.second, limits.first),
James Feist71d31b22019-01-02 16:57:54 -080046 objServer(objectServer), presence(std::move(presence)),
47 redundancy(redundancy), inputDev(io, open(path.c_str(), O_RDONLY)),
48 waitTimer(io), errCount(0)
James Feist6714a252018-09-10 15:26:18 -070049{
James Feist251c7822018-09-12 12:54:15 -070050 sensorInterface = objectServer.add_interface(
51 "/xyz/openbmc_project/sensors/fan_tach/" + name,
52 "xyz.openbmc_project.Sensor.Value");
53
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070054 if (thresholds::hasWarningInterface(thresholds))
James Feist6714a252018-09-10 15:26:18 -070055 {
James Feist251c7822018-09-12 12:54:15 -070056 thresholdInterfaceWarning = objectServer.add_interface(
James Feist6714a252018-09-10 15:26:18 -070057 "/xyz/openbmc_project/sensors/fan_tach/" + name,
58 "xyz.openbmc_project.Sensor.Threshold.Warning");
59 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070060 if (thresholds::hasCriticalInterface(thresholds))
James Feist6714a252018-09-10 15:26:18 -070061 {
James Feist251c7822018-09-12 12:54:15 -070062 thresholdInterfaceCritical = objectServer.add_interface(
James Feist6714a252018-09-10 15:26:18 -070063 "/xyz/openbmc_project/sensors/fan_tach/" + name,
64 "xyz.openbmc_project.Sensor.Threshold.Critical");
65 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070066 setInitialProperties(conn);
James Feist6ef20402019-01-07 16:45:08 -080067 setupPowerMatch(conn);
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070068 setupRead();
James Feist6714a252018-09-10 15:26:18 -070069}
70
71TachSensor::~TachSensor()
72{
73 // close the input dev to cancel async operations
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070074 inputDev.close();
75 waitTimer.cancel();
James Feist251c7822018-09-12 12:54:15 -070076 objServer.remove_interface(thresholdInterfaceWarning);
77 objServer.remove_interface(thresholdInterfaceCritical);
78 objServer.remove_interface(sensorInterface);
James Feist6714a252018-09-10 15:26:18 -070079}
80
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070081void TachSensor::setupRead(void)
James Feist6714a252018-09-10 15:26:18 -070082{
83 boost::asio::async_read_until(
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070084 inputDev, readBuf, '\n',
James Feistd8705872019-02-08 13:26:09 -080085 [&](const boost::system::error_code& ec,
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070086 std::size_t /*bytes_transfered*/) { handleResponse(ec); });
James Feist6714a252018-09-10 15:26:18 -070087}
88
James Feistd8705872019-02-08 13:26:09 -080089void TachSensor::handleResponse(const boost::system::error_code& err)
James Feist6714a252018-09-10 15:26:18 -070090{
91 if (err == boost::system::errc::bad_file_descriptor)
92 {
93 return; // we're being destroyed
94 }
James Feist7bc2bab2018-10-26 14:09:45 -070095 bool missing = false;
James Feist1169eb42018-10-31 10:08:47 -070096 size_t pollTime = pwmPollMs;
James Feist7bc2bab2018-10-26 14:09:45 -070097 if (presence)
98 {
99 if (!presence->getValue())
100 {
James Feist1169eb42018-10-31 10:08:47 -0700101 updateValue(std::numeric_limits<double>::quiet_NaN());
James Feist7bc2bab2018-10-26 14:09:45 -0700102 missing = true;
James Feist1169eb42018-10-31 10:08:47 -0700103 pollTime = sensorFailedPollTimeMs;
James Feist7bc2bab2018-10-26 14:09:45 -0700104 }
105 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700106 std::istream responseStream(&readBuf);
James Feist7bc2bab2018-10-26 14:09:45 -0700107 if (!missing)
James Feist6714a252018-09-10 15:26:18 -0700108 {
James Feist7bc2bab2018-10-26 14:09:45 -0700109 if (!err)
James Feist6714a252018-09-10 15:26:18 -0700110 {
James Feist7bc2bab2018-10-26 14:09:45 -0700111 std::string response;
112 try
James Feist6714a252018-09-10 15:26:18 -0700113 {
James Feist7bc2bab2018-10-26 14:09:45 -0700114 std::getline(responseStream, response);
115 float nvalue = std::stof(response);
116 responseStream.clear();
Richard Marian Thomaiyarb2eb29a2018-12-08 18:26:58 +0530117 if (overridenState)
Richard Marian Thomaiyar87219222018-11-06 20:25:38 +0530118 {
119 nvalue = overriddenValue;
120 }
James Feist7bc2bab2018-10-26 14:09:45 -0700121 if (nvalue != value)
122 {
123 updateValue(nvalue);
124 }
125 errCount = 0;
James Feist6714a252018-09-10 15:26:18 -0700126 }
James Feistd8705872019-02-08 13:26:09 -0800127 catch (const std::invalid_argument&)
James Feist7bc2bab2018-10-26 14:09:45 -0700128 {
129 errCount++;
130 }
James Feist6714a252018-09-10 15:26:18 -0700131 }
132 else
133 {
James Feist71d31b22019-01-02 16:57:54 -0800134 if (!isPowerOn())
James Feist7bc2bab2018-10-26 14:09:45 -0700135 {
James Feist71d31b22019-01-02 16:57:54 -0800136 errCount = 0;
137 updateValue(std::numeric_limits<double>::quiet_NaN());
James Feist7bc2bab2018-10-26 14:09:45 -0700138 }
139 else
140 {
James Feist71d31b22019-01-02 16:57:54 -0800141 pollTime = sensorFailedPollTimeMs;
142 errCount++;
James Feist7bc2bab2018-10-26 14:09:45 -0700143 }
James Feist6714a252018-09-10 15:26:18 -0700144 }
James Feist71d31b22019-01-02 16:57:54 -0800145 if (errCount >= warnAfterErrorCount)
146 {
147 // only print once
148 if (errCount == warnAfterErrorCount)
149 {
150 std::cerr << "Failure to read sensor " << name << " at " << path
151 << " ec:" << err << "\n";
152 }
153 updateValue(0);
154 }
James Feist6714a252018-09-10 15:26:18 -0700155 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700156 responseStream.clear();
157 inputDev.close();
James Feist6714a252018-09-10 15:26:18 -0700158 int fd = open(path.c_str(), O_RDONLY);
159 if (fd <= 0)
160 {
161 return; // we're no longer valid
162 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700163 inputDev.assign(fd);
James Feist7bc2bab2018-10-26 14:09:45 -0700164 waitTimer.expires_from_now(boost::posix_time::milliseconds(pollTime));
James Feistd8705872019-02-08 13:26:09 -0800165 waitTimer.async_wait([&](const boost::system::error_code& ec) {
James Feist6714a252018-09-10 15:26:18 -0700166 if (ec == boost::asio::error::operation_aborted)
167 {
168 return; // we're being canceled
169 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700170 setupRead();
James Feist6714a252018-09-10 15:26:18 -0700171 });
172}
173
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700174void TachSensor::checkThresholds(void)
James Feist6714a252018-09-10 15:26:18 -0700175{
James Feistdc6c55f2018-10-31 12:53:20 -0700176 bool status = thresholds::checkThresholds(this);
177 if (redundancy)
178 {
179 redundancy->update("/xyz/openbmc_project/sensors/fan_tach/" + name,
180 !status);
181 }
James Feist6714a252018-09-10 15:26:18 -0700182}
183
James Feist7bc2bab2018-10-26 14:09:45 -0700184PresenceSensor::PresenceSensor(const size_t index, bool inverted,
James Feistd8705872019-02-08 13:26:09 -0800185 boost::asio::io_service& io) :
James Feist7bc2bab2018-10-26 14:09:45 -0700186 inverted(inverted),
187 inputDev(io)
188{
189 // todo: use gpiodaemon
190 std::string device = gpioPath + std::string("gpio") + std::to_string(index);
191 fd = open((device + "/value").c_str(), O_RDONLY);
192 if (fd < 0)
193 {
194 std::cerr << "Error opening gpio " << index << "\n";
195 return;
196 }
197
198 std::ofstream deviceFile(device + "/edge");
199 if (!deviceFile.good())
200 {
201 std::cerr << "Error setting edge " << device << "\n";
202 return;
203 }
204 deviceFile << "both";
205 deviceFile.close();
206
207 inputDev.assign(boost::asio::ip::tcp::v4(), fd);
208 monitorPresence();
209 read();
210}
211
212PresenceSensor::~PresenceSensor()
213{
214 inputDev.close();
215 close(fd);
216}
217
218void PresenceSensor::monitorPresence(void)
219{
220 inputDev.async_wait(boost::asio::ip::tcp::socket::wait_error,
James Feistd8705872019-02-08 13:26:09 -0800221 [this](const boost::system::error_code& ec) {
James Feist7bc2bab2018-10-26 14:09:45 -0700222 if (ec == boost::system::errc::bad_file_descriptor)
223 {
224 return; // we're being destroyed
225 }
226 else if (ec)
227 {
228 std::cerr
229 << "Error on presence sensor socket\n";
230 }
231 else
232 {
233 read();
234 }
235 monitorPresence();
236 });
237}
238
239void PresenceSensor::read(void)
240{
241 constexpr size_t readSize = sizeof("0");
242 std::string readBuf;
243 readBuf.resize(readSize);
244 lseek(fd, 0, SEEK_SET);
245 size_t r = ::read(fd, readBuf.data(), readSize);
James Feist95b079b2018-11-21 09:28:00 -0800246 if (r != readSize)
James Feist7bc2bab2018-10-26 14:09:45 -0700247 {
248 std::cerr << "Error reading gpio\n";
249 }
250 else
251 {
252 bool value = std::stoi(readBuf);
253 if (inverted)
254 {
255 value = !value;
256 }
257 status = value;
258 }
259}
260
261bool PresenceSensor::getValue(void)
262{
263 return status;
James Feistdc6c55f2018-10-31 12:53:20 -0700264}
265
266RedundancySensor::RedundancySensor(
James Feistd8705872019-02-08 13:26:09 -0800267 size_t count, const std::vector<std::string>& children,
268 sdbusplus::asio::object_server& objectServer) :
James Feistdc6c55f2018-10-31 12:53:20 -0700269 count(count),
270 iface(objectServer.add_interface(
271 "/xyz/openbmc_project/control/FanRedundancy/Tach",
272 "xyz.openbmc_project.control.FanRedundancy")),
273 objectServer(objectServer)
274{
275 iface->register_property("Collection", children);
276 iface->register_property("Status", std::string("Full"));
277 iface->register_property("AllowedFailures", static_cast<uint8_t>(count));
278 iface->initialize();
279}
280RedundancySensor::~RedundancySensor()
281{
282 objectServer.remove_interface(iface);
283}
James Feistd8705872019-02-08 13:26:09 -0800284void RedundancySensor::update(const std::string& name, bool failed)
James Feistdc6c55f2018-10-31 12:53:20 -0700285{
286 statuses[name] = failed;
287 size_t failedCount = 0;
288
289 std::string state = "Full";
James Feistd8705872019-02-08 13:26:09 -0800290 for (const auto& status : statuses)
James Feistdc6c55f2018-10-31 12:53:20 -0700291 {
292 if (status.second)
293 {
294 failedCount++;
295 }
296 if (failedCount > count)
297 {
298 state = "Failed";
299 break;
300 }
301 else if (failedCount)
302 {
303 state = "Degraded";
304 }
305 }
306 iface->set_property("Status", state);
307}