blob: 8993f2b1841ce199668003a68a6ef652841955d5 [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*/
James Feist82bac4c2019-03-11 11:16:53 -070016#include "PwmSensor.hpp"
17
18#include "Utils.hpp"
19
James Feist38fb5982020-05-28 10:09:54 -070020#include <sdbusplus/asio/object_server.hpp>
21
James Feist6714a252018-09-10 15:26:18 -070022#include <fstream>
23#include <iostream>
Patrick Venture96e97db2019-10-31 13:44:38 -070024#include <stdexcept>
25#include <string>
James Feist6714a252018-09-10 15:26:18 -070026
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080027static constexpr double sysPwmMax = 255.0;
28static constexpr double psuPwmMax = 100.0;
James Feist3a07f552019-08-27 13:34:54 -070029static constexpr double defaultPwm = 30.0;
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080030static constexpr double targetIfaceMax = sysPwmMax;
James Feist6714a252018-09-10 15:26:18 -070031
Cheng C Yang916360b2019-05-07 18:47:16 +080032PwmSensor::PwmSensor(const std::string& name, const std::string& sysPath,
AppaRao Pulid9d8caf2020-02-27 20:56:59 +053033 std::shared_ptr<sdbusplus::asio::connection>& conn,
James Feist82bac4c2019-03-11 11:16:53 -070034 sdbusplus::asio::object_server& objectServer,
AppaRao Pulid9d8caf2020-02-27 20:56:59 +053035 const std::string& sensorConfiguration,
36 const std::string& sensorType) :
Brad Bishopfbb44ad2019-11-08 09:42:37 -050037 sysPath(sysPath),
38 objectServer(objectServer), name(name)
James Feist6714a252018-09-10 15:26:18 -070039{
James Feist6714a252018-09-10 15:26:18 -070040 // add interface under sensor and Control.FanPwm as Control is used
41 // in obmc project, also add sensor so it can be viewed as a sensor
42 sensorInterface = objectServer.add_interface(
43 "/xyz/openbmc_project/sensors/fan_pwm/" + name,
44 "xyz.openbmc_project.Sensor.Value");
45 uint32_t pwmValue = getValue(false);
Kuiying Wang105a1972020-08-28 19:07:53 +080046 if (sensorType == "PSU")
47 {
48 pwmMax = psuPwmMax;
49 }
50 else
51 {
52 pwmMax = sysPwmMax;
53 }
54
James Feist3a07f552019-08-27 13:34:54 -070055 if (!pwmValue)
56 {
57 // default pwm to non 0
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080058 pwmValue = static_cast<uint32_t>(pwmMax * (defaultPwm / 100.0));
James Feist3a07f552019-08-27 13:34:54 -070059 setValue(pwmValue);
60 }
James Feistb6c0b912019-07-09 12:21:44 -070061 double fValue = 100.0 * (static_cast<double>(pwmValue) / pwmMax);
James Feist6714a252018-09-10 15:26:18 -070062 sensorInterface->register_property(
63 "Value", fValue,
64 [this](const double& req, double& resp) {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080065 if (!std::isfinite(req))
James Feist6714a252018-09-10 15:26:18 -070066 {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080067 // Reject attempted change, if to NaN or other non-numeric
68 return -1;
69 }
70 if (req > 100.0 || req < 0.0)
71 {
72 // TODO(): It does not seem desirable to halt daemon here,
73 // probably should just reject the change, continue running?
James Feist6714a252018-09-10 15:26:18 -070074 throw std::runtime_error("Value out of range");
75 return -1;
76 }
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080077
78 double reqValue = (req / 100.0) * pwmMax;
79 double respValue = (resp / 100.0) * pwmMax;
80 auto reqInt = static_cast<uint32_t>(std::round(reqValue));
81 auto respInt = static_cast<uint32_t>(std::round(respValue));
82 // Avoid floating-point equality, compare as integers
83 if (reqInt == respInt)
James Feist46c5c1d2018-11-30 12:04:07 -080084 {
85 return 1;
86 }
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080087 setValue(reqInt);
James Feist6714a252018-09-10 15:26:18 -070088 resp = req;
James Feist46c5c1d2018-11-30 12:04:07 -080089
90 controlInterface->signal_property("Target");
91
James Feist6714a252018-09-10 15:26:18 -070092 return 1;
93 },
94 [this](double& curVal) {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -080095 double currScaled = (curVal / 100.0) * pwmMax;
96 auto currInt = static_cast<uint32_t>(std::round(currScaled));
97 auto getInt = getValue();
98 // Avoid floating-point equality, compare as integers
99 if (currInt != getInt)
James Feist46c5c1d2018-11-30 12:04:07 -0800100 {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -0800101 double getScaled =
102 100.0 * (static_cast<double>(getInt) / pwmMax);
103 curVal = getScaled;
James Feist46c5c1d2018-11-30 12:04:07 -0800104 controlInterface->signal_property("Target");
105 sensorInterface->signal_property("Value");
106 }
James Feist6714a252018-09-10 15:26:18 -0700107 return curVal;
108 });
109 // pwm sensor interface is in percent
110 sensorInterface->register_property("MaxValue", static_cast<int64_t>(100));
111 sensorInterface->register_property("MinValue", static_cast<int64_t>(0));
112
113 controlInterface = objectServer.add_interface(
114 "/xyz/openbmc_project/control/fanpwm/" + name,
115 "xyz.openbmc_project.Control.FanPwm");
116 controlInterface->register_property(
117 "Target", static_cast<uint64_t>(pwmValue),
118 [this](const uint64_t& req, uint64_t& resp) {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -0800119 if (req > static_cast<uint64_t>(targetIfaceMax))
James Feist6714a252018-09-10 15:26:18 -0700120 {
121 throw std::runtime_error("Value out of range");
122 return -1;
123 }
James Feist46c5c1d2018-11-30 12:04:07 -0800124 if (req == resp)
125 {
126 return 1;
127 }
Josh Lehan6e6ac0f2020-12-30 22:32:57 -0800128 auto scaledValue = static_cast<double>(req) / targetIfaceMax;
129 auto roundValue = std::round(scaledValue * pwmMax);
130 setValue(static_cast<uint32_t>(roundValue));
James Feist6714a252018-09-10 15:26:18 -0700131 resp = req;
James Feist46c5c1d2018-11-30 12:04:07 -0800132
133 sensorInterface->signal_property("Value");
134
James Feist6714a252018-09-10 15:26:18 -0700135 return 1;
136 },
137 [this](uint64_t& curVal) {
Josh Lehan6e6ac0f2020-12-30 22:32:57 -0800138 auto getInt = getValue();
139 auto scaledValue = static_cast<double>(getInt) / pwmMax;
140 auto roundValue = std::round(scaledValue * targetIfaceMax);
141 auto value = static_cast<uint64_t>(roundValue);
James Feist46c5c1d2018-11-30 12:04:07 -0800142 if (curVal != value)
143 {
144 curVal = value;
145 controlInterface->signal_property("Target");
146 sensorInterface->signal_property("Value");
147 }
James Feist6714a252018-09-10 15:26:18 -0700148 return curVal;
149 });
150 sensorInterface->initialize();
151 controlInterface->initialize();
James Feist82bac4c2019-03-11 11:16:53 -0700152
153 association = objectServer.add_interface(
James Feist2adc95c2019-09-30 14:55:28 -0700154 "/xyz/openbmc_project/sensors/fan_pwm/" + name, association::interface);
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530155
156 // PowerSupply sensors should be associated with chassis board path
157 // and inventory along with psu object.
158 if (sensorType == "PSU")
159 {
160 createInventoryAssoc(conn, association, sensorConfiguration);
161 }
162 else
163 {
164 createAssociation(association, sensorConfiguration);
165 }
James Feist6714a252018-09-10 15:26:18 -0700166}
167PwmSensor::~PwmSensor()
168{
169 objectServer.remove_interface(sensorInterface);
170 objectServer.remove_interface(controlInterface);
AppaRao Pulid9d8caf2020-02-27 20:56:59 +0530171 objectServer.remove_interface(association);
James Feist6714a252018-09-10 15:26:18 -0700172}
173
174void PwmSensor::setValue(uint32_t value)
175{
176 std::ofstream ref(sysPath);
177 if (!ref.good())
178 {
179 throw std::runtime_error("Bad Write File");
James Feist6714a252018-09-10 15:26:18 -0700180 }
181 ref << value;
182}
183
184// on success returns pwm, on failure throws except on initialization, where it
185// prints an error and returns 0
186uint32_t PwmSensor::getValue(bool errThrow)
187{
188 std::ifstream ref(sysPath);
189 if (!ref.good())
190 {
191 return -1;
192 }
193 std::string line;
194 if (!std::getline(ref, line))
195 {
196 return -1;
197 }
198 try
199 {
200 uint32_t value = std::stoi(line);
201 return value;
202 }
James Feistb6c0b912019-07-09 12:21:44 -0700203 catch (std::invalid_argument&)
James Feist6714a252018-09-10 15:26:18 -0700204 {
205 std::cerr << "Error reading pwm at " << sysPath << "\n";
206 // throw if not initial read to be caught by dbus bindings
207 if (errThrow)
208 {
209 throw std::runtime_error("Bad Read");
210 }
211 }
212 return 0;
213}