Add LED grouping support
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.
Fixes openbmc/openbmc#550
Change-Id: I7a56d08755288dcfce45ee4c6d6b6c5e5aa454f7
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..c93a550
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,17 @@
+AM_DEFAULT_SOURCE_EXT = .cpp
+AM_CPPFLAGS = -I$(top_srcdir)/../sdbusplus
+
+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)
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100755
index 0000000..412550f
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+AUTOCONF_FILES="Makefile.in aclocal.m4 ar-lib autom4te.cache compile config.* \
+ configure depcomp install-sh ltmain.sh missing *libtool"
+
+case $1 in
+ clean)
+ test -f Makefile && make maintainer-clean
+ rm -rf ${AUTOCONF_FILES}
+
+ exit 0
+ ;;
+esac
+
+autoreconf -i
+echo 'Run "./configure ${CONFIGURE_FLAGS} && make"'
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..059f612
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,42 @@
+# Initialization
+AC_PREREQ([2.69])
+AC_INIT([phosphor-led-manager], [1.0], [https://github.com/openbmc/phosphor-led-manager/issues])
+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
+
+# 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])])
+
+# Checks for libraries.
+PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221])
+
+# 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])])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS])
+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"])
+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_DEFINE_UNQUOTED([INTERFACE], ["$INTERFACE"], [The Ledmanager Dbus interface])
+
+# Create configured output
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/led-main.cpp b/led-main.cpp
new file mode 100644
index 0000000..e10c2fe
--- /dev/null
+++ b/led-main.cpp
@@ -0,0 +1,9 @@
+#include "led-manager.hpp"
+#include "config.h"
+
+int main(void)
+{
+ phosphor::led::Manager ledMgr(BUSNAME, OBJPATH, INTERFACE);
+ ledMgr.run();
+ return 0;
+}
diff --git a/led-manager.cpp b/led-manager.cpp
new file mode 100644
index 0000000..8bc1751
--- /dev/null
+++ b/led-manager.cpp
@@ -0,0 +1,119 @@
+#include <iostream>
+#include <cstring>
+#include <sdbusplus/vtable.hpp>
+#include <sdbusplus/message.hpp>
+#include <sdbusplus/bus.hpp>
+#include "led-manager.hpp"
+#include "led-gen.hpp"
+
+namespace phosphor
+{
+
+namespace led
+{
+
+/** @brief Called when the group's property is read
+ * Signature as needed by sd_bus
+ */
+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++;
+ }
+
+ //TODO : Need to see how to represent group specific asserted state
+ // May be a new tuple / map ?
+ sd_bus_message_append(reply, "b", 0);
+ return 0;
+}
+
+/** @brief Called when the group's asserted state is updated
+ * Signature as needed by sd_bus
+ */
+int setGroupState(sd_bus *bus, const char *path, const char *interface,
+ const char *property, sd_bus_message *value,
+ 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;
+}
+
+/** @brief Users having to assert a group will just turn this property to 1
+ * similarly, setting this property to 0 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()
+};
+
+/** @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)
+{
+ // 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)
+ {
+ auto grpPath = path + grp.first;
+ intfContainer.emplace_back(sdbusplus::server::interface::interface(
+ iv_bus, grpPath.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
+ // mapping between led names to device strigs eventually
+ //for (auto &set: grp.second)
+ //{
+
+ //}
+ }
+ // Once done, claim the bus and systemd will
+ // consider this service started
+ iv_bus.request_name(busName);
+}
+
+/** @brief Wait for client requests */
+void Manager::run()
+{
+ while(true)
+ {
+ try
+ {
+ iv_bus.process_discard();
+ iv_bus.wait();
+ }
+ catch (std::exception &e)
+ {
+ std::cerr << e.what() << std::endl;
+ }
+ }
+}
+
+} // namespace led
+
+} // namespace phosphor
diff --git a/led-manager.hpp b/led-manager.hpp
new file mode 100644
index 0000000..1ff869b
--- /dev/null
+++ b/led-manager.hpp
@@ -0,0 +1,99 @@
+#pragma once
+
+#include <map>
+#include <set>
+#include <vector>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/interface.hpp>
+#include <sdbusplus/server/manager.hpp>
+
+namespace phosphor
+{
+
+namespace led
+{
+
+/** @class Manager
+ * @brief Manages group of LEDs and applies action on the elements of group
+ */
+
+class Manager
+{
+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
+ {
+ if (name == right.name)
+ {
+ return action < right.action;
+ }
+ return 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;
+ }
+ };
+
+ /** @brief static global map constructed at compile time */
+ static const std::map<std::string,
+ std::set<LedAction>> cv_LedMap;
+
+ /** @brief sd_bus object manager */
+ sdbusplus::server::manager::manager objManager;
+
+ /** @brief Individual objects */
+ std::vector<sdbusplus::server::interface::interface> intfContainer;
+
+ void run();
+};
+
+} // namespace led
+
+} // namespace phosphor
diff --git a/led.yaml b/led.yaml
new file mode 100755
index 0000000..0d01c96
--- /dev/null
+++ b/led.yaml
@@ -0,0 +1,119 @@
+EnclosureIdentify:
+ EnclosureIdentifyFront:
+ action: blink
+ EnclosureIdentifyBack:
+ action: blink
+
+EnclosureFault:
+ EnclosureFaultFront:
+ action: 'on'
+ EnclosureFaultBack:
+ action: 'on'
+
+PowerSupply1Identify:
+ PowerSupply_1:
+ action: blink
+ EnclosureIdentifyFront:
+ action: 'on'
+ EnclosureIdentifyBack:
+ action: 'on'
+
+PowerSupply1Fault:
+ PowerSupply_1:
+ action: 'on'
+ EnclosureFaultFront:
+ action: 'on'
+ EnclosureFaultBack:
+ action: 'on'
+
+PowerSupply2Identify:
+ PowerSupply_2:
+ action: blink
+ EnclosureIdentifyFront:
+ action: 'on'
+ EnclosureIdentifyBack:
+ action: 'on'
+
+PowerSupply2Fault:
+ PowerSupply_2:
+ action: 'on'
+ EnclosureFaultFront:
+ action: 'on'
+ EnclosureFaultBack:
+ action: 'on'
+
+FanA1Identify:
+ Fan_A1:
+ action: blink
+ EnclosureIdentifyFront:
+ action: 'on'
+ EnclosureIdentifyBack:
+ action: 'on'
+
+FanA1Fault:
+ Fan_A1:
+ action: 'on'
+ EnclosureFaultFront:
+ action: 'on'
+ EnclosureFaultBack:
+ action: 'on'
+
+FanA2Identify:
+ Fan_A2:
+ action: blink
+ EnclosureIdentifyFront:
+ action: 'on'
+ EnclosureIdentifyBack:
+ action: 'on'
+
+FanA2Fault:
+ Fan_A2:
+ action: 'on'
+ EnclosureFaultFront:
+ action: 'on'
+ EnclosureFaultBack:
+ action: 'on'
+
+FanA3Identify:
+ Fan_A3:
+ action: blink
+ EnclosureIdentifyFront:
+ action: 'on'
+ EnclosureIdentifyBack:
+ action: 'on'
+
+FanA3Fault:
+ Fan_A3:
+ action: 'on'
+ EnclosureFaultFront:
+ action: 'on'
+ EnclosureFaultBack:
+ action: 'on'
+
+FanA4Identify:
+ Fan_A4:
+ action: blink
+ EnclosureIdentifyFront:
+ action: 'on'
+ EnclosureIdentifyBack:
+ action: 'on'
+
+FanA4Fault:
+ Fan_A4:
+ action: 'on'
+ EnclosureFaultFront:
+ action: 'on'
+ EnclosureFaultBack:
+ action: 'on'
+
+PowerOn:
+ OpPanelFront:
+ action: 'on'
+ PanelPowerButtonNote1:
+ action: 'on'
+
+PowerOff:
+ OpPanelFront:
+ action: blink
+ PanelPowerButtonNote1:
+ action: blink
diff --git a/parse_led.py b/parse_led.py
new file mode 100755
index 0000000..96bcd5e
--- /dev/null
+++ b/parse_led.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+import yaml
+
+if __name__ == '__main__':
+ with open('led.yaml', 'r') as f:
+ ifile = yaml.safe_load(f)
+
+ with open('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')
+ for group in ifile.iterkeys():
+ # Value of this group is a std::set<string, led structure>
+ ledset = ifile[group]
+ ofile.write(' {\"' + group + '\",{\n')
+
+ for led_dict, list_dict in ledset.iteritems():
+ for name, value in list_dict.iteritems():
+ if group and led_dict and name and value:
+ ofile.write(' {\"' + led_dict + '\",')
+ ofile.write(value.upper() + '},\n')
+ ofile.write(' }},\n')
+ ofile.write('};\n')
+