|  | #include <iostream> | 
|  | #include <string> | 
|  | #include <algorithm> | 
|  | #include <phosphor-logging/log.hpp> | 
|  | #include <sdbusplus/exception.hpp> | 
|  | #include <xyz/openbmc_project/Led/Physical/server.hpp> | 
|  | #include "manager.hpp" | 
|  | namespace phosphor | 
|  | { | 
|  | namespace led | 
|  | { | 
|  |  | 
|  | // Assert -or- De-assert | 
|  | bool Manager::setGroupState(const std::string& path, bool assert, | 
|  | group& ledsAssert, group& ledsDeAssert) | 
|  | { | 
|  | if (assert) | 
|  | { | 
|  | assertedGroups.insert(&ledMap.at(path)); | 
|  | } | 
|  | else | 
|  | { | 
|  | auto search = assertedGroups.find(&ledMap.at(path)); | 
|  | if (search != assertedGroups.end()) | 
|  | { | 
|  | assertedGroups.erase(&ledMap.at(path)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // This will contain the union of what's already in the asserted group | 
|  | group desiredState {}; | 
|  | for(const auto& grp : assertedGroups) | 
|  | { | 
|  | desiredState.insert(grp->cbegin(), grp->cend()); | 
|  | } | 
|  |  | 
|  | // Find difference between Combined and Desired to identify | 
|  | // which LEDs are getting altered | 
|  | group transient {}; | 
|  | std::set_difference(combinedState.begin(), combinedState.end(), | 
|  | desiredState.begin(), desiredState.end(), | 
|  | std::inserter(transient, transient.begin()), | 
|  | ledComp); | 
|  | if(transient.size()) | 
|  | { | 
|  | // Find common LEDs between transient and Desired to know if some LEDs | 
|  | // are changing state and not really getting DeAsserted | 
|  | group ledsTransient {}; | 
|  | std::set_intersection(transient.begin(),transient.end(), | 
|  | desiredState.begin(), desiredState.end(), | 
|  | std::inserter(ledsTransient, ledsTransient.begin()), | 
|  | ledLess); | 
|  |  | 
|  | // Find difference between above 2 to identify those LEDs which are | 
|  | // really getting DeAsserted | 
|  | std::set_difference(transient.begin(),transient.end(), | 
|  | ledsTransient.begin(),ledsTransient.end(), | 
|  | std::inserter(ledsDeAssert, ledsDeAssert.begin()), | 
|  | ledLess); | 
|  |  | 
|  | // Remove the elements from Current that are being DeAsserted. | 
|  | if(ledsDeAssert.size()) | 
|  | { | 
|  | // Power off LEDs that are to be really DeAsserted | 
|  | for (auto& it:ledsDeAssert) | 
|  | { | 
|  | // Update LEDs in "physically asserted" set by removing those | 
|  | // LEDs which are De-Asserted | 
|  | auto found = currentState.find(it); | 
|  | if (found != currentState.end()) | 
|  | { | 
|  | currentState.erase(found); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Now LEDs that are to be Asserted. These could either be fresh asserts | 
|  | // -or- change between [On]<-->[Blink] | 
|  | group temp {}; | 
|  | std::unique_copy(desiredState.begin(), desiredState.end(), | 
|  | std::inserter(temp, temp.begin()), | 
|  | ledEqual); | 
|  | if(temp.size()) | 
|  | { | 
|  | // Find difference between [desired to be Asserted] and those LEDs | 
|  | // that are physically asserted currently. | 
|  | std::set_difference(temp.begin(), temp.end(), | 
|  | currentState.begin(), currentState.end(), | 
|  | std::inserter(ledsAssert, ledsAssert.begin()), | 
|  | ledComp); | 
|  | } | 
|  |  | 
|  | // Update the current actual and desired(the virtual actual) | 
|  | currentState = std::move(temp); | 
|  | combinedState = std::move(desiredState); | 
|  |  | 
|  | // If we survive, then set the state accordingly. | 
|  | return assert; | 
|  | } | 
|  |  | 
|  | /** @brief Run through the map and apply action on the LEDs */ | 
|  | void Manager::driveLEDs(group& ledsAssert, group& ledsDeAssert) | 
|  | { | 
|  | using namespace phosphor::logging; | 
|  | // Map of physical LED dbus paths to their Service providers | 
|  | populateObjectMap(); | 
|  |  | 
|  | if (phyLeds.empty()) | 
|  | { | 
|  | // Error message is inside the map construction logic. | 
|  | return; | 
|  | } | 
|  |  | 
|  | // This order of LED operation is important. | 
|  | if (ledsDeAssert.size()) | 
|  | { | 
|  | for (const auto& it: ledsDeAssert) | 
|  | { | 
|  | std::string objPath = std::string(PHY_LED_PATH) + it.name; | 
|  | log<level::DEBUG>("De-Asserting LED", entry("NAME=%s", it.name.c_str())); | 
|  | drivePhysicalLED(objPath, Layout::Action::Off, it.dutyOn); | 
|  | } | 
|  | } | 
|  |  | 
|  | if(ledsAssert.size()) | 
|  | { | 
|  | for (const auto& it: ledsAssert) | 
|  | { | 
|  | std::string objPath = std::string(PHY_LED_PATH) + it.name; | 
|  | log<level::DEBUG>("Asserting LED", entry("NAME=%s", it.name.c_str())); | 
|  | drivePhysicalLED(objPath, it.action, it.dutyOn); | 
|  | } | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Calls into driving physical LED post choosing the action | 
|  | void Manager::drivePhysicalLED(const std::string& objPath, | 
|  | Layout::Action action, | 
|  | uint8_t dutyOn) | 
|  | { | 
|  | using namespace phosphor::logging; | 
|  |  | 
|  | auto service = phyLeds.find(objPath); | 
|  | if (service == phyLeds.end() || service->second.empty()) | 
|  | { | 
|  | log<level::ERR>("No service providers for physical LED", | 
|  | entry("PATH=%s",objPath.c_str())); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // If Blink, set its property | 
|  | if (action == Layout::Action::Blink) | 
|  | { | 
|  | drivePhysicalLED(service->second, objPath, "DutyOn", dutyOn); | 
|  | } | 
|  | drivePhysicalLED(service->second, objPath, "State", | 
|  | getPhysicalAction(action)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | /** @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); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Populates a map with physical LED paths to its service providers */ | 
|  | void Manager::populateObjectMap() | 
|  | { | 
|  | using namespace phosphor::logging; | 
|  |  | 
|  | // Mapper dbus constructs | 
|  | constexpr auto MAPPER_BUSNAME   = "xyz.openbmc_project.ObjectMapper"; | 
|  | constexpr auto MAPPER_OBJ_PATH  = "/xyz/openbmc_project/object_mapper"; | 
|  | constexpr auto MAPPER_IFACE     = "xyz.openbmc_project.ObjectMapper"; | 
|  |  | 
|  | // Needed to be passed to get the SubTree level | 
|  | auto depth = 0; | 
|  |  | 
|  | // Clean start | 
|  | phyLeds.clear(); | 
|  |  | 
|  | // Make a mapper call | 
|  | auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH, | 
|  | MAPPER_IFACE, "GetSubTree"); | 
|  | // Cook rest of the things. | 
|  | mapperCall.append(PHY_LED_PATH); | 
|  | mapperCall.append(depth); | 
|  | mapperCall.append(std::vector<std::string>({PHY_LED_IFACE})); | 
|  |  | 
|  | auto reply = bus.call(mapperCall); | 
|  | if (reply.is_method_error()) | 
|  | { | 
|  | // Its okay if we do not see a corresponding physical LED. | 
|  | log<level::INFO>("Error looking up Physical LED services", | 
|  | entry("PATH=%s",PHY_LED_PATH)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Response by mapper in the case of success | 
|  | std::map<std::string, std::map<std::string, | 
|  | std::vector<std::string>>> objectTree; | 
|  |  | 
|  | // This is the dict of object paths - service names - interfaces | 
|  | try | 
|  | { | 
|  | reply.read(objectTree); | 
|  | } | 
|  | catch (const sdbusplus::exception::SdBusError& e) | 
|  | { | 
|  | log<level::ERR>("Failed to parse Physical LED service lookup", | 
|  | entry("ERROR=%s", e.what()), | 
|  | entry("REPLY_SIG=%s", reply.get_signature())); | 
|  | return; | 
|  | } | 
|  | if (objectTree.empty()) | 
|  | { | 
|  | log<level::INFO>("Physical LED lookup did not return any services", | 
|  | entry("PATH=%s",PHY_LED_PATH)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Now construct our path -> Service name map. | 
|  | for (const auto& iter : objectTree) | 
|  | { | 
|  | phyLeds.emplace(iter.first, iter.second.begin()->first); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | } // namespace led | 
|  | } // namespace phosphor |