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