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 <vishwa@linux.vnet.ibm.com>
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 @@
# 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 <iostream>
#include <cstring>
+#include <algorithm>
#include <sdbusplus/vtable.hpp>
#include <sdbusplus/message.hpp>
#include <sdbusplus/bus.hpp>
#include "led-manager.hpp"
#include "led-gen.hpp"
-
namespace phosphor
{
-
namespace led
{
+std::set<const Group::group*> 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 @@
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<Group*>(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 @@
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<Group*>(data);
+ return ledMgr->setGroupState(path, state);
}
-/** @brief Users having to assert a group will just turn this property to 1
- * similarly, setting this property to 0 will deassert the group
+// Get the asserted state
+bool Group::getGroupState(const std::string& path)
+{
+ return assertedGroups.find(&ledMap.at(path)) != assertedGroups.end();
+}
+
+// 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 @@
}
// 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 <sdbusplus/bus.hpp>
#include <sdbusplus/server/interface.hpp>
#include <sdbusplus/server/manager.hpp>
-
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;
+ };
+
+ /** @brief For finding intersection */
+ static bool ledComp(const LedAction& left, const LedAction& right)
+ {
+ return left.name < right.name;
}
- // Needed for set union / intersect
- bool operator==(const LedAction& right) const
- {
- // Only if name and action are same, consider equal
- if (name == right.name &&
- action == right.action)
- {
- return true;
- }
- return false;
- }
- };
+ using group = std::set<LedAction>;
- /** @brief static global map constructed at compile time */
- static const std::map<std::string,
- std::set<LedAction>> cv_LedMap;
+ /** @brief static global map constructed at compile time */
+ static const std::map<std::string, group> ledMap;
- /** @brief sd_bus object manager */
- sdbusplus::server::manager::manager objManager;
+ /** @brief Dbus constructs used by LED manager */
+ sdbusplus::bus::bus bus;
- /** @brief Individual objects */
- std::vector<sdbusplus::server::interface::interface> intfContainer;
+ /** @brief sd_bus object manager */
+ sdbusplus::server::manager::manager objManager;
- void run();
+ /** @brief Individual objects */
+ std::vector<sdbusplus::server::interface::interface> intfContainer;
+
+ /** @brief Pointers to groups that are in asserted state */
+ static std::set<const group*> 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 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 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);
+
+ 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<std::string,')
- ofile.write(' std::set<phosphor::led::Manager::LedAction>>')
- ofile.write(' phosphor::led::Manager::cv_LedMap = {\n\n')
+ ofile.write(' std::set<phosphor::led::Group::LedAction>>')
+ ofile.write(' phosphor::led::Group::ledMap = {\n\n')
for group in ifile.iterkeys():
# Value of this group is a std::set<string, led structure>
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():