1 /**
2  * Copyright © 2016,2018 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "physical.hpp"
18 
19 #include <cassert>
20 #include <cstdlib>
21 #include <iostream>
22 #include <string>
23 namespace phosphor
24 {
25 namespace led
26 {
27 
28 /** @brief Populates key parameters */
29 void Physical::setInitialState()
30 {
31     assert = led->getMaxBrightness();
32     auto trigger = led->getTrigger();
33     if (trigger == "timer")
34     {
35         // LED is blinking. Get the on and off delays and derive percent duty
36         auto delayOn = led->getDelayOn();
37         uint16_t periodMs = delayOn + led->getDelayOff();
38         auto percentScale = periodMs / 100;
39         this->dutyOn(delayOn / percentScale);
40         this->period(periodMs);
41         sdbusplus::xyz::openbmc_project::Led::server::Physical::state(
42             Action::Blink);
43     }
44     else
45     {
46         // Cache current LED state
47         auto brightness = led->getBrightness();
48         if (brightness != 0U && assert != 0U)
49         {
50             sdbusplus::xyz::openbmc_project::Led::server::Physical::state(
51                 Action::On);
52         }
53         else
54         {
55             sdbusplus::xyz::openbmc_project::Led::server::Physical::state(
56                 Action::Off);
57         }
58     }
59 }
60 
61 auto Physical::state() const -> Action
62 {
63     return sdbusplus::xyz::openbmc_project::Led::server::Physical::state();
64 }
65 
66 auto Physical::state(Action value) -> Action
67 {
68     auto current =
69         sdbusplus::xyz::openbmc_project::Led::server::Physical::state();
70 
71     auto requested =
72         sdbusplus::xyz::openbmc_project::Led::server::Physical::state(value);
73 
74     driveLED(current, requested);
75 
76     return value;
77 }
78 
79 void Physical::driveLED(Action current, Action request)
80 {
81     if (current == request)
82     {
83         return;
84     }
85 
86     if (request == Action::On || request == Action::Off)
87     {
88         return stableStateOperation(request);
89     }
90 
91     assert(request == Action::Blink);
92     blinkOperation();
93 }
94 
95 void Physical::stableStateOperation(Action action)
96 {
97     auto value = (action == Action::On) ? assert : deasserted;
98 
99     led->setTrigger("none");
100     led->setBrightness(value);
101 }
102 
103 void Physical::blinkOperation()
104 {
105     /*
106       The configuration of the trigger type must precede the configuration of
107       the trigger type properties. From the kernel documentation:
108       "You can change triggers in a similar manner to the way an IO scheduler
109       is chosen (via /sys/class/leds/<device>/trigger). Trigger specific
110       parameters can appear in /sys/class/leds/<device> once a given trigger is
111       selected."
112       Refer:
113       https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/leds/leds-class.txt?h=v5.2#n26
114     */
115     auto d = static_cast<unsigned long>(dutyOn());
116     if (d > 100)
117     {
118         d = 100;
119     }
120 
121     auto p = static_cast<unsigned long>(period());
122 
123     led->setTrigger("timer");
124     led->setDelayOn(p * d / 100UL);
125     led->setDelayOff(p * (100UL - d) / 100UL);
126 }
127 
128 /** @brief set led color property in DBus*/
129 void Physical::setLedColor(const std::string& color)
130 {
131     static const std::string prefix =
132         "xyz.openbmc_project.Led.Physical.Palette.";
133 
134     if (color.empty())
135     {
136         return;
137     }
138 
139     std::string tmp = color;
140     tmp[0] = static_cast<char>(toupper(tmp[0]));
141     try
142     {
143         auto palette = convertPaletteFromString(prefix + tmp);
144         setPropertyByName("Color", palette);
145     }
146     catch (const sdbusplus::exception::InvalidEnumString&)
147     {
148         // if color var contains invalid color,
149         // Color property will have default value
150     }
151 }
152 
153 } // namespace led
154 } // namespace phosphor
155