blob: 35185cde6871f04d988b4e2f73f835f01ed04861 [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
Ed Tanous8a57ec02020-10-09 12:46:52 -070019#include <TachSensor.hpp>
20#include <Utils.hpp>
James Feist8086aba2020-08-25 16:00:59 -070021#include <boost/asio/read_until.hpp>
Zhikui Ren347dd4e2019-12-12 13:39:50 -080022#include <gpiod.hpp>
James Feist38fb5982020-05-28 10:09:54 -070023#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
25
Josh Lehan5170fe62022-08-03 13:17:41 -070026#include <charconv>
James Feist38fb5982020-05-28 10:09:54 -070027#include <fstream>
James Feist6714a252018-09-10 15:26:18 -070028#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070029#include <istream>
James Feist6714a252018-09-10 15:26:18 -070030#include <limits>
Patrick Venture96e97db2019-10-31 13:44:38 -070031#include <memory>
32#include <optional>
Patrick Venture96e97db2019-10-31 13:44:38 -070033#include <stdexcept>
James Feist6714a252018-09-10 15:26:18 -070034#include <string>
Patrick Venture96e97db2019-10-31 13:44:38 -070035#include <utility>
36#include <vector>
James Feist6714a252018-09-10 15:26:18 -070037
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070038static constexpr unsigned int pwmPollMs = 500;
James Feist6714a252018-09-10 15:26:18 -070039
James Feistd8705872019-02-08 13:26:09 -080040TachSensor::TachSensor(const std::string& path, const std::string& objectType,
41 sdbusplus::asio::object_server& objectServer,
42 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feist60c0ec72019-03-18 15:08:43 -070043 std::unique_ptr<PresenceSensor>&& presenceSensor,
James Feist7b18b1e2019-05-14 13:42:09 -070044 std::optional<RedundancySensor>* redundancy,
James Feistd8705872019-02-08 13:26:09 -080045 boost::asio::io_service& io, const std::string& fanName,
Jeff Lin7b7a9de2021-02-22 11:16:27 +080046 std::vector<thresholds::Threshold>&& thresholdsIn,
James Feistd8705872019-02-08 13:26:09 -080047 const std::string& sensorConfiguration,
Ed Tanousf69fbf92022-01-14 10:15:33 -080048 const std::pair<double, double>& limits,
James Feist49a8ccd2020-09-16 16:09:52 -070049 const PowerState& powerState,
50 const std::optional<std::string>& ledIn) :
Zhikui Renda98f092021-11-01 09:41:08 -070051 Sensor(escapeName(fanName), std::move(thresholdsIn), sensorConfiguration,
52 objectType, false, false, limits.second, limits.first, conn,
53 powerState),
Brad Bishopfbb44ad2019-11-08 09:42:37 -050054 objServer(objectServer), redundancy(redundancy),
Josh Lehan5170fe62022-08-03 13:17:41 -070055 presence(std::move(presenceSensor)),
56 inputDev(io, path, boost::asio::random_access_file::read_only),
57 waitTimer(io), path(path), led(ledIn)
James Feist6714a252018-09-10 15:26:18 -070058{
James Feist251c7822018-09-12 12:54:15 -070059 sensorInterface = objectServer.add_interface(
60 "/xyz/openbmc_project/sensors/fan_tach/" + name,
61 "xyz.openbmc_project.Sensor.Value");
62
Jayashree Dhanapal56678082022-01-04 17:27:20 +053063 for (const auto& threshold : thresholds)
James Feist6714a252018-09-10 15:26:18 -070064 {
Jayashree Dhanapal56678082022-01-04 17:27:20 +053065 std::string interface = thresholds::getInterface(threshold.level);
66 thresholdInterfaces[static_cast<size_t>(threshold.level)] =
67 objectServer.add_interface(
68 "/xyz/openbmc_project/sensors/fan_tach/" + name, interface);
James Feist6714a252018-09-10 15:26:18 -070069 }
James Feist078f2322019-03-08 11:09:05 -080070 association = objectServer.add_interface(
71 "/xyz/openbmc_project/sensors/fan_tach/" + name,
James Feist2adc95c2019-09-30 14:55:28 -070072 association::interface);
James Feist60c0ec72019-03-18 15:08:43 -070073
74 if (presence)
75 {
76 itemIface =
James Feistd8bd5622019-06-26 12:09:05 -070077 objectServer.add_interface("/xyz/openbmc_project/inventory/" + name,
James Feist60c0ec72019-03-18 15:08:43 -070078 "xyz.openbmc_project.Inventory.Item");
79 itemIface->register_property("PrettyName",
80 std::string()); // unused property
81 itemIface->register_property("Present", true);
82 itemIface->initialize();
James Feist2adc95c2019-09-30 14:55:28 -070083 itemAssoc = objectServer.add_interface(
84 "/xyz/openbmc_project/inventory/" + name, association::interface);
James Feistd8bd5622019-06-26 12:09:05 -070085 itemAssoc->register_property(
86 "associations",
87 std::vector<Association>{
88 {"sensors", "inventory",
89 "/xyz/openbmc_project/sensors/fan_tach/" + name}});
90 itemAssoc->initialize();
James Feist60c0ec72019-03-18 15:08:43 -070091 }
Andrei Kartashev39287412022-02-04 16:04:47 +030092 setInitialProperties(sensor_paths::unitRPMs);
James Feist6714a252018-09-10 15:26:18 -070093}
94
95TachSensor::~TachSensor()
96{
97 // close the input dev to cancel async operations
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -070098 inputDev.close();
99 waitTimer.cancel();
Jayashree Dhanapal56678082022-01-04 17:27:20 +0530100 for (const auto& iface : thresholdInterfaces)
101 {
102 objServer.remove_interface(iface);
103 }
James Feist251c7822018-09-12 12:54:15 -0700104 objServer.remove_interface(sensorInterface);
James Feist078f2322019-03-08 11:09:05 -0800105 objServer.remove_interface(association);
James Feist60c0ec72019-03-18 15:08:43 -0700106 objServer.remove_interface(itemIface);
James Feistd8bd5622019-06-26 12:09:05 -0700107 objServer.remove_interface(itemAssoc);
James Feist6714a252018-09-10 15:26:18 -0700108}
109
Josh Lehan5170fe62022-08-03 13:17:41 -0700110void TachSensor::setupRead()
James Feist6714a252018-09-10 15:26:18 -0700111{
Josh Lehan5170fe62022-08-03 13:17:41 -0700112 std::weak_ptr<TachSensor> weakRef = weak_from_this();
113 inputDev.async_read_some_at(
114 0, boost::asio::buffer(readBuf),
115 [weakRef](const boost::system::error_code& ec, std::size_t bytesRead) {
116 std::shared_ptr<TachSensor> self = weakRef.lock();
117 if (self)
118 {
119 self->handleResponse(ec, bytesRead);
120 }
121 });
122}
123
124void TachSensor::restartRead(size_t pollTime)
125{
126 std::weak_ptr<TachSensor> weakRef = weak_from_this();
Ed Tanous9b4a20e2022-09-06 08:47:11 -0700127 waitTimer.expires_from_now(std::chrono::milliseconds(pollTime));
Josh Lehan5170fe62022-08-03 13:17:41 -0700128 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
129 if (ec == boost::asio::error::operation_aborted)
130 {
131 return; // we're being canceled
132 }
133 std::shared_ptr<TachSensor> self = weakRef.lock();
134 if (!self)
135 {
136 return;
137 }
138 self->setupRead();
Ed Tanousbb679322022-05-16 16:10:00 -0700139 });
James Feist6714a252018-09-10 15:26:18 -0700140}
141
Josh Lehan5170fe62022-08-03 13:17:41 -0700142void TachSensor::handleResponse(const boost::system::error_code& err,
143 size_t bytesRead)
James Feist6714a252018-09-10 15:26:18 -0700144{
Josh Lehan5170fe62022-08-03 13:17:41 -0700145 if ((err == boost::system::errc::bad_file_descriptor) ||
146 (err == boost::asio::error::misc_errors::not_found))
James Feist6714a252018-09-10 15:26:18 -0700147 {
Josh Lehan5170fe62022-08-03 13:17:41 -0700148 std::cerr << "TachSensor " << name << " removed " << path << "\n";
James Feist6714a252018-09-10 15:26:18 -0700149 return; // we're being destroyed
150 }
James Feist7bc2bab2018-10-26 14:09:45 -0700151 bool missing = false;
James Feist1169eb42018-10-31 10:08:47 -0700152 size_t pollTime = pwmPollMs;
James Feist7bc2bab2018-10-26 14:09:45 -0700153 if (presence)
154 {
155 if (!presence->getValue())
156 {
James Feist961bf092020-07-01 16:38:12 -0700157 markAvailable(false);
James Feist7bc2bab2018-10-26 14:09:45 -0700158 missing = true;
James Feist1169eb42018-10-31 10:08:47 -0700159 pollTime = sensorFailedPollTimeMs;
James Feist7bc2bab2018-10-26 14:09:45 -0700160 }
James Feist60c0ec72019-03-18 15:08:43 -0700161 itemIface->set_property("Present", !missing);
James Feist7bc2bab2018-10-26 14:09:45 -0700162 }
Josh Lehan5170fe62022-08-03 13:17:41 -0700163
James Feist7bc2bab2018-10-26 14:09:45 -0700164 if (!missing)
James Feist6714a252018-09-10 15:26:18 -0700165 {
James Feist7bc2bab2018-10-26 14:09:45 -0700166 if (!err)
James Feist6714a252018-09-10 15:26:18 -0700167 {
Josh Lehan5170fe62022-08-03 13:17:41 -0700168 const char* bufEnd = readBuf.data() + bytesRead;
169 int nvalue = 0;
170 std::from_chars_result ret =
171 std::from_chars(readBuf.data(), bufEnd, nvalue);
172 if (ret.ec != std::errc())
James Feist7bc2bab2018-10-26 14:09:45 -0700173 {
James Feist961bf092020-07-01 16:38:12 -0700174 incrementError();
175 pollTime = sensorFailedPollTimeMs;
James Feist7bc2bab2018-10-26 14:09:45 -0700176 }
Josh Lehan5170fe62022-08-03 13:17:41 -0700177 else
178 {
179 updateValue(nvalue);
180 }
James Feist6714a252018-09-10 15:26:18 -0700181 }
182 else
183 {
James Feist961bf092020-07-01 16:38:12 -0700184 incrementError();
185 pollTime = sensorFailedPollTimeMs;
James Feist71d31b22019-01-02 16:57:54 -0800186 }
James Feist6714a252018-09-10 15:26:18 -0700187 }
Ed Tanous99c44092022-01-14 09:59:09 -0800188
Josh Lehan5170fe62022-08-03 13:17:41 -0700189 restartRead(pollTime);
James Feist6714a252018-09-10 15:26:18 -0700190}
191
Jae Hyun Yoo9ced0a32018-10-25 10:42:39 -0700192void TachSensor::checkThresholds(void)
James Feist6714a252018-09-10 15:26:18 -0700193{
Zhikui Ren00bd56d2021-11-19 12:32:27 -0800194 bool status = thresholds::checkThresholds(this);
James Feist95e54902019-09-04 15:13:36 -0700195
Ed Tanous2049bd22022-07-09 07:20:26 -0700196 if ((redundancy != nullptr) && *redundancy)
James Feistdc6c55f2018-10-31 12:53:20 -0700197 {
James Feist7b18b1e2019-05-14 13:42:09 -0700198 (*redundancy)
199 ->update("/xyz/openbmc_project/sensors/fan_tach/" + name, !status);
James Feistdc6c55f2018-10-31 12:53:20 -0700200 }
James Feist49a8ccd2020-09-16 16:09:52 -0700201
202 bool curLed = !status;
203 if (led && ledState != curLed)
204 {
205 ledState = curLed;
206 setLed(dbusConnection, *led, curLed);
207 }
James Feist6714a252018-09-10 15:26:18 -0700208}
209
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800210PresenceSensor::PresenceSensor(const std::string& gpioName, bool inverted,
James Feist7b18b1e2019-05-14 13:42:09 -0700211 boost::asio::io_service& io,
212 const std::string& name) :
Ed Tanous8a17c302021-09-02 15:07:11 -0700213 gpioLine(gpiod::find_line(gpioName)),
214 gpioFd(io), name(name)
James Feist7bc2bab2018-10-26 14:09:45 -0700215{
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800216 if (!gpioLine)
James Feist7bc2bab2018-10-26 14:09:45 -0700217 {
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800218 std::cerr << "Error requesting gpio: " << gpioName << "\n";
219 status = false;
James Feist7bc2bab2018-10-26 14:09:45 -0700220 return;
221 }
222
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800223 try
James Feist7bc2bab2018-10-26 14:09:45 -0700224 {
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800225 gpioLine.request({"FanSensor", gpiod::line_request::EVENT_BOTH_EDGES,
226 inverted ? gpiod::line_request::FLAG_ACTIVE_LOW : 0});
Ed Tanous2049bd22022-07-09 07:20:26 -0700227 status = (gpioLine.get_value() != 0);
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800228
229 int gpioLineFd = gpioLine.event_get_fd();
230 if (gpioLineFd < 0)
231 {
232 std::cerr << "Failed to get " << gpioName << " fd\n";
233 return;
234 }
235
236 gpioFd.assign(gpioLineFd);
237 }
Patrick Williams26601e82021-10-06 12:43:25 -0500238 catch (const std::system_error&)
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800239 {
240 std::cerr << "Error reading gpio: " << gpioName << "\n";
241 status = false;
James Feist7bc2bab2018-10-26 14:09:45 -0700242 return;
243 }
James Feist7bc2bab2018-10-26 14:09:45 -0700244
James Feist7bc2bab2018-10-26 14:09:45 -0700245 monitorPresence();
James Feist7bc2bab2018-10-26 14:09:45 -0700246}
247
248PresenceSensor::~PresenceSensor()
249{
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800250 gpioFd.close();
251 gpioLine.release();
James Feist7bc2bab2018-10-26 14:09:45 -0700252}
253
254void PresenceSensor::monitorPresence(void)
255{
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800256 gpioFd.async_wait(boost::asio::posix::stream_descriptor::wait_read,
257 [this](const boost::system::error_code& ec) {
Ed Tanousbb679322022-05-16 16:10:00 -0700258 if (ec == boost::system::errc::bad_file_descriptor)
259 {
260 return; // we're being destroyed
261 }
262 if (ec)
263 {
264 std::cerr << "Error on presence sensor " << name << " \n";
265 ;
266 }
267 else
268 {
269 read();
270 }
271 monitorPresence();
272 });
James Feist7bc2bab2018-10-26 14:09:45 -0700273}
274
275void PresenceSensor::read(void)
276{
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800277 gpioLine.event_read();
Ed Tanous2049bd22022-07-09 07:20:26 -0700278 status = (gpioLine.get_value() != 0);
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800279 // Read is invoked when an edge event is detected by monitorPresence
280 if (status)
James Feist7bc2bab2018-10-26 14:09:45 -0700281 {
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800282 logFanInserted(name);
James Feist7bc2bab2018-10-26 14:09:45 -0700283 }
284 else
285 {
Zhikui Ren347dd4e2019-12-12 13:39:50 -0800286 logFanRemoved(name);
James Feist7bc2bab2018-10-26 14:09:45 -0700287 }
288}
289
Ed Tanous2049bd22022-07-09 07:20:26 -0700290bool PresenceSensor::getValue(void) const
James Feist7bc2bab2018-10-26 14:09:45 -0700291{
292 return status;
James Feistdc6c55f2018-10-31 12:53:20 -0700293}
294
James Feist9bb67462019-03-15 15:09:50 -0700295RedundancySensor::RedundancySensor(size_t count,
296 const std::vector<std::string>& children,
297 sdbusplus::asio::object_server& objectServer,
298 const std::string& sensorConfiguration) :
James Feistdc6c55f2018-10-31 12:53:20 -0700299 count(count),
300 iface(objectServer.add_interface(
301 "/xyz/openbmc_project/control/FanRedundancy/Tach",
James Feist9bb67462019-03-15 15:09:50 -0700302 "xyz.openbmc_project.Control.FanRedundancy")),
303 association(objectServer.add_interface(
304 "/xyz/openbmc_project/control/FanRedundancy/Tach",
James Feist2adc95c2019-09-30 14:55:28 -0700305 association::interface)),
James Feistdc6c55f2018-10-31 12:53:20 -0700306 objectServer(objectServer)
307{
James Feist9bb67462019-03-15 15:09:50 -0700308 createAssociation(association, sensorConfiguration);
James Feistdc6c55f2018-10-31 12:53:20 -0700309 iface->register_property("Collection", children);
310 iface->register_property("Status", std::string("Full"));
311 iface->register_property("AllowedFailures", static_cast<uint8_t>(count));
312 iface->initialize();
313}
314RedundancySensor::~RedundancySensor()
315{
James Feist9bb67462019-03-15 15:09:50 -0700316 objectServer.remove_interface(association);
James Feistdc6c55f2018-10-31 12:53:20 -0700317 objectServer.remove_interface(iface);
318}
James Feistd8705872019-02-08 13:26:09 -0800319void RedundancySensor::update(const std::string& name, bool failed)
James Feistdc6c55f2018-10-31 12:53:20 -0700320{
321 statuses[name] = failed;
322 size_t failedCount = 0;
323
James Feist7b18b1e2019-05-14 13:42:09 -0700324 std::string newState = redundancy::full;
Zev Weiss0521a062022-08-12 18:21:02 -0700325 for (const auto& [name, status] : statuses)
James Feistdc6c55f2018-10-31 12:53:20 -0700326 {
Zev Weiss0521a062022-08-12 18:21:02 -0700327 if (status)
James Feistdc6c55f2018-10-31 12:53:20 -0700328 {
329 failedCount++;
330 }
331 if (failedCount > count)
332 {
James Feist7b18b1e2019-05-14 13:42:09 -0700333 newState = redundancy::failed;
James Feistdc6c55f2018-10-31 12:53:20 -0700334 break;
335 }
Ed Tanous2049bd22022-07-09 07:20:26 -0700336 if (failedCount != 0U)
James Feistdc6c55f2018-10-31 12:53:20 -0700337 {
James Feist7b18b1e2019-05-14 13:42:09 -0700338 newState = redundancy::degraded;
James Feistdc6c55f2018-10-31 12:53:20 -0700339 }
340 }
James Feist7b18b1e2019-05-14 13:42:09 -0700341 if (state != newState)
342 {
343 if (state == redundancy::full)
344 {
345 logFanRedundancyLost();
346 }
347 else if (newState == redundancy::full)
348 {
349 logFanRedundancyRestored();
350 }
351 state = newState;
352 iface->set_property("Status", state);
353 }
James Feistdc6c55f2018-10-31 12:53:20 -0700354}