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