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/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