blob: c1f06746f043ebd3834378f7db11d9c3efd3b956 [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*/
Ed Tanous8a57ec02020-10-09 12:46:52 -070016#include <PwmSensor.hpp>
17#include <Utils.hpp>
James Feist38fb5982020-05-28 10:09:54 -070018#include <sdbusplus/asio/object_server.hpp>
19
James Feist6714a252018-09-10 15:26:18 -070020#include <fstream>
21#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070022#include <stdexcept>
23#include <string>
James Feist6714a252018-09-10 15:26:18 -070024
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080025static constexpr double sysPwmMax = 255.0;
26static constexpr double psuPwmMax = 100.0;
James Feist3a07f552019-08-27 13:34:54 -070027static constexpr double defaultPwm = 30.0;
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080028static constexpr double targetIfaceMax = sysPwmMax;
James Feist6714a252018-09-10 15:26:18 -070029
Cheng C Yang916360b2019-05-07 18:47:16 +080030PwmSensor::PwmSensor(const std::string& name, const std::string& sysPath,
AppaRao Pulid9d8caf2020-02-27 20:56:59 +053031 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feist82bac4c2019-03-11 11:16:53 -070032 sdbusplus::asio::object_server& objectServer,
AppaRao Pulid9d8caf2020-02-27 20:56:59 +053033 const std::string& sensorConfiguration,
Jie Yang3291b9c2021-07-29 14:46:51 -070034 const std::string& sensorType, bool isValueMutable) :
Brad Bishopfbb44ad2019-11-08 09:42:37 -050035 sysPath(sysPath),
36 objectServer(objectServer), name(name)
James Feist6714a252018-09-10 15:26:18 -070037{
James Feist6714a252018-09-10 15:26:18 -070038 // add interface under sensor and Control.FanPwm as Control is used
39 // in obmc project, also add sensor so it can be viewed as a sensor
40 sensorInterface = objectServer.add_interface(
41 "/xyz/openbmc_project/sensors/fan_pwm/" + name,
42 "xyz.openbmc_project.Sensor.Value");
43 uint32_t pwmValue = getValue(false);
Kuiying Wang105a1972020-08-28 19:07:53 +080044 if (sensorType == "PSU")
45 {
46 pwmMax = psuPwmMax;
47 }
48 else
49 {
50 pwmMax = sysPwmMax;
51 }
52
James Feist3a07f552019-08-27 13:34:54 -070053 if (!pwmValue)
54 {
55 // default pwm to non 0
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080056 pwmValue = static_cast<uint32_t>(pwmMax * (defaultPwm / 100.0));
James Feist3a07f552019-08-27 13:34:54 -070057 setValue(pwmValue);
58 }
James Feistb6c0b912019-07-09 12:21:44 -070059 double fValue = 100.0 * (static_cast<double>(pwmValue) / pwmMax);
James Feist6714a252018-09-10 15:26:18 -070060 sensorInterface->register_property(
61 "Value", fValue,
62 [this](const double& req, double& resp) {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080063 if (!std::isfinite(req))
James Feist6714a252018-09-10 15:26:18 -070064 {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080065 // Reject attempted change, if to NaN or other non-numeric
66 return -1;
67 }
68 if (req > 100.0 || req < 0.0)
69 {
70 // TODO(): It does not seem desirable to halt daemon here,
71 // probably should just reject the change, continue running?
James Feist6714a252018-09-10 15:26:18 -070072 throw std::runtime_error("Value out of range");
73 return -1;
74 }
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080075
76 double reqValue = (req / 100.0) * pwmMax;
77 double respValue = (resp / 100.0) * pwmMax;
78 auto reqInt = static_cast<uint32_t>(std::round(reqValue));
79 auto respInt = static_cast<uint32_t>(std::round(respValue));
80 // Avoid floating-point equality, compare as integers
81 if (reqInt == respInt)
James Feist46c5c1d2018-11-30 12:04:07 -080082 {
83 return 1;
84 }
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080085 setValue(reqInt);
James Feist6714a252018-09-10 15:26:18 -070086 resp = req;
James Feist46c5c1d2018-11-30 12:04:07 -080087
88 controlInterface->signal_property("Target");
89
James Feist6714a252018-09-10 15:26:18 -070090 return 1;
91 },
92 [this](double& curVal) {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080093 double currScaled = (curVal / 100.0) * pwmMax;
94 auto currInt = static_cast<uint32_t>(std::round(currScaled));
95 auto getInt = getValue();
96 // Avoid floating-point equality, compare as integers
97 if (currInt != getInt)
James Feist46c5c1d2018-11-30 12:04:07 -080098 {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080099 double getScaled =
100 100.0 * (static_cast<double>(getInt) / pwmMax);
101 curVal = getScaled;
James Feist46c5c1d2018-11-30 12:04:07 -0800102 controlInterface->signal_property("Target");
103 sensorInterface->signal_property("Value");
104 }
James Feist6714a252018-09-10 15:26:18 -0700105 return curVal;
106 });
107 // pwm sensor interface is in percent
108 sensorInterface->register_property("MaxValue", static_cast<int64_t>(100));
109 sensorInterface->register_property("MinValue", static_cast<int64_t>(0));
Zev Weiss6b6891c2021-04-22 02:46:21 -0500110 sensorInterface->register_property("Unit", sensor_paths::unitPercent);
James Feist6714a252018-09-10 15:26:18 -0700111
112 controlInterface = objectServer.add_interface(
113 "/xyz/openbmc_project/control/fanpwm/" + name,
114 "xyz.openbmc_project.Control.FanPwm");
115 controlInterface->register_property(
116 "Target", static_cast<uint64_t>(pwmValue),
117 [this](const uint64_t& req, uint64_t& resp) {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -0800118 if (req > static_cast<uint64_t>(targetIfaceMax))
James Feist6714a252018-09-10 15:26:18 -0700119 {
120 throw std::runtime_error("Value out of range");
121 return -1;
122 }
James Feist46c5c1d2018-11-30 12:04:07 -0800123 if (req == resp)
124 {
125 return 1;
126 }
Josh Lehan6e6ac0f2020-12-30 22:32:57 -0800127 auto scaledValue = static_cast<double>(req) / targetIfaceMax;
128 auto roundValue = std::round(scaledValue * pwmMax);
129 setValue(static_cast<uint32_t>(roundValue));
James Feist6714a252018-09-10 15:26:18 -0700130 resp = req;
James Feist46c5c1d2018-11-30 12:04:07 -0800131
132 sensorInterface->signal_property("Value");
133
James Feist6714a252018-09-10 15:26:18 -0700134 return 1;
135 },
136 [this](uint64_t& curVal) {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -0800137 auto getInt = getValue();
138 auto scaledValue = static_cast<double>(getInt) / pwmMax;
139 auto roundValue = std::round(scaledValue * targetIfaceMax);
140 auto value = static_cast<uint64_t>(roundValue);
James Feist46c5c1d2018-11-30 12:04:07 -0800141 if (curVal != value)
142 {
143 curVal = value;
144 controlInterface->signal_property("Target");
145 sensorInterface->signal_property("Value");
146 }
James Feist6714a252018-09-10 15:26:18 -0700147 return curVal;
148 });
Jie Yang3291b9c2021-07-29 14:46:51 -0700149
James Feist6714a252018-09-10 15:26:18 -0700150 sensorInterface->initialize();
151 controlInterface->initialize();
James Feist82bac4c2019-03-11 11:16:53 -0700152
Jie Yang3291b9c2021-07-29 14:46:51 -0700153 if (isValueMutable)
154 {
155 valueMutabilityInterface =
156 std::make_shared<sdbusplus::asio::dbus_interface>(
157 conn, sensorInterface->get_object_path(),
158 valueMutabilityInterfaceName);
159 valueMutabilityInterface->register_property("Mutable", true);
160 if (!valueMutabilityInterface->initialize())
161 {
162 std::cerr
163 << "error initializing sensor value mutability interface\n";
164 valueMutabilityInterface = nullptr;
165 }
166 }
167
James Feist82bac4c2019-03-11 11:16:53 -0700168 association = objectServer.add_interface(
James Feist2adc95c2019-09-30 14:55:28 -0700169 "/xyz/openbmc_project/sensors/fan_pwm/" + name, association::interface);
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530170
171 // PowerSupply sensors should be associated with chassis board path
172 // and inventory along with psu object.
173 if (sensorType == "PSU")
174 {
175 createInventoryAssoc(conn, association, sensorConfiguration);
176 }
177 else
178 {
179 createAssociation(association, sensorConfiguration);
180 }
James Feist6714a252018-09-10 15:26:18 -0700181}
182PwmSensor::~PwmSensor()
183{
184 objectServer.remove_interface(sensorInterface);
185 objectServer.remove_interface(controlInterface);
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530186 objectServer.remove_interface(association);
James Feist6714a252018-09-10 15:26:18 -0700187}
188
189void PwmSensor::setValue(uint32_t value)
190{
191 std::ofstream ref(sysPath);
192 if (!ref.good())
193 {
194 throw std::runtime_error("Bad Write File");
James Feist6714a252018-09-10 15:26:18 -0700195 }
196 ref << value;
197}
198
199// on success returns pwm, on failure throws except on initialization, where it
200// prints an error and returns 0
201uint32_t PwmSensor::getValue(bool errThrow)
202{
203 std::ifstream ref(sysPath);
204 if (!ref.good())
205 {
206 return -1;
207 }
208 std::string line;
209 if (!std::getline(ref, line))
210 {
211 return -1;
212 }
213 try
214 {
215 uint32_t value = std::stoi(line);
216 return value;
217 }
Patrick Williams26601e82021-10-06 12:43:25 -0500218 catch (const std::invalid_argument&)
James Feist6714a252018-09-10 15:26:18 -0700219 {
220 std::cerr << "Error reading pwm at " << sysPath << "\n";
221 // throw if not initial read to be caught by dbus bindings
222 if (errThrow)
223 {
224 throw std::runtime_error("Bad Read");
225 }
226 }
227 return 0;
228}