Drive physical LED when a LED group is actioned on

Change-Id: I3107c1d961c459379b77548a738533567eccf693
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/Makefile.am b/Makefile.am
index 921ec4c..228a810 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,7 +13,7 @@
 led-gen.hpp: ${srcdir}/parse_led.py
 	$(AM_V)@LEDGEN@ > $@
 
-phosphor_ledmanager_LDFLAGS = $(SYSTEMD_LIBS)
-phosphor_ledmanager_CFLAGS = $(SYSTEMD_CFLAGS)
+phosphor_ledmanager_LDFLAGS = $(SYSTEMD_LIBS) $(SDBUSPLUS_LIBS) $(PHOSPHOR_LOGGING_LIBS)
+phosphor_ledmanager_CFLAGS = $(SYSTEMD_CFLAGS) $(SDBUSPLUS_CFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
 
 SUBDIRS = test
diff --git a/configure.ac b/configure.ac
index 0286aa6..83c45bd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,6 +46,7 @@
 # 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/server.hpp, ,[AC_MSG_ERROR([Could not find sdbusplus/server.hpp...openbmc/sdbusplus package required])])
+PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],, [AC_MSG_ERROR([Could not find phosphor-logging...openbmc/phosphor-logging package required])])
 
 # Dbus service name
 AC_ARG_VAR(BUSNAME, [The Dbus busname to own])
diff --git a/led-main.cpp b/led-main.cpp
index 2dc0dce..4df1a6a 100644
--- a/led-main.cpp
+++ b/led-main.cpp
@@ -7,12 +7,12 @@
 
 int main(void)
 {
-    /** @brief Group manager object */
-    phosphor::led::Manager manager(systemLedMap);
-
     /** @brief Dbus constructs used by LED Group manager */
     sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
 
+    /** @brief Group manager object */
+    phosphor::led::Manager manager(bus, systemLedMap);
+
     /** @brief sd_bus object manager */
     sdbusplus::server::manager::manager objManager(bus, OBJPATH);
 
diff --git a/manager.cpp b/manager.cpp
index e4137d3..44b0480 100644
--- a/manager.cpp
+++ b/manager.cpp
@@ -1,6 +1,7 @@
 #include <iostream>
 #include <string>
 #include <algorithm>
+#include <phosphor-logging/log.hpp>
 #include "manager.hpp"
 namespace phosphor
 {
@@ -80,6 +81,13 @@
 void Manager::driveLEDs(group& ledsAssert, group& ledsDeAssert,
                         group& ledsUpdate)
 {
+    // For now, physical LED is driven by xyz.openbmc_project.Led.Controller
+    // at /xyz/openbmc_project/led/physical. However, its possible that in the
+    // future, the physical LEDs are driven by different dbus services.
+    // when that happens, service name needs to be obtained everytime a
+    // particular LED would be targeted as opposed to getting one now and then
+    // using it for all
+
     // This order of LED operation is important.
     if (ledsUpdate.size())
     {
@@ -87,8 +95,8 @@
                   << std::endl;
         for (const auto& it: ledsUpdate)
         {
-            std::cout << "\t{" << it.name << "::" << it.action << "}"
-                << std::endl;
+            std::string objPath = std::string(PHY_LED_PATH) + it.name;
+            drivePhysicalLED(objPath, it.action, it.dutyOn);
         }
     }
 
@@ -97,8 +105,8 @@
         std::cout << "De-Asserting LEDs" << std::endl;
         for (const auto& it: ledsDeAssert)
         {
-            std::cout << "\t{" << it.name << "::" << it.action << "}"
-                << std::endl;
+            std::string objPath = std::string(PHY_LED_PATH) + it.name;
+            drivePhysicalLED(objPath, Layout::Action::Off, it.dutyOn);
         }
     }
 
@@ -107,12 +115,91 @@
         std::cout << "Asserting LEDs" << std::endl;
         for (const auto& it: ledsAssert)
         {
-            std::cout << "\t{" << it.name << "::" << it.action << "}"
-                << std::endl;
+            std::string objPath = std::string(PHY_LED_PATH) + it.name;
+            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)
+{
+    auto service = getServiceName(objPath, PHY_LED_IFACE);
+    if (!service.empty())
+    {
+        // If Blink, set its property
+        if (action == Layout::Action::Blink)
+        {
+            drivePhysicalLED(service, objPath, "DutyOn", dutyOn);
+        }
+        drivePhysicalLED(service, objPath, "State",
+                getPhysicalAction(action));
+    }
+}
+
+/** @brief Returns action string based on enum */
+const char* const Manager::getPhysicalAction(Layout::Action action)
+{
+    // TODO : When this is moved over to using libdus interfaces, this code will
+    // away. https://github.com/openbmc/phosphor-led-manager/issues/2
+    if(action == Layout::Action::On)
+    {
+        return "xyz.openbmc_project.Led.Physical.Action.On";
+    }
+    else if(action == Layout::Action::Blink)
+    {
+        return "xyz.openbmc_project.Led.Physical.Action.Blink";
+    }
+    else
+    {
+        return "xyz.openbmc_project.Led.Physical.Action.Off";
+    }
+}
+
+/** Given the LED dbus path and interface, returns the service name */
+std::string Manager::getServiceName(const std::string& objPath,
+                                    const std::string& interface) const
+{
+    using namespace phosphor::logging;
+
+    // Mapper dbus constructs
+    constexpr auto MAPPER_BUSNAME   = "xyz.openbmc_project.ObjectMapper";
+    constexpr auto MAPPER_OBJ_PATH  = "/xyz/openbmc_project/ObjectMapper";
+    constexpr auto MAPPER_IFACE     = "xyz.openbmc_project.ObjectMapper";
+
+    // Make a mapper call
+    auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
+                                          MAPPER_IFACE, "GetObject");
+    // Cook rest of the things.
+    mapperCall.append(objPath);
+    mapperCall.append(std::vector<std::string>({interface}));
+
+    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 service",
+                entry("PATH=%s",objPath.c_str()));
+        return "";
+    }
+
+    // Response by mapper in the case of success
+    std::map<std::string, std::vector<std::string>> serviceNames;
+
+    // This is the service name for the passed in objpath
+    reply.read(serviceNames);
+    if (serviceNames.empty())
+    {
+        log<level::INFO>("Physical LED lookup did not return any service",
+                entry("PATH=%s",objPath.c_str()));
+        return "";
+    }
+
+    return serviceNames.begin()->first;
+}
+
 } // namespace led
 } // namespace phosphor
