blob: b42062c8ca88dcf33332de02cd03422997df304c [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 Feistce3fca42018-11-21 12:58:24 -080034TachSensor::TachSensor(const std::string &path, const std::string &objectType,
James Feist6714a252018-09-10 15:26:18 -070035 sdbusplus::asio::object_server &objectServer,
36 std::shared_ptr<sdbusplus::asio::connection> &conn,
James Feist7bc2bab2018-10-26 14:09:45 -070037 std::unique_ptr<PresenceSensor> &&presence,
James Feist95b079b2018-11-21 09:28:00 -080038 const std::shared_ptr<RedundancySensor> &redundancy,
James Feist6714a252018-09-10 15:26:18 -070039 boost::asio::io_service &io, const std::string &fanName,
40 std::vector<thresholds::Threshold> &&_thresholds,
James Feist87d713a2018-12-06 16:06:24 -080041 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 Feistdc6c55f2018-10-31 12:53:20 -070046 objServer(objectServer), dbusConnection(conn),
47 presence(std::move(presence)), redundancy(redundancy),
James Feistce3fca42018-11-21 12:58:24 -080048 inputDev(io, open(path.c_str(), O_RDONLY)), 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 Feist6714a252018-09-10 15:26:18 -070067 isPowerOn(dbusConnection); // first call initializes
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 Feist6714a252018-09-10 15:26:18 -070085 [&](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
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070089void 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 Feist7bc2bab2018-10-26 14:09:45 -0700127 catch (const std::invalid_argument &)
128 {
129 errCount++;
130 }
James Feist6714a252018-09-10 15:26:18 -0700131 }
132 else
133 {
James Feist1169eb42018-10-31 10:08:47 -0700134 pollTime = sensorFailedPollTimeMs;
James Feist7bc2bab2018-10-26 14:09:45 -0700135 errCount++;
136 }
James Feistdc6c55f2018-10-31 12:53:20 -0700137 if (errCount >= warnAfterErrorCount)
James Feist7bc2bab2018-10-26 14:09:45 -0700138 {
139 // only an error if power is on
140 if (isPowerOn(dbusConnection))
141 {
James Feistdc6c55f2018-10-31 12:53:20 -0700142 // only print once
143 if (errCount == warnAfterErrorCount)
144 {
145 std::cerr << "Failure to read sensor " << name << " at "
146 << path << " ec:" << err << "\n";
147 }
James Feist7bc2bab2018-10-26 14:09:45 -0700148 updateValue(0);
149 }
150 else
151 {
152 errCount = 0; // check power again in 10 cycles
James Feist1169eb42018-10-31 10:08:47 -0700153 updateValue(std::numeric_limits<double>::quiet_NaN());
James Feist7bc2bab2018-10-26 14:09:45 -0700154 }
James Feist6714a252018-09-10 15:26:18 -0700155 }
156 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700157 responseStream.clear();
158 inputDev.close();
James Feist6714a252018-09-10 15:26:18 -0700159 int fd = open(path.c_str(), O_RDONLY);
160 if (fd <= 0)
161 {
162 return; // we're no longer valid
163 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700164 inputDev.assign(fd);
James Feist7bc2bab2018-10-26 14:09:45 -0700165 waitTimer.expires_from_now(boost::posix_time::milliseconds(pollTime));
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700166 waitTimer.async_wait([&](const boost::system::error_code &ec) {
James Feist6714a252018-09-10 15:26:18 -0700167 if (ec == boost::asio::error::operation_aborted)
168 {
169 return; // we're being canceled
170 }
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700171 setupRead();
James Feist6714a252018-09-10 15:26:18 -0700172 });
173}
174
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700175void TachSensor::checkThresholds(void)
James Feist6714a252018-09-10 15:26:18 -0700176{
James Feistdc6c55f2018-10-31 12:53:20 -0700177 bool status = thresholds::checkThresholds(this);
178 if (redundancy)
179 {
180 redundancy->update("/xyz/openbmc_project/sensors/fan_tach/" + name,
181 !status);
182 }
James Feist6714a252018-09-10 15:26:18 -0700183}
184
James Feist7bc2bab2018-10-26 14:09:45 -0700185PresenceSensor::PresenceSensor(const size_t index, bool inverted,
186 boost::asio::io_service &io) :
187 inverted(inverted),
188 inputDev(io)
189{
190 // todo: use gpiodaemon
191 std::string device = gpioPath + std::string("gpio") + std::to_string(index);
192 fd = open((device + "/value").c_str(), O_RDONLY);
193 if (fd < 0)
194 {
195 std::cerr << "Error opening gpio " << index << "\n";
196 return;
197 }
198
199 std::ofstream deviceFile(device + "/edge");
200 if (!deviceFile.good())
201 {
202 std::cerr << "Error setting edge " << device << "\n";
203 return;
204 }
205 deviceFile << "both";
206 deviceFile.close();
207
208 inputDev.assign(boost::asio::ip::tcp::v4(), fd);
209 monitorPresence();
210 read();
211}
212
213PresenceSensor::~PresenceSensor()
214{
215 inputDev.close();
216 close(fd);
217}
218
219void PresenceSensor::monitorPresence(void)
220{
221 inputDev.async_wait(boost::asio::ip::tcp::socket::wait_error,
222 [this](const boost::system::error_code &ec) {
223 if (ec == boost::system::errc::bad_file_descriptor)
224 {
225 return; // we're being destroyed
226 }
227 else if (ec)
228 {
229 std::cerr
230 << "Error on presence sensor socket\n";
231 }
232 else
233 {
234 read();
235 }
236 monitorPresence();
237 });
238}
239
240void PresenceSensor::read(void)
241{
242 constexpr size_t readSize = sizeof("0");
243 std::string readBuf;
244 readBuf.resize(readSize);
245 lseek(fd, 0, SEEK_SET);
246 size_t r = ::read(fd, readBuf.data(), readSize);
James Feist95b079b2018-11-21 09:28:00 -0800247 if (r != readSize)
James Feist7bc2bab2018-10-26 14:09:45 -0700248 {
249 std::cerr << "Error reading gpio\n";
250 }
251 else
252 {
253 bool value = std::stoi(readBuf);
254 if (inverted)
255 {
256 value = !value;
257 }
258 status = value;
259 }
260}
261
262bool PresenceSensor::getValue(void)
263{
264 return status;
James Feistdc6c55f2018-10-31 12:53:20 -0700265}
266
267RedundancySensor::RedundancySensor(
268 size_t count, const std::vector<std::string> &children,
269 sdbusplus::asio::object_server &objectServer) :
270 count(count),
271 iface(objectServer.add_interface(
272 "/xyz/openbmc_project/control/FanRedundancy/Tach",
273 "xyz.openbmc_project.control.FanRedundancy")),
274 objectServer(objectServer)
275{
276 iface->register_property("Collection", children);
277 iface->register_property("Status", std::string("Full"));
278 iface->register_property("AllowedFailures", static_cast<uint8_t>(count));
279 iface->initialize();
280}
281RedundancySensor::~RedundancySensor()
282{
283 objectServer.remove_interface(iface);
284}
285void RedundancySensor::update(const std::string &name, bool failed)
286{
287 statuses[name] = failed;
288 size_t failedCount = 0;
289
290 std::string state = "Full";
291 for (const auto &status : statuses)
292 {
293 if (status.second)
294 {
295 failedCount++;
296 }
297 if (failedCount > count)
298 {
299 state = "Failed";
300 break;
301 }
302 else if (failedCount)
303 {
304 state = "Degraded";
305 }
306 }
307 iface->set_property("Status", state);
308}