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     }
42     else
43     {
44         // Cache current LED state
45         auto brightness = led.getBrightness();
46         if (brightness != 0U && assert != 0U)
47         {
48             sdbusplus::xyz::openbmc_project::Led::server::Physical::state(
49                 Action::On);
50         }
51         else
52         {
53             sdbusplus::xyz::openbmc_project::Led::server::Physical::state(
54                 Action::Off);
55         }
56     }
57 }
58 
59 auto Physical::state() const -> Action
60 {
61     return sdbusplus::xyz::openbmc_project::Led::server::Physical::state();
62 }
63 
64 auto Physical::state(Action value) -> Action
65 {
66     auto current =
67         sdbusplus::xyz::openbmc_project::Led::server::Physical::state();
68 
69     auto requested =
70         sdbusplus::xyz::openbmc_project::Led::server::Physical::state(value);
71 
72     driveLED(current, requested);
73 
74     return value;
75 }
76 
77 void Physical::driveLED(Action current, Action request)
78 {
79     if (current == request)
80     {
81         return;
82     }
83 
84     if (request == Action::On || request == Action::Off)
85     {
86         return stableStateOperation(request);
87     }
88 
89     assert(request == Action::Blink);
90     blinkOperation();
91 }
92 
93 void Physical::stableStateOperation(Action action)
94 {
95     auto value = (action == Action::On) ? assert : deasserted;
96 
97     led.setTrigger("none");
98     led.setBrightness(value);
99 }
100 
101 void Physical::blinkOperation()
102 {
103     /*
104       The configuration of the trigger type must precede the configuration of
105       the trigger type properties. From the kernel documentation:
106       "You can change triggers in a similar manner to the way an IO scheduler
107       is chosen (via /sys/class/leds/<device>/trigger). Trigger specific
108       parameters can appear in /sys/class/leds/<device> once a given trigger is
109       selected."
110       Refer:
111       https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/leds/leds-class.txt?h=v5.2#n26
112     */
113     auto d = static_cast<unsigned long>(dutyOn());
114     if (d > 100)
115     {
116         d = 100;
117     }
118 
119     auto p = static_cast<unsigned long>(period());
120 
121     led.setTrigger("timer");
122     led.setDelayOn(p * d / 100UL);
123     led.setDelayOff(p * (100UL - d) / 100UL);
124 }
125 
126 /** @brief set led color property in DBus*/
127 void Physical::setLedColor(const std::string& color)
128 {
129     static const std::string prefix =
130         "xyz.openbmc_project.Led.Physical.Palette.";
131 
132     if (color.empty())
133     {
134         return;
135     }
136 
137     std::string tmp = color;
138     tmp[0] = static_cast<char>(toupper(tmp[0]));
139     try
140     {
141         auto palette = convertPaletteFromString(prefix + tmp);
142         setPropertyByName("Color", palette);
143     }
144     catch (const sdbusplus::exception::InvalidEnumString&)
145     {
146         // if color var contains invalid color,
147         // Color property will have default value
148     }
149 }
150 
151 } // namespace led
152 } // namespace phosphor
153