blob: ad54dfe594e0402be4d4dc7eafa47131e58601c7 [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
19#include "Utils.hpp"
20
James Feist38fb5982020-05-28 10:09:54 -070021#include <sdbusplus/asio/object_server.hpp>
22
James Feist6714a252018-09-10 15:26:18 -070023#include <fstream>
24#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070025#include <stdexcept>
26#include <string>
James Feist6714a252018-09-10 15:26:18 -070027
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080028static constexpr double sysPwmMax = 255.0;
29static constexpr double psuPwmMax = 100.0;
James Feist3a07f552019-08-27 13:34:54 -070030static constexpr double defaultPwm = 30.0;
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080031static constexpr double targetIfaceMax = sysPwmMax;
James Feist6714a252018-09-10 15:26:18 -070032
Cheng C Yang916360b2019-05-07 18:47:16 +080033PwmSensor::PwmSensor(const std::string& name, const std::string& sysPath,
AppaRao Pulid9d8caf2020-02-27 20:56:59 +053034 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feist82bac4c2019-03-11 11:16:53 -070035 sdbusplus::asio::object_server& objectServer,
AppaRao Pulid9d8caf2020-02-27 20:56:59 +053036 const std::string& sensorConfiguration,
Jie Yang3291b9c2021-07-29 14:46:51 -070037 const std::string& sensorType, bool isValueMutable) :
Brad Bishopfbb44ad2019-11-08 09:42:37 -050038 sysPath(sysPath),
39 objectServer(objectServer), name(name)
James Feist6714a252018-09-10 15:26:18 -070040{
James Feist6714a252018-09-10 15:26:18 -070041 // add interface under sensor and Control.FanPwm as Control is used
42 // in obmc project, also add sensor so it can be viewed as a sensor
43 sensorInterface = objectServer.add_interface(
44 "/xyz/openbmc_project/sensors/fan_pwm/" + name,
45 "xyz.openbmc_project.Sensor.Value");
46 uint32_t pwmValue = getValue(false);
Kuiying Wang105a1972020-08-28 19:07:53 +080047 if (sensorType == "PSU")
48 {
49 pwmMax = psuPwmMax;
50 }
51 else
52 {
53 pwmMax = sysPwmMax;
54 }
55
Ed Tanous2049bd22022-07-09 07:20:26 -070056 if (pwmValue == 0U)
James Feist3a07f552019-08-27 13:34:54 -070057 {
58 // default pwm to non 0
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080059 pwmValue = static_cast<uint32_t>(pwmMax * (defaultPwm / 100.0));
James Feist3a07f552019-08-27 13:34:54 -070060 setValue(pwmValue);
61 }
James Feistb6c0b912019-07-09 12:21:44 -070062 double fValue = 100.0 * (static_cast<double>(pwmValue) / pwmMax);
James Feist6714a252018-09-10 15:26:18 -070063 sensorInterface->register_property(
64 "Value", fValue,
65 [this](const double& req, double& resp) {
Ed Tanousbb679322022-05-16 16:10:00 -070066 if (!std::isfinite(req))
67 {
68 // Reject attempted change, if to NaN or other non-numeric
69 return -1;
70 }
71 if (req > 100.0 || req < 0.0)
72 {
73 // TODO(): It does not seem desirable to halt daemon here,
74 // probably should just reject the change, continue running?
75 throw std::runtime_error("Value out of range");
76 return -1;
77 }
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080078
Ed Tanousbb679322022-05-16 16:10:00 -070079 double reqValue = (req / 100.0) * pwmMax;
80 double respValue = (resp / 100.0) * pwmMax;
81 auto reqInt = static_cast<uint32_t>(std::round(reqValue));
82 auto respInt = static_cast<uint32_t>(std::round(respValue));
83 // Avoid floating-point equality, compare as integers
84 if (reqInt == respInt)
85 {
James Feist6714a252018-09-10 15:26:18 -070086 return 1;
Ed Tanousbb679322022-05-16 16:10:00 -070087 }
88 setValue(reqInt);
89 resp = req;
90
91 controlInterface->signal_property("Target");
92
93 return 1;
James Feist6714a252018-09-10 15:26:18 -070094 },
95 [this](double& curVal) {
Ed Tanousbb679322022-05-16 16:10:00 -070096 double currScaled = (curVal / 100.0) * pwmMax;
97 auto currInt = static_cast<uint32_t>(std::round(currScaled));
98 auto getInt = getValue();
99 // Avoid floating-point equality, compare as integers
100 if (currInt != getInt)
101 {
102 double getScaled = 100.0 * (static_cast<double>(getInt) / pwmMax);
103 curVal = getScaled;
104 controlInterface->signal_property("Target");
105 sensorInterface->signal_property("Value");
106 }
107 return curVal;
108 });
James Feist6714a252018-09-10 15:26:18 -0700109 // pwm sensor interface is in percent
David Wangead7e922022-12-09 09:30:30 +0800110 sensorInterface->register_property("MaxValue", static_cast<double>(100));
111 sensorInterface->register_property("MinValue", static_cast<double>(0));
Zev Weiss6b6891c2021-04-22 02:46:21 -0500112 sensorInterface->register_property("Unit", sensor_paths::unitPercent);
James Feist6714a252018-09-10 15:26:18 -0700113
114 controlInterface = objectServer.add_interface(
115 "/xyz/openbmc_project/control/fanpwm/" + name,
116 "xyz.openbmc_project.Control.FanPwm");
117 controlInterface->register_property(
118 "Target", static_cast<uint64_t>(pwmValue),
119 [this](const uint64_t& req, uint64_t& resp) {
Ed Tanousbb679322022-05-16 16:10:00 -0700120 if (req > static_cast<uint64_t>(targetIfaceMax))
121 {
122 throw std::runtime_error("Value out of range");
123 return -1;
124 }
125 if (req == resp)
126 {
James Feist6714a252018-09-10 15:26:18 -0700127 return 1;
Ed Tanousbb679322022-05-16 16:10:00 -0700128 }
129 auto scaledValue = static_cast<double>(req) / targetIfaceMax;
130 auto roundValue = std::round(scaledValue * pwmMax);
131 setValue(static_cast<uint32_t>(roundValue));
132 resp = req;
133
134 sensorInterface->signal_property("Value");
135
136 return 1;
James Feist6714a252018-09-10 15:26:18 -0700137 },
138 [this](uint64_t& curVal) {
Ed Tanousbb679322022-05-16 16:10:00 -0700139 auto getInt = getValue();
140 auto scaledValue = static_cast<double>(getInt) / pwmMax;
141 auto roundValue = std::round(scaledValue * targetIfaceMax);
142 auto value = static_cast<uint64_t>(roundValue);
143 if (curVal != value)
144 {
145 curVal = value;
146 controlInterface->signal_property("Target");
147 sensorInterface->signal_property("Value");
148 }
149 return curVal;
150 });
Jie Yang3291b9c2021-07-29 14:46:51 -0700151
James Feist6714a252018-09-10 15:26:18 -0700152 sensorInterface->initialize();
153 controlInterface->initialize();
James Feist82bac4c2019-03-11 11:16:53 -0700154
Jie Yang3291b9c2021-07-29 14:46:51 -0700155 if (isValueMutable)
156 {
157 valueMutabilityInterface =
158 std::make_shared<sdbusplus::asio::dbus_interface>(
159 conn, sensorInterface->get_object_path(),
160 valueMutabilityInterfaceName);
161 valueMutabilityInterface->register_property("Mutable", true);
162 if (!valueMutabilityInterface->initialize())
163 {
164 std::cerr
165 << "error initializing sensor value mutability interface\n";
166 valueMutabilityInterface = nullptr;
167 }
168 }
169
James Feist82bac4c2019-03-11 11:16:53 -0700170 association = objectServer.add_interface(
James Feist2adc95c2019-09-30 14:55:28 -0700171 "/xyz/openbmc_project/sensors/fan_pwm/" + name, association::interface);
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530172
173 // PowerSupply sensors should be associated with chassis board path
174 // and inventory along with psu object.
175 if (sensorType == "PSU")
176 {
177 createInventoryAssoc(conn, association, sensorConfiguration);
178 }
179 else
180 {
181 createAssociation(association, sensorConfiguration);
182 }
James Feist6714a252018-09-10 15:26:18 -0700183}
184PwmSensor::~PwmSensor()
185{
186 objectServer.remove_interface(sensorInterface);
187 objectServer.remove_interface(controlInterface);
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530188 objectServer.remove_interface(association);
James Feist6714a252018-09-10 15:26:18 -0700189}
190
191void PwmSensor::setValue(uint32_t value)
192{
193 std::ofstream ref(sysPath);
194 if (!ref.good())
195 {
196 throw std::runtime_error("Bad Write File");
James Feist6714a252018-09-10 15:26:18 -0700197 }
198 ref << value;
199}
200
201// on success returns pwm, on failure throws except on initialization, where it
202// prints an error and returns 0
203uint32_t PwmSensor::getValue(bool errThrow)
204{
205 std::ifstream ref(sysPath);
206 if (!ref.good())
207 {
208 return -1;
209 }
210 std::string line;
211 if (!std::getline(ref, line))
212 {
213 return -1;
214 }
215 try
216 {
217 uint32_t value = std::stoi(line);
218 return value;
219 }
Patrick Williams26601e82021-10-06 12:43:25 -0500220 catch (const std::invalid_argument&)
James Feist6714a252018-09-10 15:26:18 -0700221 {
222 std::cerr << "Error reading pwm at " << sysPath << "\n";
223 // throw if not initial read to be caught by dbus bindings
224 if (errThrow)
225 {
226 throw std::runtime_error("Bad Read");
227 }
228 }
229 return 0;
230}