blob: 336f819f4737e870eca03d6e9ada64e691716de6 [file] [log] [blame]
George Liub6151622020-11-23 18:16:18 +08001#include "config.h"
2
Patrick Venture91ac8d32018-11-01 17:03:22 -07003#include "manager.hpp"
4
George Liue9fb5c62021-07-01 14:05:32 +08005#include <phosphor-logging/lg2.hpp>
William A. Kennington III151122a2018-05-15 12:00:32 -07006#include <sdbusplus/exception.hpp>
Vishwanatha Subbanna4fa92482017-03-10 14:39:20 +05307#include <xyz/openbmc_project/Led/Physical/server.hpp>
George Liua6c18f82020-06-22 10:50:04 +08008
9#include <algorithm>
Patrick Williams858e5732025-05-14 15:25:07 -040010#include <chrono>
George Liua6c18f82020-06-22 10:50:04 +080011#include <iostream>
12#include <string>
Patrick Williams858e5732025-05-14 15:25:07 -040013
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +053014namespace phosphor
15{
16namespace led
17{
18
Alexander Hansena6f9a412024-07-24 12:27:42 +020019// apply the led action to the map
20static 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
Alexander Hansen55badf72024-07-24 14:35:13 +020031 const bool hasPriority = currentAction.priority.has_value();
32
33 if (hasPriority && currentAction.action == action.priority)
Alexander Hansena6f9a412024-07-24 12:27:42 +020034 {
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
Alexander Hansen7ba70c82024-07-23 13:46:25 +020044static auto getNewMapWithGroupPriorities(
45 std::set<const Layout::GroupLayout*, Layout::CompareGroupLayout> sorted)
Alexander Hansena6f9a412024-07-24 12:27:42 +020046 -> std::map<LedName, Layout::LedAction>
47{
48 std::map<LedName, Layout::LedAction> newState;
49
50 // update the new map with the desired state
George Liu3d487512024-08-23 09:19:57 +080051 for (const auto* it : sorted)
Alexander Hansena6f9a412024-07-24 12:27:42 +020052 {
53 // apply all led actions of that group to the map
George Liu112821c2024-08-22 19:00:24 +080054 for (const Layout::LedAction& action : it->actionSet)
Alexander Hansen7ba70c82024-07-23 13:46:25 +020055 {
56 newState[action.name] = action;
57 }
58 }
59 return newState;
60}
61
62static 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
George Liu112821c2024-08-22 19:00:24 +080070 for (const Layout::LedAction& action : it->actionSet)
Alexander Hansena6f9a412024-07-24 12:27:42 +020071 {
72 applyGroupAction(newState, action);
73 }
74 }
75 return newState;
76}
77
Alexander Hansen7ba70c82024-07-23 13:46:25 +020078// create the resulting new map from all currently asserted groups
Patrick Williams275ad182025-03-03 11:19:17 -050079std::map<LedName, Layout::LedAction> Manager::getNewMap(
80 std::set<const Layout::GroupLayout*> assertedGroups)
Alexander Hansen7ba70c82024-07-23 13:46:25 +020081{
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
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +0530110// Assert -or- De-assert
Vishwanatha Subbannaed490732016-12-20 15:59:29 +0530111bool Manager::setGroupState(const std::string& path, bool assert,
Patrick Williams158b2c12022-03-17 05:57:44 -0500112 ActionSet& ledsAssert, ActionSet& ledsDeAssert)
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +0530113{
114 if (assert)
115 {
116 assertedGroups.insert(&ledMap.at(path));
117 }
118 else
119 {
George Liu7f53a032021-05-04 11:18:21 +0800120 if (assertedGroups.contains(&ledMap.at(path)))
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +0530121 {
122 assertedGroups.erase(&ledMap.at(path));
123 }
124 }
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +0530125
Alexander Hansena6f9a412024-07-24 12:27:42 +0200126 // 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)
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +0530132 {
Alexander Hansena6f9a412024-07-24 12:27:42 +0200133 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)
George Liu9e104152024-08-23 09:38:20 +0800139 {
Alexander Hansena6f9a412024-07-24 12:27:42 +0200140 continue;
George Liu9e104152024-08-23 09:38:20 +0800141 }
Alexander Hansena6f9a412024-07-24 12:27:42 +0200142 }
143
144 ledsAssert.insert(action);
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +0530145 }
146
Alexander Hansena6f9a412024-07-24 12:27:42 +0200147 // the ledsDeAssert are those in the old map but not in the new map
148 for (const auto& [name, action] : ledStateMap)
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +0530149 {
Alexander Hansena6f9a412024-07-24 12:27:42 +0200150 if (!newState.contains(name))
Vishwanatha Subbanna4b000d82017-05-03 18:44:16 +0530151 {
Alexander Hansena6f9a412024-07-24 12:27:42 +0200152 ledsDeAssert.insert(action);
Vishwanatha Subbanna4b000d82017-05-03 18:44:16 +0530153 }
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +0530154 }
155
Alexander Hansena6f9a412024-07-24 12:27:42 +0200156 ledStateMap = newState;
Vishwanatha Subbannaed490732016-12-20 15:59:29 +0530157
158 // If we survive, then set the state accordingly.
159 return assert;
160}
161
George Liub6151622020-11-23 18:16:18 +0800162void Manager::setLampTestCallBack(
Patrick Williams158b2c12022-03-17 05:57:44 -0500163 std::function<bool(ActionSet& ledsAssert, ActionSet& ledsDeAssert)>
164 callBack)
George Liub6151622020-11-23 18:16:18 +0800165{
166 lampTestCallBack = callBack;
167}
168
Vishwanatha Subbannaed490732016-12-20 15:59:29 +0530169/** @brief Run through the map and apply action on the LEDs */
Patrick Williams158b2c12022-03-17 05:57:44 -0500170void Manager::driveLEDs(ActionSet& ledsAssert, ActionSet& ledsDeAssert)
Vishwanatha Subbannaed490732016-12-20 15:59:29 +0530171{
George Liub6151622020-11-23 18:16:18 +0800172#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
Potin Laif1ed4792023-07-13 18:45:14 +0800181 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)
Vishwanatha Subbannaed490732016-12-20 15:59:29 +0530193 {
Potin Laif1ed4792023-07-13 18:45:14 +0800194 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);
Vishwanatha Subbannaed490732016-12-20 15:59:29 +0530207 }
208
Potin Laif1ed4792023-07-13 18:45:14 +0800209 driveLedsHandler();
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +0530210 return;
211}
212
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530213// Calls into driving physical LED post choosing the action
Potin Laif1ed4792023-07-13 18:45:14 +0800214int Manager::drivePhysicalLED(const std::string& objPath, Layout::Action action,
George Liu80f51bb2024-08-22 20:17:36 +0800215 uint8_t dutyOn, uint16_t period)
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530216{
George Liu1c737af2020-10-16 09:07:02 +0800217 try
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530218 {
George Liu1c737af2020-10-16 09:07:02 +0800219 // If Blink, set its property
220 if (action == Layout::Action::Blink)
221 {
222 PropertyValue dutyOnValue{dutyOn};
223 PropertyValue periodValue{period};
224
George Liuf0592552024-08-23 09:46:17 +0800225 phosphor::led::utils::DBusHandler::setProperty(
226 objPath, phyLedIntf, "DutyOn", dutyOnValue);
227 phosphor::led::utils::DBusHandler::setProperty(
228 objPath, phyLedIntf, "Period", periodValue);
George Liu1c737af2020-10-16 09:07:02 +0800229 }
230
231 PropertyValue actionValue{getPhysicalAction(action)};
George Liuf0592552024-08-23 09:46:17 +0800232 phosphor::led::utils::DBusHandler::setProperty(objPath, phyLedIntf,
233 "State", actionValue);
George Liu1c737af2020-10-16 09:07:02 +0800234 }
George Liu829c0b32023-07-18 10:00:47 +0800235 catch (const sdbusplus::exception_t& e)
George Liu1c737af2020-10-16 09:07:02 +0800236 {
Patrick Williams858e5732025-05-14 15:25:07 -0400237 // 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
George Liue9fb5c62021-07-01 14:05:32 +0800251 lg2::error(
252 "Error setting property for physical LED, ERROR = {ERROR}, OBJECT_PATH = {PATH}",
253 "ERROR", e, "PATH", objPath);
Patrick Williams858e5732025-05-14 15:25:07 -0400254 physicalLEDErrors[objPath] = now;
Potin Laif1ed4792023-07-13 18:45:14 +0800255 return -1;
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530256 }
Vishwanatha Subbannadcc3f382017-03-24 20:15:02 +0530257
Potin Laif1ed4792023-07-13 18:45:14 +0800258 return 0;
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530259}
260
261/** @brief Returns action string based on enum */
Vishwanatha Subbanna4fa92482017-03-10 14:39:20 +0530262std::string Manager::getPhysicalAction(Layout::Action action)
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530263{
Vishwanatha Subbanna4fa92482017-03-10 14:39:20 +0530264 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.
Patrick Venture91ac8d32018-11-01 17:03:22 -0700269 if (action == Layout::Action::On)
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530270 {
Vishwanatha Subbanna4fa92482017-03-10 14:39:20 +0530271 return server::convertForMessage(server::Physical::Action::On);
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530272 }
Patrick Venture91ac8d32018-11-01 17:03:22 -0700273 else if (action == Layout::Action::Blink)
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530274 {
Vishwanatha Subbanna4fa92482017-03-10 14:39:20 +0530275 return server::convertForMessage(server::Physical::Action::Blink);
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530276 }
277 else
278 {
Vishwanatha Subbanna4fa92482017-03-10 14:39:20 +0530279 return server::convertForMessage(server::Physical::Action::Off);
Vishwanatha Subbanna11ca8f92017-02-27 19:33:45 +0530280 }
281}
282
Potin Laif1ed4792023-07-13 18:45:14 +0800283void Manager::driveLedsHandler(void)
284{
285 ActionSet failedLedsAssert;
286 ActionSet failedLedsDeAssert;
287
288 // This order of LED operation is important.
Alexander Hansenfe476e12024-07-23 15:45:22 +0200289 for (const auto& it : reqLedsDeAssert)
Potin Laif1ed4792023-07-13 18:45:14 +0800290 {
Alexander Hansenfe476e12024-07-23 15:45:22 +0200291 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,
George Liu49875a22024-08-23 08:58:59 +0800295 it.period) != 0)
Potin Laif1ed4792023-07-13 18:45:14 +0800296 {
Alexander Hansenfe476e12024-07-23 15:45:22 +0200297 failedLedsDeAssert.insert(it);
Potin Laif1ed4792023-07-13 18:45:14 +0800298 }
299 }
300
Alexander Hansenfe476e12024-07-23 15:45:22 +0200301 for (const auto& it : reqLedsAssert)
Potin Laif1ed4792023-07-13 18:45:14 +0800302 {
Alexander Hansenfe476e12024-07-23 15:45:22 +0200303 std::string objPath = std::string(phyLedPath) + it.name;
304 lg2::debug("Asserting LED, NAME = {NAME}, ACTION = {ACTION}", "NAME",
305 it.name, "ACTION", it.action);
George Liu49875a22024-08-23 08:58:59 +0800306 if (drivePhysicalLED(objPath, it.action, it.dutyOn, it.period) != 0)
Potin Laif1ed4792023-07-13 18:45:14 +0800307 {
Alexander Hansenfe476e12024-07-23 15:45:22 +0200308 failedLedsAssert.insert(it);
Potin Laif1ed4792023-07-13 18:45:14 +0800309 }
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
Vishwanatha Subbanna4c8c72b2016-11-29 23:02:06 +0530327} // namespace led
328} // namespace phosphor