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