| #include "config.h" |
| |
| #include "manager.hpp" |
| |
| #include <phosphor-logging/lg2.hpp> |
| #include <sdbusplus/exception.hpp> |
| #include <xyz/openbmc_project/Led/Physical/server.hpp> |
| |
| #include <algorithm> |
| #include <iostream> |
| #include <string> |
| namespace phosphor |
| { |
| namespace led |
| { |
| |
| // apply the led action to the map |
| static void applyGroupAction(std::map<LedName, Layout::LedAction>& newState, |
| Layout::LedAction action) |
| { |
| if (!newState.contains(action.name)) |
| { |
| newState[action.name] = action; |
| return; |
| } |
| |
| auto currentAction = newState[action.name]; |
| |
| if (currentAction.action == action.priority) |
| { |
| // if the current action is already the priority action, |
| // we cannot override it |
| return; |
| } |
| |
| newState[action.name] = action; |
| } |
| |
| // create the resulting new map from all currently asserted groups |
| auto Manager::getNewMap(std::set<const ActionSet*> assertedGroups) |
| -> std::map<LedName, Layout::LedAction> |
| { |
| std::map<LedName, Layout::LedAction> newState; |
| |
| // update the new map with the desired state |
| for (const auto it : assertedGroups) |
| { |
| // apply all led actions of that group to the map |
| for (auto action : *it) |
| { |
| applyGroupAction(newState, action); |
| } |
| } |
| return newState; |
| } |
| |
| // Assert -or- De-assert |
| bool Manager::setGroupState(const std::string& path, bool assert, |
| ActionSet& ledsAssert, ActionSet& ledsDeAssert) |
| { |
| if (assert) |
| { |
| assertedGroups.insert(&ledMap.at(path)); |
| } |
| else |
| { |
| if (assertedGroups.contains(&ledMap.at(path))) |
| { |
| assertedGroups.erase(&ledMap.at(path)); |
| } |
| } |
| |
| // create the new map from the asserted groups |
| auto newState = getNewMap(assertedGroups); |
| |
| // the ledsAssert are those that are in the new map and change state |
| // + those in the new map and not in the old map |
| for (const auto& [name, action] : newState) |
| { |
| if (ledStateMap.contains(name)) |
| { |
| // check if the led action has changed |
| auto& currentAction = ledStateMap[name]; |
| |
| if (currentAction.action == action.action) |
| continue; |
| } |
| |
| ledsAssert.insert(action); |
| } |
| |
| // the ledsDeAssert are those in the old map but not in the new map |
| for (const auto& [name, action] : ledStateMap) |
| { |
| if (!newState.contains(name)) |
| { |
| ledsDeAssert.insert(action); |
| } |
| } |
| |
| ledStateMap = newState; |
| |
| // If we survive, then set the state accordingly. |
| return assert; |
| } |
| |
| void Manager::setLampTestCallBack( |
| std::function<bool(ActionSet& ledsAssert, ActionSet& ledsDeAssert)> |
| callBack) |
| { |
| lampTestCallBack = callBack; |
| } |
| |
| /** @brief Run through the map and apply action on the LEDs */ |
| void Manager::driveLEDs(ActionSet& ledsAssert, ActionSet& ledsDeAssert) |
| { |
| #ifdef USE_LAMP_TEST |
| // Use the lampTestCallBack method and trigger the callback method in the |
| // lamp test(processLEDUpdates), in this way, all lamp test operations |
| // are performed in the lamp test class. |
| if (lampTestCallBack(ledsAssert, ledsDeAssert)) |
| { |
| return; |
| } |
| #endif |
| ActionSet newReqChangedLeds; |
| std::vector<std::pair<ActionSet&, ActionSet&>> actionsVec = { |
| {reqLedsAssert, ledsAssert}, {reqLedsDeAssert, ledsDeAssert}}; |
| |
| timer.setEnabled(false); |
| std::set_union(ledsAssert.begin(), ledsAssert.end(), ledsDeAssert.begin(), |
| ledsDeAssert.end(), |
| std::inserter(newReqChangedLeds, newReqChangedLeds.begin()), |
| ledLess); |
| |
| // prepare reqLedsAssert & reqLedsDeAssert |
| for (auto pair : actionsVec) |
| { |
| ActionSet tmpSet; |
| |
| // Discard current required LED actions, if these LEDs have new actions |
| // in newReqChangedLeds. |
| std::set_difference(pair.first.begin(), pair.first.end(), |
| newReqChangedLeds.begin(), newReqChangedLeds.end(), |
| std::inserter(tmpSet, tmpSet.begin()), ledLess); |
| |
| // Union the remaining LED actions with new LED actions. |
| pair.first.clear(); |
| std::set_union(tmpSet.begin(), tmpSet.end(), pair.second.begin(), |
| pair.second.end(), |
| std::inserter(pair.first, pair.first.begin()), ledLess); |
| } |
| |
| driveLedsHandler(); |
| return; |
| } |
| |
| // Calls into driving physical LED post choosing the action |
| int Manager::drivePhysicalLED(const std::string& objPath, Layout::Action action, |
| uint8_t dutyOn, const uint16_t period) |
| { |
| try |
| { |
| // If Blink, set its property |
| if (action == Layout::Action::Blink) |
| { |
| PropertyValue dutyOnValue{dutyOn}; |
| PropertyValue periodValue{period}; |
| |
| dBusHandler.setProperty(objPath, phyLedIntf, "DutyOn", dutyOnValue); |
| dBusHandler.setProperty(objPath, phyLedIntf, "Period", periodValue); |
| } |
| |
| PropertyValue actionValue{getPhysicalAction(action)}; |
| dBusHandler.setProperty(objPath, phyLedIntf, "State", actionValue); |
| } |
| catch (const sdbusplus::exception_t& e) |
| { |
| lg2::error( |
| "Error setting property for physical LED, ERROR = {ERROR}, OBJECT_PATH = {PATH}", |
| "ERROR", e, "PATH", objPath); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /** @brief Returns action string based on enum */ |
| std::string Manager::getPhysicalAction(Layout::Action action) |
| { |
| namespace server = sdbusplus::xyz::openbmc_project::Led::server; |
| |
| // TODO: openbmc/phosphor-led-manager#5 |
| // Somehow need to use the generated Action enum than giving one |
| // in ledlayout. |
| if (action == Layout::Action::On) |
| { |
| return server::convertForMessage(server::Physical::Action::On); |
| } |
| else if (action == Layout::Action::Blink) |
| { |
| return server::convertForMessage(server::Physical::Action::Blink); |
| } |
| else |
| { |
| return server::convertForMessage(server::Physical::Action::Off); |
| } |
| } |
| |
| void Manager::driveLedsHandler(void) |
| { |
| ActionSet failedLedsAssert; |
| ActionSet failedLedsDeAssert; |
| |
| // This order of LED operation is important. |
| for (const auto& it : reqLedsDeAssert) |
| { |
| std::string objPath = std::string(phyLedPath) + it.name; |
| lg2::debug("De-Asserting LED, NAME = {NAME}, ACTION = {ACTION}", "NAME", |
| it.name, "ACTION", it.action); |
| if (drivePhysicalLED(objPath, Layout::Action::Off, it.dutyOn, |
| it.period)) |
| { |
| failedLedsDeAssert.insert(it); |
| } |
| } |
| |
| for (const auto& it : reqLedsAssert) |
| { |
| std::string objPath = std::string(phyLedPath) + it.name; |
| lg2::debug("Asserting LED, NAME = {NAME}, ACTION = {ACTION}", "NAME", |
| it.name, "ACTION", it.action); |
| if (drivePhysicalLED(objPath, it.action, it.dutyOn, it.period)) |
| { |
| failedLedsAssert.insert(it); |
| } |
| } |
| |
| reqLedsAssert = failedLedsAssert; |
| reqLedsDeAssert = failedLedsDeAssert; |
| |
| if (reqLedsDeAssert.empty() && reqLedsAssert.empty()) |
| { |
| timer.setEnabled(false); |
| } |
| else |
| { |
| timer.restartOnce(std::chrono::seconds(1)); |
| } |
| |
| return; |
| } |
| |
| } // namespace led |
| } // namespace phosphor |