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