blob: ec02419d72d5bceb36546b58d2506f19364d0593 [file] [log] [blame]
#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