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