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 && assert)
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     return;
58 }
59 
60 auto Physical::state() const -> Action
61 {
62     return sdbusplus::xyz::openbmc_project::Led::server::Physical::state();
63 }
64 
65 auto Physical::state(Action value) -> Action
66 {
67     auto current =
68         sdbusplus::xyz::openbmc_project::Led::server::Physical::state();
69 
70     auto requested =
71         sdbusplus::xyz::openbmc_project::Led::server::Physical::state(value);
72 
73     driveLED(current, requested);
74 
75     return value;
76 }
77 
78 void Physical::driveLED(Action current, Action request)
79 {
80     if (current == request)
81     {
82         return;
83     }
84 
85     if (request == Action::On || request == Action::Off)
86     {
87         return stableStateOperation(request);
88     }
89 
90     assert(request == Action::Blink);
91     blinkOperation();
92 }
93 
94 void Physical::stableStateOperation(Action action)
95 {
96     auto value = (action == Action::On) ? assert : DEASSERT;
97 
98     led.setTrigger("none");
99     led.setBrightness(value);
100     return;
101 }
102 
103 void Physical::blinkOperation()
104 {
105     auto dutyOn = this->dutyOn();
106 
107     /*
108       The configuration of the trigger type must precede the configuration of
109       the trigger type properties. From the kernel documentation:
110       "You can change triggers in a similar manner to the way an IO scheduler
111       is chosen (via /sys/class/leds/<device>/trigger). Trigger specific
112       parameters can appear in /sys/class/leds/<device> once a given trigger is
113       selected."
114       Refer:
115       https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/leds/leds-class.txt?h=v5.2#n26
116     */
117     led.setTrigger("timer");
118     // Convert percent duty to milliseconds for sysfs interface
119     auto factor = this->period() / 100.0;
120     led.setDelayOn(dutyOn * factor);
121     led.setDelayOff((100 - dutyOn) * factor);
122 
123     return;
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     if (!color.length())
132         return;
133     std::string tmp = color;
134     tmp[0] = toupper(tmp[0]);
135     try
136     {
137         auto palette = convertPaletteFromString(prefix + tmp);
138         setPropertyByName("Color", palette);
139     }
140     catch (const sdbusplus::exception::InvalidEnumString&)
141     {
142         // if color var contains invalid color,
143         // Color property will have default value
144     }
145 }
146 
147 } // namespace led
148 } // namespace phosphor
149