From bb8fe0b6e025aaff1b0f0360cf4ddb35f3d0912d Mon Sep 17 00:00:00 2001 From: Vishwanatha Subbanna Date: Sat, 12 Nov 2016 18:29:38 +0530 Subject: Add methods to driving the LEDs This enables creating custom groups and participating LEDs so that it can later be generated from MRW. For each of the group, a dbus object is created which will announce LED actions. Corresponding groups are asserted / de-asserted based on user input. Change-Id: I7e64bea13767b8d083dd946f4cf3aeb37e62ff17 Signed-off-by: Vishwanatha Subbanna --- Makefile.am | 17 +++--- configure.ac | 30 ++++++---- led-main.cpp | 2 +- led-manager.cpp | 166 ++++++++++++++++++++++++++++++++++++++++++-------------- led-manager.hpp | 154 +++++++++++++++++++++++++++++----------------------- parse_led.py | 12 ++-- 6 files changed, 245 insertions(+), 136 deletions(-) diff --git a/Makefile.am b/Makefile.am index c93a550..e146a74 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,17 +1,16 @@ AM_DEFAULT_SOURCE_EXT = .cpp -AM_CPPFLAGS = -I$(top_srcdir)/../sdbusplus +sbin_PROGRAMS = phosphor-ledmanager + +phosphor_ledmanager_SOURCES = \ + led-main.cpp \ + led-manager.cpp BUILT_SOURCES = led-gen.hpp CLEANFILES = led-gen.hpp + PYTHON="/usr/bin/python" led-gen.hpp: ${srcdir}/parse_led.py ${AM_V_at}${PYTHON} $^ > $@ -sbin_PROGRAMS = \ - ledmanager - -ledmanager_SOURCES = \ - led-manager.cpp \ - led-main.cpp - -ledmanager_LDFLAGS = $(SYSTEMD_LIBS) +phosphor_ledmanager_LDFLAGS = $(SYSTEMD_LIBS) +phosphor_ledmanager_CFLAGS = $(SYSTEMD_CFLAGS) diff --git a/configure.ac b/configure.ac index 059f612..600571e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,17 +1,19 @@ # Initialization AC_PREREQ([2.69]) AC_INIT([phosphor-led-manager], [1.0], [https://github.com/openbmc/phosphor-led-manager/issues]) +AC_LANG([C++]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz]) AM_SILENT_RULES([yes]) # Checks for programs. AC_PROG_CXX -AX_CXX_COMPILE_STDCXX_14([noext]) -AM_PROG_AR AC_PROG_INSTALL AC_PROG_MAKE_SET -LT_INIT + +# Checks for typedefs, structures, and compiler characteristics. +AX_CXX_COMPILE_STDCXX_14([noext]) +AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS]) # Python AM_PATH_PYTHON([2.7], [AC_SUBST([PYTHON], [echo "$PYTHON"])], [AC_MSG_ERROR([Could not find python-2.7 installed...python-2.7 is required])]) @@ -19,22 +21,26 @@ AM_PATH_PYTHON([2.7], [AC_SUBST([PYTHON], [echo "$PYTHON"])], [AC_MSG_ERROR([Cou # Checks for libraries. PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221]) +# Check/set gtest specific functions. +AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"]) +AC_SUBST(GTEST_CPPFLAGS) + # Checks for header files. AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd developement package required])]) -#AC_CHECK_HEADER(sdbusplus/vtable.hpp, ,[AC_MSG_ERROR([Could not find vtable.hpp...openbmc/sdbusplus package required])]) -#AC_CHECK_HEADER(dbusplus/message.hpp, ,[AC_MSG_ERROR([Could not find messsage.hpp...openbmc/sdbusplus package required])]) -#AC_CHECK_HEADER(sdbusplus/bus.hpp, ,[AC_MSG_ERROR([Could not find bus.hpp...openbmc/sdbusplus package required])]) +AC_CHECK_HEADER(sdbusplus/server.hpp, ,[AC_MSG_ERROR([Could not find sdbusplus/server.hpp...openbmc/sdbusplus package required])]) -# Checks for typedefs, structures, and compiler characteristics. -AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS]) +# Dbus service name AC_ARG_VAR(BUSNAME, [The Dbus busname to own]) -AC_ARG_VAR(OBJPATH, [The Ledmanager Dbus root]) -AC_ARG_VAR(INTERFACE, [The Ledmanager Dbus interface]) AS_IF([test "x$BUSNAME" == "x"], [BUSNAME="xyz.openbmc_project.ledmanager"]) +AC_DEFINE_UNQUOTED([BUSNAME], ["$BUSNAME"], [The Dbus busname to own]) + +# Service dbus root +AC_ARG_VAR(OBJPATH, [The Ledmanager Dbus root]) AS_IF([test "x$OBJPATH" == "x"], [OBJPATH="/xyz/openbmc_project/ledmanager/groups"]) -AS_IF([test "x$INTERFACE" == "x"], [INTERFACE="xyz.openbmc_project.Ledmanager"]) -AC_DEFINE_UNQUOTED([BUSNAME], ["$BUSNAME"], [The DBus busname to own]) AC_DEFINE_UNQUOTED([OBJPATH], ["$OBJPATH"], [The Ledmanager Dbus root]) + +AC_ARG_VAR(INTERFACE, [The Ledmanager Dbus interface]) +AS_IF([test "x$INTERFACE" == "x"], [INTERFACE="xyz.openbmc_project.ledmanager"]) AC_DEFINE_UNQUOTED([INTERFACE], ["$INTERFACE"], [The Ledmanager Dbus interface]) # Create configured output diff --git a/led-main.cpp b/led-main.cpp index e10c2fe..90bba1e 100644 --- a/led-main.cpp +++ b/led-main.cpp @@ -3,7 +3,7 @@ int main(void) { - phosphor::led::Manager ledMgr(BUSNAME, OBJPATH, INTERFACE); + phosphor::led::Group ledMgr(BUSNAME, OBJPATH, INTERFACE); ledMgr.run(); return 0; } diff --git a/led-manager.cpp b/led-manager.cpp index 8bc1751..bfbf426 100644 --- a/led-manager.cpp +++ b/led-manager.cpp @@ -1,17 +1,19 @@ #include #include +#include #include #include #include #include "led-manager.hpp" #include "led-gen.hpp" - namespace phosphor { - namespace led { +std::set Group::assertedGroups; +Group::group Group::currentState; + /** @brief Called when the group's property is read * Signature as needed by sd_bus */ @@ -19,16 +21,10 @@ int getGroupState(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *data, sd_bus_error* error) { - auto group = strrchr(path, '/'); - if (group) - { - // Removing the starting '/' in /group - group++; - } + auto ledMgr = static_cast(data); + auto state = ledMgr->getGroupState(path); - //TODO : Need to see how to represent group specific asserted state - // May be a new tuple / map ? - sd_bus_message_append(reply, "b", 0); + sd_bus_message_append(reply, "b", state); return 0; } @@ -40,49 +36,135 @@ int setGroupState(sd_bus *bus, const char *path, const char *interface, void *data, sd_bus_error* error) { bool state {}; - auto group = strrchr(path, '/'); - if (group) - { - // Removing the starting '/' in /group - group++; - } - auto msg = sdbusplus::message::message(value); sd_bus_message_ref(value); msg.read(state); - //TODO : Need to see how to represent group specific asserted state - // May be a new tuple / map ? - return 1; + auto ledMgr = static_cast(data); + return ledMgr->setGroupState(path, state); +} + +// Get the asserted state +bool Group::getGroupState(const std::string& path) +{ + return assertedGroups.find(&ledMap.at(path)) != assertedGroups.end(); } -/** @brief Users having to assert a group will just turn this property to 1 - * similarly, setting this property to 0 will deassert the group +// Assert -or- De-assert +int Group::setGroupState(const std::string& path, bool assert) +{ + if (assert) + { + assertedGroups.insert(&ledMap.at(path)); + } + else + { + auto search = assertedGroups.find(&ledMap.at(path)); + if (search != assertedGroups.end()) + { + assertedGroups.erase(&ledMap.at(path)); + } + else + { + std::cout << "Group [ " << path << " ] Not present\n"; + } + } + return driveLEDs(); +} + +// Run through the map and apply action +int Group::driveLEDs() +{ + // 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()); + } + + // Always Do execute Turn Off and then Turn on since we have the Blink + // taking priority over -on- + group ledsToDeAssert {}; + + std::set_difference(currentState.begin(), currentState.end(), + desiredState.begin(), desiredState.end(), + std::inserter(ledsToDeAssert, ledsToDeAssert.begin())); + if(ledsToDeAssert.size()) + { + // We really do not want the Group Manager to know how a particular LED + // transitions from State-A --> State-B and all this must be handled by + // the physical LED controller implementation. + // So in this case, Group Manager really does not want to turn off the + // LEDs and then turning it back on and let the physical LED controller + // handle that. + + // If we previously had a FRU in ON state , and then if there was a + // request to make it blink, the end state would now be blink. + // If we either turn off blink / fault, then we need to go back to its + // previous state. + group ledsToReAssert {}; + std::set_intersection(desiredState.begin(), desiredState.end(), + ledsToDeAssert.begin(), ledsToDeAssert.end(), + std::inserter(ledsToReAssert, ledsToReAssert.begin()), + ledComp); + + if (ledsToReAssert.size()) + { + std::cout << "Asserting LEDs again" << std::endl; + for (const auto& it: ledsToReAssert) + { + std::cout << "\t{" << it.name << "::" << it.action << "}" + << std::endl; + } + } + } + + // Turn on these + group ledsToAssert {}; + std::set_difference(desiredState.begin(), desiredState.end(), + currentState.begin(), currentState.end(), + std::inserter(ledsToAssert, ledsToAssert.begin())); + + if(ledsToAssert.size()) + { + std::cout << "Asserting LEDs" << std::endl; + for (const auto& it: ledsToAssert) + { + std::cout << "\t{" << it.name << "::" << it.action << "}" + << std::endl; + } + } + + // Done.. Save the latest and greatest. + currentState = std::move(desiredState); + + return 0; +} + +/** @brief Users having to assert a group will just turn this property to TRUE + * similarly, setting this property to FALSE will deassert the group */ constexpr sdbusplus::vtable::vtable_t led_vtable[] = { sdbusplus::vtable::start(), - sdbusplus::vtable::property("Assert", "b", - getGroupState, setGroupState, sdbusplus::vtable::property_::emits_change), - sdbusplus::vtable::end() + sdbusplus::vtable::property("Asserted", "b", + getGroupState, setGroupState, + sdbusplus::vtable::property_::emits_change), + sdbusplus::vtable::end() }; /** @brief Initialize the bus and announce services */ -Manager::Manager(const char* busName, - const char* objPath, - const char* intfName) : - iv_bus(sdbusplus::bus::new_system()), - objManager(iv_bus, objPath) +Group::Group(const char* busName, + const char* objPath, + const char* intfName) : + bus(sdbusplus::bus::new_system()), + objManager(bus, objPath) { - // Like /org/openbmc/ledmanager/groups/ - auto path = std::string(objPath) + "/"; - /** Now create so many dbus objects as there are groups */ - for (auto &grp: Manager::cv_LedMap) + for (auto &grp: Group::ledMap) { - auto grpPath = path + grp.first; - intfContainer.emplace_back(sdbusplus::server::interface::interface( - iv_bus, grpPath.c_str(), intfName, led_vtable, this)); + intfContainer.emplace_back(bus, grp.first.c_str(), + intfName, led_vtable, this); // These are now set of structs having LED name and the action. Do not // have anything to be done here at the moment but need to create a @@ -94,18 +176,18 @@ Manager::Manager(const char* busName, } // Once done, claim the bus and systemd will // consider this service started - iv_bus.request_name(busName); + bus.request_name(busName); } /** @brief Wait for client requests */ -void Manager::run() +void Group::run() { while(true) { try { - iv_bus.process_discard(); - iv_bus.wait(); + bus.process_discard(); + bus.wait(); } catch (std::exception &e) { diff --git a/led-manager.hpp b/led-manager.hpp index 1ff869b..32306bd 100644 --- a/led-manager.hpp +++ b/led-manager.hpp @@ -6,92 +6,112 @@ #include #include #include - namespace phosphor { - namespace led { -/** @class Manager +/** @class Group * @brief Manages group of LEDs and applies action on the elements of group */ -class Manager +class Group { -private: - - /** @brief Define possible actions on a given LED. - * For the BLINK operation, follow 50-50 duty cycle - */ - enum Action - { - OFF, - ON, - BLINK, - }; - - /** @brief Dbus constructs used by LED manager */ - sdbusplus::bus::bus iv_bus; - -public: - Manager() = delete; - ~Manager() = default; - Manager(const Manager&) = delete; - Manager& operator=(const Manager&) = delete; - Manager(Manager&&) = default; - Manager& operator=(Manager&&) = default; - - /** @brief Constructs LED manager - * - * @param[in] busName - The Dbus name to own - * @param[in] objPath - The Dbus path that hosts LED manager - * @param[in] intfName - The Dbus interface - */ - Manager(const char* busName, const char* objPath, const char* intfName); - - /** @brief Name of the LED and it's proposed action. - * This structure is supplied as configuration at build time - */ - struct LedAction - { - std::string name; - Action action; - - // Needed for inserting elements into sets - bool operator<(const LedAction& right) const + public: + /** @brief Define possible actions on a given LED. + * For the BLINK operation, follow 50-50 duty cycle + */ + enum Action { - if (name == right.name) + OFF, + ON, + BLINK, + }; + + Group() = delete; + ~Group() = default; + Group(const Group&) = delete; + Group& operator=(const Group&) = delete; + Group(Group&&) = delete; + Group& operator=(Group&&) = delete; + + /** @brief Constructs LED manager + * + * @param[in] busName - The Dbus name to own + * @param[in] objPath - The Dbus path that hosts LED manager + * @param[in] intfName - The Dbus interface + */ + Group(const char* busName, const char* objPath, const char* intfName); + + /** @brief Name of the LED and it's proposed action. + * This structure is supplied as configuration at build time + */ + struct LedAction + { + std::string name; + Action action; + + // Needed for inserting elements into sets + bool operator<(const LedAction& right) const { - return action < right.action; + if (name == right.name) + { + return action < right.action; + } + return name < right.name; } - return name < right.name; - } + }; - // Needed for set union / intersect - bool operator==(const LedAction& right) const + /** @brief For finding intersection */ + static bool ledComp(const LedAction& left, const LedAction& right) { - // Only if name and action are same, consider equal - if (name == right.name && - action == right.action) - { - return true; - } - return false; + return left.name < right.name; } - }; - /** @brief static global map constructed at compile time */ - static const std::map> cv_LedMap; + using group = std::set; + + /** @brief static global map constructed at compile time */ + static const std::map ledMap; + + /** @brief Dbus constructs used by LED manager */ + sdbusplus::bus::bus bus; + + /** @brief sd_bus object manager */ + sdbusplus::server::manager::manager objManager; + + /** @brief Individual objects */ + std::vector intfContainer; + + /** @brief Pointers to groups that are in asserted state */ + static std::set assertedGroups; + + /** @brief Contains the LEDs that are in asserted state */ + static group currentState; + + /** @brief Waits on the client request and processes them */ + void run(); - /** @brief sd_bus object manager */ - sdbusplus::server::manager::manager objManager; + /** @brief Given a group name, tells if its in asserted state or not. + * + * @param[in] name - Group name + * @return - Whether in asserted state or not + */ + bool getGroupState(const std::string& name); - /** @brief Individual objects */ - std::vector intfContainer; + /** @brief Given a group name, applies the action on the group + * + * @param[in] name - Group name + * @param[in] assert - Could be 0 or 1 + * @return - Success or exception thrown + */ + int setGroupState(const std::string& name, bool assert); - void run(); + private: + /** @brief Finds the set of LEDs to operate on and executes action + * + * @return: Returns '0' for now. + */ + int driveLEDs(); }; } // namespace led diff --git a/parse_led.py b/parse_led.py index 96bcd5e..7f212ea 100755 --- a/parse_led.py +++ b/parse_led.py @@ -1,21 +1,23 @@ #!/usr/bin/env python import yaml +import os if __name__ == '__main__': - with open('led.yaml', 'r') as f: + script_dir = os.path.dirname(os.path.realpath(__file__)) + with open(os.path.join(script_dir, 'led.yaml'), 'r') as f: ifile = yaml.safe_load(f) - with open('led-gen.hpp', 'w') as ofile: + with open(os.path.join(script_dir, 'led-gen.hpp'), 'w') as ofile: ofile.write('/* !!! WARNING: This is a GENERATED Code..') ofile.write('Please do NOT Edit !!! */\n\n') ofile.write('const std::map>') - ofile.write(' phosphor::led::Manager::cv_LedMap = {\n\n') + ofile.write(' std::set>') + ofile.write(' phosphor::led::Group::ledMap = {\n\n') for group in ifile.iterkeys(): # Value of this group is a std::set ledset = ifile[group] - ofile.write(' {\"' + group + '\",{\n') + ofile.write(' {\"' + "/xyz/openbmc_project/ledmanager/groups/" + group + '\",{\n') for led_dict, list_dict in ledset.iteritems(): for name, value in list_dict.iteritems(): -- cgit v1.2.1