diff --git a/manager.hpp b/manager.hpp
index 10af42d..8dbbcd6 100644
--- a/manager.hpp
+++ b/manager.hpp
@@ -2,12 +2,18 @@
 
 #include <map>
 #include <set>
+#include <sdbusplus/bus.hpp>
 #include "ledlayout.hpp"
 namespace phosphor
 {
 namespace led
 {
 
+/** @brief Physical LED dbus constructs */
+constexpr auto PHY_LED_PATH = "/xyz/openbmc_project/led/physical/";
+constexpr auto PHY_LED_IFACE = "xyz.openbmc_project.Led.Physical";
+constexpr auto DBUS_PROPERTY_IFACE = "org.freedesktop.DBus.Properties";
+
 /** @class Manager
  *  @brief Manages group of LEDs and applies action on the elements of group
  */
@@ -30,16 +36,20 @@
         }
 
         using group = std::set<phosphor::led::Layout::LedAction>;
+        using LedLayout = std::map<std::string, group>;
 
         /** @brief static global map constructed at compile time */
-        const std::map<std::string, group>& ledMap;
+        const LedLayout& ledMap;
 
-        /** @brief Refer the user supplied LED layout.
+        /** @brief Refer the user supplied LED layout and sdbusplus handler
          *
-         *  @param [in] ledLayout - LEDs group layout
+         *  @param [in] bus       - sdbusplus handler
+         *  @param [in] LedLayout - LEDs group layout
          */
-        explicit Manager(const std::map<std::string, Manager::group>& ledLayout)
-                : ledMap(ledLayout)
+        Manager(sdbusplus::bus::bus& bus,
+                const LedLayout& ledLayout)
+                : ledMap(ledLayout),
+                  bus(bus)
         {
             // Nothing here
         }
@@ -72,12 +82,71 @@
                        group& ledsUpdate);
 
     private:
+        /** @brief sdbusplus handler */
+        sdbusplus::bus::bus& bus;
+
         /** @brief Pointers to groups that are in asserted state */
         std::set<const group*> assertedGroups;
 
         /** @brief Contains the LEDs that are in asserted state */
         group currentState;
 
