blob: 2a431ebce0d755cbcbb3680e6c3aa17235e20006 [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*/
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103016
17#include "PwmSensor.hpp"
18
Ed Tanouseacbfdd2024-04-04 12:00:24 -070019#include "SensorPaths.hpp"
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103020#include "Utils.hpp"
Ed Tanouseacbfdd2024-04-04 12:00:24 -070021#include "sensor.hpp"
Andrew Jefferye73bd0a2023-01-25 10:39:57 +103022
Ed Tanouseacbfdd2024-04-04 12:00:24 -070023#include <sdbusplus/asio/connection.hpp>
James Feist38fb5982020-05-28 10:09:54 -070024#include <sdbusplus/asio/object_server.hpp>
25
Ed Tanouseacbfdd2024-04-04 12:00:24 -070026#include <cmath>
27#include <cstdint>
James Feist6714a252018-09-10 15:26:18 -070028#include <fstream>
29#include <iostream>
Ed Tanouseacbfdd2024-04-04 12:00:24 -070030#include <memory>
Patrick Venture96e97db2019-10-31 13:44:38 -070031#include <stdexcept>
32#include <string>
James Feist6714a252018-09-10 15:26:18 -070033
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080034static constexpr double sysPwmMax = 255.0;
35static constexpr double psuPwmMax = 100.0;
James Feist3a07f552019-08-27 13:34:54 -070036static constexpr double defaultPwm = 30.0;
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080037static constexpr double targetIfaceMax = sysPwmMax;
James Feist6714a252018-09-10 15:26:18 -070038
Delphine CC Chiub0dff222023-09-28 10:19:00 +080039PwmSensor::PwmSensor(const std::string& pwmname, const std::string& sysPath,
AppaRao Pulid9d8caf2020-02-27 20:56:59 +053040 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feist82bac4c2019-03-11 11:16:53 -070041 sdbusplus::asio::object_server& objectServer,
AppaRao Pulid9d8caf2020-02-27 20:56:59 +053042 const std::string& sensorConfiguration,
Jie Yang3291b9c2021-07-29 14:46:51 -070043 const std::string& sensorType, bool isValueMutable) :
Brad Bishopfbb44ad2019-11-08 09:42:37 -050044 sysPath(sysPath),
Delphine CC Chiub0dff222023-09-28 10:19:00 +080045 objectServer(objectServer), name(sensor_paths::escapePathForDbus(pwmname))
James Feist6714a252018-09-10 15:26:18 -070046{
James Feist6714a252018-09-10 15:26:18 -070047 // add interface under sensor and Control.FanPwm as Control is used
48 // in obmc project, also add sensor so it can be viewed as a sensor
49 sensorInterface = objectServer.add_interface(
50 "/xyz/openbmc_project/sensors/fan_pwm/" + name,
51 "xyz.openbmc_project.Sensor.Value");
52 uint32_t pwmValue = getValue(false);
Kuiying Wang105a1972020-08-28 19:07:53 +080053 if (sensorType == "PSU")
54 {
55 pwmMax = psuPwmMax;
56 }
57 else
58 {
59 pwmMax = sysPwmMax;
60 }
61
Ed Tanous2049bd22022-07-09 07:20:26 -070062 if (pwmValue == 0U)
James Feist3a07f552019-08-27 13:34:54 -070063 {
64 // default pwm to non 0
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080065 pwmValue = static_cast<uint32_t>(pwmMax * (defaultPwm / 100.0));
James Feist3a07f552019-08-27 13:34:54 -070066 setValue(pwmValue);
67 }
James Feistb6c0b912019-07-09 12:21:44 -070068 double fValue = 100.0 * (static_cast<double>(pwmValue) / pwmMax);
James Feist6714a252018-09-10 15:26:18 -070069 sensorInterface->register_property(
70 "Value", fValue,
71 [this](const double& req, double& resp) {
Ed Tanousbb679322022-05-16 16:10:00 -070072 if (!std::isfinite(req))
73 {
74 // Reject attempted change, if to NaN or other non-numeric
75 return -1;
76 }
77 if (req > 100.0 || req < 0.0)
78 {
79 // TODO(): It does not seem desirable to halt daemon here,
80 // probably should just reject the change, continue running?
81 throw std::runtime_error("Value out of range");
82 return -1;
83 }
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080084
Ed Tanousbb679322022-05-16 16:10:00 -070085 double reqValue = (req / 100.0) * pwmMax;
86 double respValue = (resp / 100.0) * pwmMax;
87 auto reqInt = static_cast<uint32_t>(std::round(reqValue));
88 auto respInt = static_cast<uint32_t>(std::round(respValue));
89 // Avoid floating-point equality, compare as integers
90 if (reqInt == respInt)
91 {
James Feist6714a252018-09-10 15:26:18 -070092 return 1;
Ed Tanousbb679322022-05-16 16:10:00 -070093 }
94 setValue(reqInt);
95 resp = req;
96
97 controlInterface->signal_property("Target");
98
99 return 1;
Patrick Williams597e8422023-10-20 11:19:01 -0500100 },
James Feist6714a252018-09-10 15:26:18 -0700101 [this](double& curVal) {
Ed Tanousbb679322022-05-16 16:10:00 -0700102 double currScaled = (curVal / 100.0) * pwmMax;
103 auto currInt = static_cast<uint32_t>(std::round(currScaled));
104 auto getInt = getValue();
105 // Avoid floating-point equality, compare as integers
106 if (currInt != getInt)
107 {
108 double getScaled = 100.0 * (static_cast<double>(getInt) / pwmMax);
109 curVal = getScaled;
110 controlInterface->signal_property("Target");
111 sensorInterface->signal_property("Value");
112 }
113 return curVal;
114 });
James Feist6714a252018-09-10 15:26:18 -0700115 // pwm sensor interface is in percent
David Wangead7e922022-12-09 09:30:30 +0800116 sensorInterface->register_property("MaxValue", static_cast<double>(100));
117 sensorInterface->register_property("MinValue", static_cast<double>(0));
Zev Weiss6b6891c2021-04-22 02:46:21 -0500118 sensorInterface->register_property("Unit", sensor_paths::unitPercent);
James Feist6714a252018-09-10 15:26:18 -0700119
120 controlInterface = objectServer.add_interface(
121 "/xyz/openbmc_project/control/fanpwm/" + name,
122 "xyz.openbmc_project.Control.FanPwm");
123 controlInterface->register_property(
124 "Target", static_cast<uint64_t>(pwmValue),
125 [this](const uint64_t& req, uint64_t& resp) {
Ed Tanousbb679322022-05-16 16:10:00 -0700126 if (req > static_cast<uint64_t>(targetIfaceMax))
127 {
128 throw std::runtime_error("Value out of range");
129 return -1;
130 }
131 if (req == resp)
132 {
James Feist6714a252018-09-10 15:26:18 -0700133 return 1;
Ed Tanousbb679322022-05-16 16:10:00 -0700134 }
135 auto scaledValue = static_cast<double>(req) / targetIfaceMax;
136 auto roundValue = std::round(scaledValue * pwmMax);
137 setValue(static_cast<uint32_t>(roundValue));
138 resp = req;
139
140 sensorInterface->signal_property("Value");
141
142 return 1;
Patrick Williams597e8422023-10-20 11:19:01 -0500143 },
James Feist6714a252018-09-10 15:26:18 -0700144 [this](uint64_t& curVal) {
Ed Tanousbb679322022-05-16 16:10:00 -0700145 auto getInt = getValue();
146 auto scaledValue = static_cast<double>(getInt) / pwmMax;
147 auto roundValue = std::round(scaledValue * targetIfaceMax);
148 auto value = static_cast<uint64_t>(roundValue);
149 if (curVal != value)
150 {
151 curVal = value;
152 controlInterface->signal_property("Target");
153 sensorInterface->signal_property("Value");
154 }
155 return curVal;
156 });
Jie Yang3291b9c2021-07-29 14:46:51 -0700157
James Feist6714a252018-09-10 15:26:18 -0700158 sensorInterface->initialize();
159 controlInterface->initialize();
James Feist82bac4c2019-03-11 11:16:53 -0700160
Jie Yang3291b9c2021-07-29 14:46:51 -0700161 if (isValueMutable)
162 {
163 valueMutabilityInterface =
164 std::make_shared<sdbusplus::asio::dbus_interface>(
165 conn, sensorInterface->get_object_path(),
166 valueMutabilityInterfaceName);
167 valueMutabilityInterface->register_property("Mutable", true);
168 if (!valueMutabilityInterface->initialize())
169 {
170 std::cerr
171 << "error initializing sensor value mutability interface\n";
172 valueMutabilityInterface = nullptr;
173 }
174 }
175
James Feist82bac4c2019-03-11 11:16:53 -0700176 association = objectServer.add_interface(
James Feist2adc95c2019-09-30 14:55:28 -0700177 "/xyz/openbmc_project/sensors/fan_pwm/" + name, association::interface);
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530178
179 // PowerSupply sensors should be associated with chassis board path
180 // and inventory along with psu object.
181 if (sensorType == "PSU")
182 {
183 createInventoryAssoc(conn, association, sensorConfiguration);
184 }
185 else
186 {
187 createAssociation(association, sensorConfiguration);
188 }
James Feist6714a252018-09-10 15:26:18 -0700189}
190PwmSensor::~PwmSensor()
191{
192 objectServer.remove_interface(sensorInterface);
193 objectServer.remove_interface(controlInterface);
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530194 objectServer.remove_interface(association);
James Feist6714a252018-09-10 15:26:18 -0700195}
196
197void PwmSensor::setValue(uint32_t value)
198{
199 std::ofstream ref(sysPath);
200 if (!ref.good())
201 {
202 throw std::runtime_error("Bad Write File");
James Feist6714a252018-09-10 15:26:18 -0700203 }
204 ref << value;
205}
206
207// on success returns pwm, on failure throws except on initialization, where it
208// prints an error and returns 0
209uint32_t PwmSensor::getValue(bool errThrow)
210{
211 std::ifstream ref(sysPath);
212 if (!ref.good())
213 {
214 return -1;
215 }
216 std::string line;
217 if (!std::getline(ref, line))
218 {
219 return -1;
220 }
221 try
222 {
223 uint32_t value = std::stoi(line);
224 return value;
225 }
Patrick Williams26601e82021-10-06 12:43:25 -0500226 catch (const std::invalid_argument&)
James Feist6714a252018-09-10 15:26:18 -0700227 {
228 std::cerr << "Error reading pwm at " << sysPath << "\n";
229 // throw if not initial read to be caught by dbus bindings
230 if (errThrow)
231 {
232 throw std::runtime_error("Bad Read");
233 }
234 }
235 return 0;
236}