1 #include "config.h"
2 
3 #include "manager.hpp"
4 
5 #include <phosphor-logging/lg2.hpp>
6 #include <sdbusplus/exception.hpp>
7 #include <xyz/openbmc_project/Led/Physical/server.hpp>
8 
9 #include <algorithm>
10 #include <iostream>
11 #include <string>
12 namespace phosphor
13 {
14 namespace led
15 {
16 
17 // Assert -or- De-assert
setGroupState(const std::string & path,bool assert,ActionSet & ledsAssert,ActionSet & ledsDeAssert)18 bool Manager::setGroupState(const std::string& path, bool assert,
19                             ActionSet& ledsAssert, ActionSet& ledsDeAssert)
20 {
21     if (assert)
22     {
23         assertedGroups.insert(&ledMap.at(path));
24     }
25     else
26     {
27         if (assertedGroups.contains(&ledMap.at(path)))
28         {
29             assertedGroups.erase(&ledMap.at(path));
30         }
31     }
32 
33     // This will contain the union of what's already in the asserted ActionSet
34     ActionSet desiredState{};
35     for (const auto& grp : assertedGroups)
36     {
37         desiredState.insert(grp->cbegin(), grp->cend());
38     }
39 
40     // Find difference between Combined and Desired to identify
41     // which LEDs are getting altered
42     ActionSet transient{};
43     std::set_difference(combinedState.begin(), combinedState.end(),
44                         desiredState.begin(), desiredState.end(),
45                         std::inserter(transient, transient.begin()), ledComp);
46     if (transient.size())
47     {
48         // Find common LEDs between transient and Desired to know if some LEDs
49         // are changing state and not really getting DeAsserted
50         ActionSet ledsTransient{};
51         std::set_intersection(
52             transient.begin(), transient.end(), desiredState.begin(),
53             desiredState.end(),
54             std::inserter(ledsTransient, ledsTransient.begin()), ledLess);
55 
56         // Find difference between above 2 to identify those LEDs which are
57         // really getting DeAsserted
58         std::set_difference(transient.begin(), transient.end(),
59                             ledsTransient.begin(), ledsTransient.end(),
60                             std::inserter(ledsDeAssert, ledsDeAssert.begin()),
61                             ledLess);
62 
63         // Remove the elements from Current that are being DeAsserted.
64         if (ledsDeAssert.size())
65         {
66             // Power off LEDs that are to be really DeAsserted
67             for (auto& it : ledsDeAssert)
68             {
69                 // Update LEDs in "physically asserted" set by removing those
70                 // LEDs which are De-Asserted
71                 auto found = currentState.find(it);
72                 if (found != currentState.end())
73                 {
74                     currentState.erase(found);
75                 }
76             }
77         }
78     }
79 
80     // Now LEDs that are to be Asserted. These could either be fresh asserts
81     // -or- change between [On]<-->[Blink]
82     ActionSet temp{};
83     std::unique_copy(desiredState.begin(), desiredState.end(),
84                      std::inserter(temp, temp.begin()), ledEqual);
85     if (temp.size())
86     {
87         // Find difference between [desired to be Asserted] and those LEDs
88         // that are physically asserted currently.
89         std::set_difference(
90             temp.begin(), temp.end(), currentState.begin(), currentState.end(),
91             std::inserter(ledsAssert, ledsAssert.begin()), ledComp);
92     }
93 
94     // Update the current actual and desired(the virtual actual)
95     currentState = std::move(temp);
96     combinedState = std::move(desiredState);
97 
98     // If we survive, then set the state accordingly.
99     return assert;
100 }
101 
setLampTestCallBack(std::function<bool (ActionSet & ledsAssert,ActionSet & ledsDeAssert)> callBack)102 void Manager::setLampTestCallBack(
103     std::function<bool(ActionSet& ledsAssert, ActionSet& ledsDeAssert)>
104         callBack)
105 {
106     lampTestCallBack = callBack;
107 }
108 
109 /** @brief Run through the map and apply action on the LEDs */
driveLEDs(ActionSet & ledsAssert,ActionSet & ledsDeAssert)110 void Manager::driveLEDs(ActionSet& ledsAssert, ActionSet& ledsDeAssert)
111 {
112 #ifdef USE_LAMP_TEST
113     // Use the lampTestCallBack method and trigger the callback method in the
114     // lamp test(processLEDUpdates), in this way, all lamp test operations
115     // are performed in the lamp test class.
116     if (lampTestCallBack(ledsAssert, ledsDeAssert))
117     {
118         return;
119     }
120 #endif
121     ActionSet newReqChangedLeds;
122     std::vector<std::pair<ActionSet&, ActionSet&>> actionsVec = {
123         {reqLedsAssert, ledsAssert}, {reqLedsDeAssert, ledsDeAssert}};
124 
125     timer.setEnabled(false);
126     std::set_union(ledsAssert.begin(), ledsAssert.end(), ledsDeAssert.begin(),
127                    ledsDeAssert.end(),
128                    std::inserter(newReqChangedLeds, newReqChangedLeds.begin()),
129                    ledLess);
130 
131     // prepare reqLedsAssert & reqLedsDeAssert
132     for (auto pair : actionsVec)
133     {
134         ActionSet tmpSet;
135 
136         // Discard current required LED actions, if these LEDs have new actions
137         // in newReqChangedLeds.
138         std::set_difference(pair.first.begin(), pair.first.end(),
139                             newReqChangedLeds.begin(), newReqChangedLeds.end(),
140                             std::inserter(tmpSet, tmpSet.begin()), ledLess);
141 
142         // Union the remaining LED actions with new LED actions.
143         pair.first.clear();
144         std::set_union(tmpSet.begin(), tmpSet.end(), pair.second.begin(),
145                        pair.second.end(),
146                        std::inserter(pair.first, pair.first.begin()), ledLess);
147     }
148 
149     driveLedsHandler();
150     return;
151 }
152 
153 // Calls into driving physical LED post choosing the action
drivePhysicalLED(const std::string & objPath,Layout::Action action,uint8_t dutyOn,const uint16_t period)154 int Manager::drivePhysicalLED(const std::string& objPath, Layout::Action action,
155                               uint8_t dutyOn, const uint16_t period)
156 {
157     try
158     {
159         // If Blink, set its property
160         if (action == Layout::Action::Blink)
161         {
162             PropertyValue dutyOnValue{dutyOn};
163             PropertyValue periodValue{period};
164 
165             dBusHandler.setProperty(objPath, phyLedIntf, "DutyOn", dutyOnValue);
166             dBusHandler.setProperty(objPath, phyLedIntf, "Period", periodValue);
167         }
168 
169         PropertyValue actionValue{getPhysicalAction(action)};
170         dBusHandler.setProperty(objPath, phyLedIntf, "State", actionValue);
171     }
172     catch (const sdbusplus::exception_t& e)
173     {
174         lg2::error(
175             "Error setting property for physical LED, ERROR = {ERROR}, OBJECT_PATH = {PATH}",
176             "ERROR", e, "PATH", objPath);
177         return -1;
178     }
179 
180     return 0;
181 }
182 
183 /** @brief Returns action string based on enum */
getPhysicalAction(Layout::Action action)184 std::string Manager::getPhysicalAction(Layout::Action action)
185 {
186     namespace server = sdbusplus::xyz::openbmc_project::Led::server;
187 
188     // TODO: openbmc/phosphor-led-manager#5
189     //    Somehow need to use the generated Action enum than giving one
190     //    in ledlayout.
191     if (action == Layout::Action::On)
192     {
193         return server::convertForMessage(server::Physical::Action::On);
194     }
195     else if (action == Layout::Action::Blink)
196     {
197         return server::convertForMessage(server::Physical::Action::Blink);
198     }
199     else
200     {
201         return server::convertForMessage(server::Physical::Action::Off);
202     }
203 }
204 
driveLedsHandler(void)205 void Manager::driveLedsHandler(void)
206 {
207     ActionSet failedLedsAssert;
208     ActionSet failedLedsDeAssert;
209 
210     // This order of LED operation is important.
211     if (reqLedsDeAssert.size())
212     {
213         for (const auto& it : reqLedsDeAssert)
214         {
215             std::string objPath = std::string(phyLedPath) + it.name;
216             lg2::debug("De-Asserting LED, NAME = {NAME}, ACTION = {ACTION}",
217                        "NAME", it.name, "ACTION", it.action);
218             if (drivePhysicalLED(objPath, Layout::Action::Off, it.dutyOn,
219                                  it.period))
220             {
221                 failedLedsDeAssert.insert(it);
222             }
223         }
224     }
225 
226     if (reqLedsAssert.size())
227     {
228         for (const auto& it : reqLedsAssert)
229         {
230             std::string objPath = std::string(phyLedPath) + it.name;
231             lg2::debug("Asserting LED, NAME = {NAME}, ACTION = {ACTION}",
232                        "NAME", it.name, "ACTION", it.action);
233             if (drivePhysicalLED(objPath, it.action, it.dutyOn, it.period))
234             {
235                 failedLedsAssert.insert(it);
236             }
237         }
238     }
239 
240     reqLedsAssert = failedLedsAssert;
241     reqLedsDeAssert = failedLedsDeAssert;
242 
243     if (reqLedsDeAssert.empty() && reqLedsAssert.empty())
244     {
245         timer.setEnabled(false);
246     }
247     else
248     {
249         timer.restartOnce(std::chrono::seconds(1));
250     }
251 
252     return;
253 }
254 
255 } // namespace led
256 } // namespace phosphor
257