+        /** @brief Returns action string based on enum
+         *
+         *  @param[in]  action - Action enum
+         *
+         *  @return string equivalent of the passed in enumeration
+         */
+        static const char* const getPhysicalAction(Layout::Action action);
+
+        /** @brief Chooses appropriate action to be triggered on physical LED
+         *  and calls into function that applies the actual action.
+         *
+         *  @param[in]  objPath   -  dbus object path
+         *  @param[in]  action    -  Intended action to be triggered
+         *  @param[in]  dutyOn    -  Duty Cycle ON percentage
+         */
+        void drivePhysicalLED(const std::string& objPath,
+                              Layout::Action action,
+                              uint8_t dutyOn);
+
+        /** @brief Makes a dbus call to a passed in service name.
+         *  This is now the physical LED controller
+         *
+         *  @param[in]  service   -  dbus service name
+         *  @param[in]  objPath   -  dbus object path
+         *  @param[in]  property  -  property to be written to
+         *  @param[in]  value     -  Value to write
+         */
+        template <typename T>
+        void drivePhysicalLED(const std::string& service,
+                              const std::string& objPath,
+                              const std::string& property,
+                              const T& value)
+        {
+            sdbusplus::message::variant<T> data = value;
+
+            auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
+                                              DBUS_PROPERTY_IFACE, "Set");
+            method.append(PHY_LED_IFACE);
+            method.append(property);
+            method.append(data);
+
+            // There will be exceptions going forward and hence don't need a
+            // response
+            bus.call_noreply(method);
+            return;
+        }
+
+        /** @brief Finds the service name given a dbus object path and interface
+         *
+         *  @param[in]  objPath    -  dbus object path
+         *  @param[in]  interface  -  dbus interface
+         *
+         *  @return: Service name or none
+         */
+        std::string getServiceName(const std::string& objPath,
+                                   const std::string& interface) const;
 };
 
 } // namespace led
diff --git a/test/Makefile.am b/test/Makefile.am
index e434c40..6efd2c7 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -5,8 +5,8 @@
 
 # # Build/add utest to test suite
 check_PROGRAMS = utest
