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')
+