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