-utest_CPPFLAGS = -Igtest $(GTEST_CPPFLAGS) $(AM_CPPFLAGS)
+utest_CPPFLAGS = -Igtest $(GTEST_CPPFLAGS) $(AM_CPPFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
 utest_CXXFLAGS = $(PTHREAD_CFLAGS)
-utest_LDFLAGS = -lgtest_main -lgtest $(PTHREAD_LIBS) $(OESDK_TESTCASE_FLAGS)
+utest_LDFLAGS = -lgtest_main -lgtest $(PTHREAD_LIBS) $(OESDK_TESTCASE_FLAGS) $(SYSTEMD_LIBS) $(PHOSPHOR_LOGGING_LIBS)
 utest_SOURCES = utest.cpp
 utest_LDADD = $(top_builddir)/manager.o
diff --git a/test/utest.cpp b/test/utest.cpp
index 38f8196..7450f09 100644
--- a/test/utest.cpp
+++ b/test/utest.cpp
@@ -1,6 +1,7 @@
 #include <set>
 #include <algorithm>
 #include <gtest/gtest.h>
+#include <sdbusplus/bus.hpp>
 #include "manager.hpp"
 #include "led-test-map.hpp"
 
@@ -8,11 +9,12 @@
 class LedTest : public ::testing::Test
 {
     public:
-        virtual void SetUp()
+        sdbusplus::bus::bus bus;
+        LedTest() : bus(sdbusplus::bus::new_default())
         {
-            // Not having a need at the moment but for future.
+            // Nothing here
         }
-        virtual void TearDown()
+        ~LedTest()
         {
             // Leaving upto auto cleanup.
         }
@@ -21,7 +23,7 @@
 /** @brief Assert Single LED to On */
 TEST_F(LedTest, assertSingleLedOn)
 {
-    Manager manager(singleLedOn);
+    Manager manager(bus, singleLedOn);
     {
         // Assert the LEDs.
         Manager::group ledsAssert {};
@@ -53,7 +55,7 @@
 /** @brief Assert Single LED to Blink */
 TEST_F(LedTest, assertSingleLedBlink)
 {
-    Manager manager(singleLedBlink);
+    Manager manager(bus, singleLedBlink);
     {
         // Assert the LEDs.
         Manager::group ledsAssert {};
@@ -85,7 +87,7 @@
 /** @brief Assert Single LED to On and Try Assert Again */
 TEST_F(LedTest, assertSingleLedOnAndreAssert)
 {
-    Manager manager(singleLedOn);
+    Manager manager(bus, singleLedOn);
     {
         // Assert the LEDs.
         Manager::group ledsAssert {};
@@ -132,7 +134,7 @@
 /** @brief Assert Multiple LEDs to On */
 TEST_F(LedTest, assertMultipleLedOn)
 {
-    Manager manager(multipleLedsOn);
+    Manager manager(bus, multipleLedsOn);
     {
         // Assert the LEDs.
         Manager::group ledsAssert {};
@@ -166,7 +168,7 @@
 /** @brief Assert Multiple LEDs to Blink */
 TEST_F(LedTest, assertMultipleLedBlink)
 {
-    Manager manager(multipleLedsBlink);
+    Manager manager(bus, multipleLedsBlink);
     {
         // Assert the LEDs.
         Manager::group ledsAssert {};
@@ -200,7 +202,7 @@
 /** @brief Assert Multiple LEDs to Blink, DeAssert */
 TEST_F(LedTest, assertMultipleLedBlinkAndDeAssert)
 {
-    Manager manager(multipleLedsBlink);
+    Manager manager(bus, multipleLedsBlink);
     {
         // Assert the LEDs.
         Manager::group ledsAssert {};
@@ -262,7 +264,7 @@
 /** @brief Assert Multiple LEDs to Blink, DeAssert Twice */
 TEST_F(LedTest, assertMultipleLedBlinkAndDeAssertTwice)
 {
-    Manager manager(multipleLedsBlink);
+    Manager manager(bus, multipleLedsBlink);
     {
         // Assert the LEDs.
         Manager::group ledsAssert {};
@@ -338,7 +340,7 @@
 /** @brief Assert Multiple LEDs to mix of On and Blink */
 TEST_F(LedTest, assertMultipleLedOnAndBlink)
 {
-    Manager manager(multipleLedsOnAndBlink);
+    Manager manager(bus, multipleLedsOnAndBlink);
     {
         // Assert the LEDs.
         Manager::group ledsAssert {};
@@ -374,7 +376,7 @@
 /** @brief Assert 2 groups having distinct LEDs */
 TEST_F(LedTest, assertTwoGroupsOnWithDistinctLEDOn)
 {
-    Manager manager(twoGroupsWithDistinctLEDsOn);
+    Manager manager(bus, twoGroupsWithDistinctLEDsOn);
     {
         // Assert Set-A
         Manager::group ledsAssert {};
@@ -436,7 +438,7 @@
 /** @brief Assert 2 groups having one of the LEDs common */
 TEST_F(LedTest, asserttwoGroupsWithOneComonLEDOn)
 {
-    Manager manager(twoGroupsWithOneComonLEDOn);
+    Manager manager(bus, twoGroupsWithOneComonLEDOn);
     {
         // Assert Set-A
         Manager::group ledsAssert {};
@@ -497,7 +499,7 @@
 /** @brief Assert 2 groups having one of the LEDs common in different state */
 TEST_F(LedTest, assertTwoGroupsWithOneComonLEDInDifferentState)
 {
-    Manager manager(twoGroupsWithOneComonLEDInDifferentState);
+    Manager manager(bus, twoGroupsWithOneComonLEDInDifferentState);
     {
         // Assert Set-A
         Manager::group ledsAssert {};
@@ -560,7 +562,7 @@
 /** @brief Assert 2 groups having multiple common LEDs in Same State */
 TEST_F(LedTest, assertTwoGroupsWithMultiplComonLEDOn)
 {
-    Manager manager(twoGroupsWithMultiplComonLEDOn);
+    Manager manager(bus, twoGroupsWithMultiplComonLEDOn);
     {
         // Assert Set-B
         Manager::group ledsAssert {};
@@ -621,7 +623,7 @@
 /** @brief Assert 2 groups having multiple LEDs common in different state */
 TEST_F(LedTest, assertTwoGroupsWithMultipleComonLEDInDifferentStates)
 {
-    Manager manager(twoGroupsWithMultipleComonLEDInDifferentState);
+    Manager manager(bus, twoGroupsWithMultipleComonLEDInDifferentState);
     {
         // Assert Set-A
         Manager::group ledsAssert {};
@@ -687,7 +689,7 @@
  */
 TEST_F(LedTest, assertTwoGroupsWithMultipleComonLEDAndDeAssertOne)
 {
-    Manager manager(twoGroupsWithMultiplComonLEDOn);
+    Manager manager(bus, twoGroupsWithMultiplComonLEDOn);
     {
         // Assert Set-A
         Manager::group ledsAssert {};
@@ -775,7 +777,7 @@
  *         different state and De-Assert one*/
 TEST_F(LedTest, assertTwoGroupsWithMultipleComonLEDInDifferentStateDeAssertOne)
 {
-    Manager manager(twoGroupsWithMultipleComonLEDInDifferentState);
+    Manager manager(bus, twoGroupsWithMultipleComonLEDInDifferentState);
     {
         // Assert Set-B
         Manager::group ledsAssert {};