xref: /openbmc/dbus-sensors/src/PwmSensor.cpp (revision d89bf542)
1 /*
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 #include "PwmSensor.hpp"
17 
18 #include "Utils.hpp"
19 
20 #include <fstream>
21 #include <iostream>
22 #include <sdbusplus/asio/object_server.hpp>
23 #include <stdexcept>
24 #include <string>
25 
26 static constexpr size_t pwmMax = 255;
27 static constexpr double defaultPwm = 30.0;
28 
29 PwmSensor::PwmSensor(const std::string& name, const std::string& sysPath,
30                      sdbusplus::asio::object_server& objectServer,
31                      const std::string& sensorConfiguration) :
32     sysPath(sysPath),
33     objectServer(objectServer), name(name)
34 {
35     // add interface under sensor and Control.FanPwm as Control is used
36     // in obmc project, also add sensor so it can be viewed as a sensor
37     sensorInterface = objectServer.add_interface(
38         "/xyz/openbmc_project/sensors/fan_pwm/" + name,
39         "xyz.openbmc_project.Sensor.Value");
40     uint32_t pwmValue = getValue(false);
41     if (!pwmValue)
42     {
43         // default pwm to non 0
44         pwmValue = static_cast<uint32_t>(pwmMax * (defaultPwm / 100));
45         setValue(pwmValue);
46     }
47     double fValue = 100.0 * (static_cast<double>(pwmValue) / pwmMax);
48     sensorInterface->register_property(
49         "Value", fValue,
50         [this](const double& req, double& resp) {
51             if (req > 100 || req < 0)
52             {
53                 throw std::runtime_error("Value out of range");
54                 return -1;
55             }
56             if (req == resp)
57             {
58                 return 1;
59             }
60             double value = (req / 100) * pwmMax;
61             setValue(static_cast<int>(value));
62             resp = req;
63 
64             controlInterface->signal_property("Target");
65 
66             return 1;
67         },
68         [this](double& curVal) {
69             double value = 100.0 * (static_cast<double>(getValue()) / pwmMax);
70             if (curVal != value)
71             {
72                 curVal = value;
73                 controlInterface->signal_property("Target");
74                 sensorInterface->signal_property("Value");
75             }
76 
77             return curVal;
78         });
79     // pwm sensor interface is in percent
80     sensorInterface->register_property("MaxValue", static_cast<int64_t>(100));
81     sensorInterface->register_property("MinValue", static_cast<int64_t>(0));
82 
83     controlInterface = objectServer.add_interface(
84         "/xyz/openbmc_project/control/fanpwm/" + name,
85         "xyz.openbmc_project.Control.FanPwm");
86     controlInterface->register_property(
87         "Target", static_cast<uint64_t>(pwmValue),
88         [this](const uint64_t& req, uint64_t& resp) {
89             if (req > pwmMax)
90             {
91                 throw std::runtime_error("Value out of range");
92                 return -1;
93             }
94             if (req == resp)
95             {
96                 return 1;
97             }
98             setValue(req);
99             resp = req;
100 
101             sensorInterface->signal_property("Value");
102 
103             return 1;
104         },
105         [this](uint64_t& curVal) {
106             uint64_t value = getValue();
107             if (curVal != value)
108             {
109                 curVal = value;
110                 controlInterface->signal_property("Target");
111                 sensorInterface->signal_property("Value");
112             }
113 
114             return curVal;
115         });
116     sensorInterface->initialize();
117     controlInterface->initialize();
118 
119     association = objectServer.add_interface(
120         "/xyz/openbmc_project/sensors/fan_pwm/" + name, association::interface);
121     createAssociation(association, sensorConfiguration);
122 }
123 PwmSensor::~PwmSensor()
124 {
125     objectServer.remove_interface(sensorInterface);
126     objectServer.remove_interface(controlInterface);
127 }
128 
129 void PwmSensor::setValue(uint32_t value)
130 {
131     std::ofstream ref(sysPath);
132     if (!ref.good())
133     {
134         throw std::runtime_error("Bad Write File");
135     }
136     ref << value;
137 }
138 
139 // on success returns pwm, on failure throws except on initialization, where it
140 // prints an error and returns 0
141 uint32_t PwmSensor::getValue(bool errThrow)
142 {
143     std::ifstream ref(sysPath);
144     if (!ref.good())
145     {
146         return -1;
147     }
148     std::string line;
149     if (!std::getline(ref, line))
150     {
151         return -1;
152     }
153     try
154     {
155         uint32_t value = std::stoi(line);
156         return value;
157     }
158     catch (std::invalid_argument&)
159     {
160         std::cerr << "Error reading pwm at " << sysPath << "\n";
161         // throw if not initial read to be caught by dbus bindings
162         if (errThrow)
163         {
164             throw std::runtime_error("Bad Read");
165         }
166     }
167     return 0;
168 }
169