/** * Copyright © 2017 IBM Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "fan.hpp" #include "sdbusplus.hpp" #include "tach_sensor.hpp" #include "utility.hpp" namespace phosphor { namespace fan { namespace monitor { constexpr auto FAN_SENSOR_VALUE_INTF = "xyz.openbmc_project.Sensor.Value"; constexpr auto FAN_TARGET_PROPERTY = "Target"; constexpr auto FAN_VALUE_PROPERTY = "Value"; using namespace std::experimental::filesystem; using InternalFailure = sdbusplus::xyz::openbmc_project::Common:: Error::InternalFailure; /** * @brief Helper function to read a property * * @param[in] interface - the interface the property is on * @param[in] propertName - the name of the property * @param[in] path - the dbus path * @param[in] bus - the dbus object * @param[out] value - filled in with the property value */ template static void readProperty(const std::string& interface, const std::string& propertyName, const std::string& path, sdbusplus::bus::bus& bus, T& value) { try { value = util::SDBusPlus::getProperty(bus, path, interface, propertyName); } catch (std::exception& e) { phosphor::logging::log(e.what()); } } TachSensor::TachSensor(Mode mode, sdbusplus::bus::bus& bus, Fan& fan, const std::string& id, bool hasTarget, size_t funcDelay, const std::string& interface, size_t factor, size_t offset, size_t timeout, const sdeventplus::Event& event) : _bus(bus), _fan(fan), _name(FAN_SENSOR_PATH + id), _invName(path(fan.getName()) / id), _hasTarget(hasTarget), _funcDelay(funcDelay), _interface(interface), _factor(factor), _offset(offset), _timeout(timeout), _timerMode(TimerMode::func), _timer(event, std::bind(&Fan::timerExpired, &fan, std::ref(*this))) { // Start from a known state of functional setFunctional(true); // Load in current Target and Input values when entering monitor mode if (mode != Mode::init) { try { // Use getProperty directly to allow a missing sensor object // to abort construction. _tachInput = util::SDBusPlus::getProperty( _bus, _name, FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY); } catch (std::exception& e) { log("Not monitoring a tach sensor", entry("SENSOR=%s", _name.c_str())); throw InvalidSensorError(); } if (_hasTarget) { readProperty(_interface, FAN_TARGET_PROPERTY, _name, _bus, _tachTarget); } auto match = getMatchString(FAN_SENSOR_VALUE_INTF); tachSignal = std::make_unique( _bus, match.c_str(), [this](auto& msg){ this->handleTachChange(msg); }); if (_hasTarget) { match = getMatchString(_interface); targetSignal = std::make_unique( _bus, match.c_str(), [this](auto& msg){ this->handleTargetChange(msg); }); } } } std::string TachSensor::getMatchString(const std::string& interface) { return sdbusplus::bus::match::rules::propertiesChanged( _name, interface); } uint64_t TachSensor::getTarget() const { if (!_hasTarget) { return _fan.findTargetSpeed(); } return _tachTarget; } void TachSensor::setFunctional(bool functional) { _functional = functional; updateInventory(_functional); } /** * @brief Reads a property from the input message and stores it in value. * T is the value type. * * Note: This can only be called once per message. * * @param[in] msg - the dbus message that contains the data * @param[in] interface - the interface the property is on * @param[in] propertName - the name of the property * @param[out] value - the value to store the property value in */ template static void readPropertyFromMessage(sdbusplus::message::message& msg, const std::string& interface, const std::string& propertyName, T& value) { std::string sensor; std::map> data; msg.read(sensor, data); if (sensor.compare(interface) == 0) { auto propertyMap = data.find(propertyName); if (propertyMap != data.end()) { value = sdbusplus::message::variant_ns::get( propertyMap->second); } } } void TachSensor::handleTargetChange(sdbusplus::message::message& msg) { readPropertyFromMessage(msg, _interface, FAN_TARGET_PROPERTY, _tachTarget); //Check all tach sensors on the fan against the target _fan.tachChanged(); } void TachSensor::handleTachChange(sdbusplus::message::message& msg) { readPropertyFromMessage(msg, FAN_SENSOR_VALUE_INTF, FAN_VALUE_PROPERTY, _tachInput); //Check just this sensor against the target _fan.tachChanged(*this); } void TachSensor::startTimer(TimerMode mode) { if (!timerRunning()) { _timer.start( getDelay(mode), util::Timer::TimerType::oneshot); _timerMode = mode; } else { if (mode != _timerMode) { _timer.stop(); _timer.start( getDelay(mode), util::Timer::TimerType::oneshot); _timerMode = mode; } } } std::chrono::microseconds TachSensor::getDelay(TimerMode mode) { using namespace std::chrono; switch(mode) { case TimerMode::nonfunc : return duration_cast(seconds(_timeout)); case TimerMode::func : return duration_cast(seconds(_funcDelay)); default : // Log an internal error for undefined timer mode log("Undefined timer mode", entry("TIMER_MODE=%u", mode)); elog(); return duration_cast(seconds(0)); } } void TachSensor::updateInventory(bool functional) { auto objectMap = util::getObjMap( _invName, util::OPERATIONAL_STATUS_INTF, util::FUNCTIONAL_PROPERTY, functional); auto response = util::SDBusPlus::lookupAndCallMethod( _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap); if (response.is_method_error()) { log("Error in notify update of tach sensor inventory"); } } } } }