Add methods to trigger action on LED

Implements the ON, OFF and BLINK operations on a given LED.

Change-Id: I74b5ac01d8e76961999a2673d52d73296f5603d7
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/physical.cpp b/physical.cpp
index 31fc1ee..38c69b0 100644
--- a/physical.cpp
+++ b/physical.cpp
@@ -25,32 +25,129 @@
 /** @brief Populates key parameters */
 void Physical::setInitialState()
 {
+    // Control files in /sys/class/leds/<led-name>
+    brightCtrl = path + BRIGHTNESS;
+    blinkCtrl =  path + BLINKCTRL;
+
+    delayOnCtrl = path + DELAYON;
+    delayOffCtrl = path + DELAYOFF;
+
     // 1. read /sys/class/leds/name/trigger
     // 2. If its 'timer', then its blinking.
     //    2.1: On blink, use delay_on and delay_off into dutyOn
     // 3. If its 'none', then read brightness. 255 means, its ON, else OFF.
-    // Implementation in the next patchset.
+
+    auto trigger = read<std::string>(blinkCtrl);
+    if (trigger == "timer")
+    {
+        // LED is blinking. Get the delay_on and delay_off and compute
+        // DutyCycle. sfsfs values are in strings. Need to convert 'em over to
+        // integer.
+        auto delayOn = std::stoi(read<std::string>(delayOnCtrl));
+        auto delayOff = std::stoi(read<std::string>(delayOffCtrl));
+
+        // Calculate frequency and then percentage ON
+        frequency = delayOn + delayOff;
+        auto factor = frequency / 100;
+        auto dutyOn = delayOn / factor;
+
+        // Update.
+        this->dutyOn(dutyOn);
+    }
+    else
+    {
+        // This is hardcoded for now. This will be changed to this->frequency()
+        // when frequency is implemented.
+        // TODO
+        frequency = 1000;
+
+        // LED is either ON or OFF
+        auto brightness = read<std::string>(brightCtrl);
+        if (brightness == std::string(ASSERT))
+        {
+            // LED is in Solid ON
+            sdbusplus::xyz::openbmc_project::Led::server
+                          ::Physical::state(Action::On);
+        }
+        else
+        {
+            // LED is in OFF state
+            sdbusplus::xyz::openbmc_project::Led::server
+                          ::Physical::state(Action::Off);
+        }
+    }
+    return;
 }
 
 /** @brief Overloaded State Property Setter function */
 auto Physical::state(Action value) -> Action
 {
-    // Set the base class's state to actuals since the getter
-    // operation is handled there.
-    auto action = sdbusplus::xyz::openbmc_project::Led::server
+    // Obtain current operation
+    auto current = sdbusplus::xyz::openbmc_project::Led::server
+                                   ::Physical::state();
+
+    // Update requested operation into base class
+    auto requested = sdbusplus::xyz::openbmc_project::Led::server
                                    ::Physical::state(value);
 
     // Apply the action.
-    driveLED();
+    driveLED(current, requested);
 
-    return action;
+    return value;
 }
 
 /** @brief apply action on the LED */
-void Physical::driveLED()
+void Physical::driveLED(Action current, Action request)
 {
-    // Replace with actual code.
-    std::cout << " Drive LED  STUB :: \n";
+    if (current == request)
+    {
+        // Best we can do here is ignore.
+        return;
+    }
+
+    // Transition TO Blinking state
+    if (request == Action::Blink)
+    {
+        return blinkOperation();
+    }
+
+    // Transition TO Stable states.
+    if(request == Action::On || request == Action::Off)
+    {
+        return stableStateOperation(request);
+    }
+    return;
+}
+
+/** @brief Either TurnON -or- TurnOFF */
+void Physical::stableStateOperation(Action action)
+{
+    auto value = (action == Action::On) ? ASSERT : DEASSERT;
+
+    // Write "none" to trigger to clear any previous action
+    write(blinkCtrl, "none");
+
+    // And write the current command
+    write(brightCtrl, value);
+    return;
+}
+
+/** @brief BLINK the LED */
+void Physical::blinkOperation()
+{
+    // Get the latest dutyOn that the user requested
+    auto dutyOn = this->dutyOn();
+
+    // Write "timer" to "trigger" file
+    write(blinkCtrl, "timer");
+
+    // Write DutyON. Value in percentage 1_millisecond.
+    // so 50% input becomes 500. Driver wants string input
+    auto percentage = frequency / 100;
+    write(delayOnCtrl, std::to_string(dutyOn * percentage));
+
+    // Write DutyOFF. Value in milli seconds so 50% input becomes 500.
+    write(delayOffCtrl, std::to_string((100 - dutyOn) * percentage));
     return;
 }