xref: /openbmc/phosphor-led-manager/manager/manager.cpp (revision 858e5731ff4a058bcf79094f4b948d0b36d127b7)
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 <chrono>
11 #include <iostream>
12 #include <string>
13 
14 namespace phosphor
15 {
16 namespace led
17 {
18 
19 // apply the led action to the map
applyGroupAction(std::map<LedName,Layout::LedAction> & newState,Layout::LedAction action)20 static void applyGroupAction(std::map<LedName, Layout::LedAction>& newState,
21                              Layout::LedAction action)
22 {
23     if (!newState.contains(action.name))
24     {
25         newState[action.name] = action;
26         return;
27     }
28 
29     auto currentAction = newState[action.name];
30 
31     const bool hasPriority = currentAction.priority.has_value();
32 
33     if (hasPriority && currentAction.action == action.priority)
34     {
35         // if the current action is already the priority action,
36         // we cannot override it
37         return;
38     }
39 
40     newState[action.name] = action;
41 }
42 
43 // create the resulting new map from all currently asserted groups
getNewMapWithGroupPriorities(std::set<const Layout::GroupLayout *,Layout::CompareGroupLayout> sorted)44 static auto getNewMapWithGroupPriorities(
45     std::set<const Layout::GroupLayout*, Layout::CompareGroupLayout> sorted)
46     -> std::map<LedName, Layout::LedAction>
47 {
48     std::map<LedName, Layout::LedAction> newState;
49 
50     // update the new map with the desired state
51     for (const auto* it : sorted)
52     {
53         // apply all led actions of that group to the map
54         for (const Layout::LedAction& action : it->actionSet)
55         {
56             newState[action.name] = action;
57         }
58     }
59     return newState;
60 }
61 
getNewMapWithLEDPriorities(std::set<const Layout::GroupLayout * > assertedGroups)62 static std::map<LedName, Layout::LedAction> getNewMapWithLEDPriorities(
63     std::set<const Layout::GroupLayout*> assertedGroups)
64 {
65     std::map<LedName, Layout::LedAction> newState;
66     // update the new map with the desired state
67     for (const Layout::GroupLayout* it : assertedGroups)
68     {
69         // apply all led actions of that group to the map
70         for (const Layout::LedAction& action : it->actionSet)
71         {
72             applyGroupAction(newState, action);
73         }
74     }
75     return newState;
76 }
77 
78 // create the resulting new map from all currently asserted groups
getNewMap(std::set<const Layout::GroupLayout * > assertedGroups)79 std::map<LedName, Layout::LedAction> Manager::getNewMap(
80     std::set<const Layout::GroupLayout*> assertedGroups)
81 {
82     std::map<LedName, Layout::LedAction> newState;
83 
84     std::set<const Layout::GroupLayout*, Layout::CompareGroupLayout> sorted;
85 
86     bool groupPriorities = false;
87 
88     for (const Layout::GroupLayout* it : assertedGroups)
89     {
90         sorted.insert(it);
91 
92         if (it->priority != 0)
93         {
94             groupPriorities = true;
95         }
96     }
97 
98     if (groupPriorities)
99     {
100         newState = getNewMapWithGroupPriorities(sorted);
101     }
102     else
103     {
104         newState = getNewMapWithLEDPriorities(assertedGroups);
105     }
106 
107     return newState;
108 }
109 
110 // Assert -or- De-assert
setGroupState(const std::string & path,bool assert,ActionSet & ledsAssert,ActionSet & ledsDeAssert)111 bool Manager::setGroupState(const std::string& path, bool assert,
112                             ActionSet& ledsAssert, ActionSet& ledsDeAssert)
113 {
114     if (assert)
115     {
116         assertedGroups.insert(&ledMap.at(path));
117     }
118     else
119     {
120         if (assertedGroups.contains(&ledMap.at(path)))
121         {
122             assertedGroups.erase(&ledMap.at(path));
123         }
124     }
125 
126     // create the new map from the asserted groups
127     auto newState = getNewMap(assertedGroups);
128 
129     // the ledsAssert are those that are in the new map and change state
130     // + those in the new map and not in the old map
131     for (const auto& [name, action] : newState)
132     {
133         if (ledStateMap.contains(name))
134         {
135             // check if the led action has changed
136             auto& currentAction = ledStateMap[name];
137 
138             if (currentAction.action == action.action)
139             {
140                 continue;
141             }
142         }
143 
144         ledsAssert.insert(action);
145     }
146 
147     // the ledsDeAssert are those in the old map but not in the new map
148     for (const auto& [name, action] : ledStateMap)
149     {
150         if (!newState.contains(name))
151         {
152             ledsDeAssert.insert(action);
153         }
154     }
155 
156     ledStateMap = newState;
157 
158     // If we survive, then set the state accordingly.
159     return assert;
160 }
161 
setLampTestCallBack(std::function<bool (ActionSet & ledsAssert,ActionSet & ledsDeAssert)> callBack)162 void Manager::setLampTestCallBack(
163     std::function<bool(ActionSet& ledsAssert, ActionSet& ledsDeAssert)>
164         callBack)
165 {
166     lampTestCallBack = callBack;
167 }
168 
169 /** @brief Run through the map and apply action on the LEDs */
driveLEDs(ActionSet & ledsAssert,ActionSet & ledsDeAssert)170 void Manager::driveLEDs(ActionSet& ledsAssert, ActionSet& ledsDeAssert)
171 {
172 #ifdef USE_LAMP_TEST
173     // Use the lampTestCallBack method and trigger the callback method in the
174     // lamp test(processLEDUpdates), in this way, all lamp test operations
175     // are performed in the lamp test class.
176     if (lampTestCallBack(ledsAssert, ledsDeAssert))
177     {
178         return;
179     }
180 #endif
181     ActionSet newReqChangedLeds;
182     std::vector<std::pair<ActionSet&, ActionSet&>> actionsVec = {
183         {reqLedsAssert, ledsAssert}, {reqLedsDeAssert, ledsDeAssert}};
184 
185     timer.setEnabled(false);
186     std::set_union(ledsAssert.begin(), ledsAssert.end(), ledsDeAssert.begin(),
187                    ledsDeAssert.end(),
188                    std::inserter(newReqChangedLeds, newReqChangedLeds.begin()),
189                    ledLess);
190 
191     // prepare reqLedsAssert & reqLedsDeAssert
192     for (auto pair : actionsVec)
193     {
194         ActionSet tmpSet;
195 
196         // Discard current required LED actions, if these LEDs have new actions
197         // in newReqChangedLeds.
198         std::set_difference(pair.first.begin(), pair.first.end(),
199                             newReqChangedLeds.begin(), newReqChangedLeds.end(),
200                             std::inserter(tmpSet, tmpSet.begin()), ledLess);
201 
202         // Union the remaining LED actions with new LED actions.
203         pair.first.clear();
204         std::set_union(tmpSet.begin(), tmpSet.end(), pair.second.begin(),
205                        pair.second.end(),
206                        std::inserter(pair.first, pair.first.begin()), ledLess);
207     }
208 
209     driveLedsHandler();
210     return;
211 }
212 
213 // Calls into driving physical LED post choosing the action
drivePhysicalLED(const std::string & objPath,Layout::Action action,uint8_t dutyOn,uint16_t period)214 int Manager::drivePhysicalLED(const std::string& objPath, Layout::Action action,
215                               uint8_t dutyOn, uint16_t period)
216 {
217     try
218     {
219         // If Blink, set its property
220         if (action == Layout::Action::Blink)
221         {
222             PropertyValue dutyOnValue{dutyOn};
223             PropertyValue periodValue{period};
224 
225             phosphor::led::utils::DBusHandler::setProperty(
226                 objPath, phyLedIntf, "DutyOn", dutyOnValue);
227             phosphor::led::utils::DBusHandler::setProperty(
228                 objPath, phyLedIntf, "Period", periodValue);
229         }
230 
231         PropertyValue actionValue{getPhysicalAction(action)};
232         phosphor::led::utils::DBusHandler::setProperty(objPath, phyLedIntf,
233                                                        "State", actionValue);
234     }
235     catch (const sdbusplus::exception_t& e)
236     {
237         // This can be a really spammy event log, so we rate limit it to once an
238         // hour per LED.
239         auto now = std::chrono::steady_clock::now();
240 
241         if (auto it = physicalLEDErrors.find(objPath);
242             it != physicalLEDErrors.end())
243         {
244             using namespace std::literals::chrono_literals;
245             if ((now - it->second) < 1h)
246             {
247                 return -1;
248             }
249         }
250 
251         lg2::error(
252             "Error setting property for physical LED, ERROR = {ERROR}, OBJECT_PATH = {PATH}",
253             "ERROR", e, "PATH", objPath);
254         physicalLEDErrors[objPath] = now;
255         return -1;
256     }
257 
258     return 0;
259 }
260 
261 /** @brief Returns action string based on enum */
getPhysicalAction(Layout::Action action)262 std::string Manager::getPhysicalAction(Layout::Action action)
263 {
264     namespace server = sdbusplus::xyz::openbmc_project::Led::server;
265 
266     // TODO: openbmc/phosphor-led-manager#5
267     //    Somehow need to use the generated Action enum than giving one
268     //    in ledlayout.
269     if (action == Layout::Action::On)
270     {
271         return server::convertForMessage(server::Physical::Action::On);
272     }
273     else if (action == Layout::Action::Blink)
274     {
275         return server::convertForMessage(server::Physical::Action::Blink);
276     }
277     else
278     {
279         return server::convertForMessage(server::Physical::Action::Off);
280     }
281 }
282 
driveLedsHandler(void)283 void Manager::driveLedsHandler(void)
284 {
285     ActionSet failedLedsAssert;
286     ActionSet failedLedsDeAssert;
287 
288     // This order of LED operation is important.
289     for (const auto& it : reqLedsDeAssert)
290     {
291         std::string objPath = std::string(phyLedPath) + it.name;
292         lg2::debug("De-Asserting LED, NAME = {NAME}, ACTION = {ACTION}", "NAME",
293                    it.name, "ACTION", it.action);
294         if (drivePhysicalLED(objPath, Layout::Action::Off, it.dutyOn,
295                              it.period) != 0)
296         {
297             failedLedsDeAssert.insert(it);
298         }
299     }
300 
301     for (const auto& it : reqLedsAssert)
302     {
303         std::string objPath = std::string(phyLedPath) + it.name;
304         lg2::debug("Asserting LED, NAME = {NAME}, ACTION = {ACTION}", "NAME",
305                    it.name, "ACTION", it.action);
306         if (drivePhysicalLED(objPath, it.action, it.dutyOn, it.period) != 0)
307         {
308             failedLedsAssert.insert(it);
309         }
310     }
311 
312     reqLedsAssert = failedLedsAssert;
313     reqLedsDeAssert = failedLedsDeAssert;
314 
315     if (reqLedsDeAssert.empty() && reqLedsAssert.empty())
316     {
317         timer.setEnabled(false);
318     }
319     else
320     {
321         timer.restartOnce(std::chrono::seconds(1));
322     }
323 
324     return;
325 }
326 
327 } // namespace led
328 } // namespace phosphor
329