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