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 // Assert -or- De-assert
setGroupState(const std::string & path,bool assert,ActionSet & ledsAssert,ActionSet & ledsDeAssert)18 bool Manager::setGroupState(const std::string& path, bool assert,
19 ActionSet& ledsAssert, ActionSet& ledsDeAssert)
20 {
21 if (assert)
22 {
23 assertedGroups.insert(&ledMap.at(path));
24 }
25 else
26 {
27 if (assertedGroups.contains(&ledMap.at(path)))
28 {
29 assertedGroups.erase(&ledMap.at(path));
30 }
31 }
32
33 // This will contain the union of what's already in the asserted ActionSet
34 ActionSet desiredState{};
35 for (const auto& grp : assertedGroups)
36 {
37 desiredState.insert(grp->cbegin(), grp->cend());
38 }
39
40 // Find difference between Combined and Desired to identify
41 // which LEDs are getting altered
42 ActionSet transient{};
43 std::set_difference(combinedState.begin(), combinedState.end(),
44 desiredState.begin(), desiredState.end(),
45 std::inserter(transient, transient.begin()), ledComp);
46 if (transient.size())
47 {
48 // Find common LEDs between transient and Desired to know if some LEDs
49 // are changing state and not really getting DeAsserted
50 ActionSet ledsTransient{};
51 std::set_intersection(
52 transient.begin(), transient.end(), desiredState.begin(),
53 desiredState.end(),
54 std::inserter(ledsTransient, ledsTransient.begin()), ledLess);
55
56 // Find difference between above 2 to identify those LEDs which are
57 // really getting DeAsserted
58 std::set_difference(transient.begin(), transient.end(),
59 ledsTransient.begin(), ledsTransient.end(),
60 std::inserter(ledsDeAssert, ledsDeAssert.begin()),
61 ledLess);
62
63 // Remove the elements from Current that are being DeAsserted.
64 if (ledsDeAssert.size())
65 {
66 // Power off LEDs that are to be really DeAsserted
67 for (auto& it : ledsDeAssert)
68 {
69 // Update LEDs in "physically asserted" set by removing those
70 // LEDs which are De-Asserted
71 auto found = currentState.find(it);
72 if (found != currentState.end())
73 {
74 currentState.erase(found);
75 }
76 }
77 }
78 }
79
80 // Now LEDs that are to be Asserted. These could either be fresh asserts
81 // -or- change between [On]<-->[Blink]
82 ActionSet temp{};
83 std::unique_copy(desiredState.begin(), desiredState.end(),
84 std::inserter(temp, temp.begin()), ledEqual);
85 if (temp.size())
86 {
87 // Find difference between [desired to be Asserted] and those LEDs
88 // that are physically asserted currently.
89 std::set_difference(
90 temp.begin(), temp.end(), currentState.begin(), currentState.end(),
91 std::inserter(ledsAssert, ledsAssert.begin()), ledComp);
92 }
93
94 // Update the current actual and desired(the virtual actual)
95 currentState = std::move(temp);
96 combinedState = std::move(desiredState);
97
98 // If we survive, then set the state accordingly.
99 return assert;
100 }
101
setLampTestCallBack(std::function<bool (ActionSet & ledsAssert,ActionSet & ledsDeAssert)> callBack)102 void Manager::setLampTestCallBack(
103 std::function<bool(ActionSet& ledsAssert, ActionSet& ledsDeAssert)>
104 callBack)
105 {
106 lampTestCallBack = callBack;
107 }
108
109 /** @brief Run through the map and apply action on the LEDs */
driveLEDs(ActionSet & ledsAssert,ActionSet & ledsDeAssert)110 void Manager::driveLEDs(ActionSet& ledsAssert, ActionSet& ledsDeAssert)
111 {
112 #ifdef USE_LAMP_TEST
113 // Use the lampTestCallBack method and trigger the callback method in the
114 // lamp test(processLEDUpdates), in this way, all lamp test operations
115 // are performed in the lamp test class.
116 if (lampTestCallBack(ledsAssert, ledsDeAssert))
117 {
118 return;
119 }
120 #endif
121 ActionSet newReqChangedLeds;
122 std::vector<std::pair<ActionSet&, ActionSet&>> actionsVec = {
123 {reqLedsAssert, ledsAssert}, {reqLedsDeAssert, ledsDeAssert}};
124
125 timer.setEnabled(false);
126 std::set_union(ledsAssert.begin(), ledsAssert.end(), ledsDeAssert.begin(),
127 ledsDeAssert.end(),
128 std::inserter(newReqChangedLeds, newReqChangedLeds.begin()),
129 ledLess);
130
131 // prepare reqLedsAssert & reqLedsDeAssert
132 for (auto pair : actionsVec)
133 {
134 ActionSet tmpSet;
135
136 // Discard current required LED actions, if these LEDs have new actions
137 // in newReqChangedLeds.
138 std::set_difference(pair.first.begin(), pair.first.end(),
139 newReqChangedLeds.begin(), newReqChangedLeds.end(),
140 std::inserter(tmpSet, tmpSet.begin()), ledLess);
141
142 // Union the remaining LED actions with new LED actions.
143 pair.first.clear();
144 std::set_union(tmpSet.begin(), tmpSet.end(), pair.second.begin(),
145 pair.second.end(),
146 std::inserter(pair.first, pair.first.begin()), ledLess);
147 }
148
149 driveLedsHandler();
150 return;
151 }
152
153 // Calls into driving physical LED post choosing the action
drivePhysicalLED(const std::string & objPath,Layout::Action action,uint8_t dutyOn,const uint16_t period)154 int Manager::drivePhysicalLED(const std::string& objPath, Layout::Action action,
155 uint8_t dutyOn, const uint16_t period)
156 {
157 try
158 {
159 // If Blink, set its property
160 if (action == Layout::Action::Blink)
161 {
162 PropertyValue dutyOnValue{dutyOn};
163 PropertyValue periodValue{period};
164
165 dBusHandler.setProperty(objPath, phyLedIntf, "DutyOn", dutyOnValue);
166 dBusHandler.setProperty(objPath, phyLedIntf, "Period", periodValue);
167 }
168
169 PropertyValue actionValue{getPhysicalAction(action)};
170 dBusHandler.setProperty(objPath, phyLedIntf, "State", actionValue);
171 }
172 catch (const sdbusplus::exception_t& e)
173 {
174 lg2::error(
175 "Error setting property for physical LED, ERROR = {ERROR}, OBJECT_PATH = {PATH}",
176 "ERROR", e, "PATH", objPath);
177 return -1;
178 }
179
180 return 0;
181 }
182
183 /** @brief Returns action string based on enum */
getPhysicalAction(Layout::Action action)184 std::string Manager::getPhysicalAction(Layout::Action action)
185 {
186 namespace server = sdbusplus::xyz::openbmc_project::Led::server;
187
188 // TODO: openbmc/phosphor-led-manager#5
189 // Somehow need to use the generated Action enum than giving one
190 // in ledlayout.
191 if (action == Layout::Action::On)
192 {
193 return server::convertForMessage(server::Physical::Action::On);
194 }
195 else if (action == Layout::Action::Blink)
196 {
197 return server::convertForMessage(server::Physical::Action::Blink);
198 }
199 else
200 {
201 return server::convertForMessage(server::Physical::Action::Off);
202 }
203 }
204
driveLedsHandler(void)205 void Manager::driveLedsHandler(void)
206 {
207 ActionSet failedLedsAssert;
208 ActionSet failedLedsDeAssert;
209
210 // This order of LED operation is important.
211 if (reqLedsDeAssert.size())
212 {
213 for (const auto& it : reqLedsDeAssert)
214 {
215 std::string objPath = std::string(phyLedPath) + it.name;
216 lg2::debug("De-Asserting LED, NAME = {NAME}, ACTION = {ACTION}",
217 "NAME", it.name, "ACTION", it.action);
218 if (drivePhysicalLED(objPath, Layout::Action::Off, it.dutyOn,
219 it.period))
220 {
221 failedLedsDeAssert.insert(it);
222 }
223 }
224 }
225
226 if (reqLedsAssert.size())
227 {
228 for (const auto& it : reqLedsAssert)
229 {
230 std::string objPath = std::string(phyLedPath) + it.name;
231 lg2::debug("Asserting LED, NAME = {NAME}, ACTION = {ACTION}",
232 "NAME", it.name, "ACTION", it.action);
233 if (drivePhysicalLED(objPath, it.action, it.dutyOn, it.period))
234 {
235 failedLedsAssert.insert(it);
236 }
237 }
238 }
239
240 reqLedsAssert = failedLedsAssert;
241 reqLedsDeAssert = failedLedsDeAssert;
242
243 if (reqLedsDeAssert.empty() && reqLedsAssert.empty())
244 {
245 timer.setEnabled(false);
246 }
247 else
248 {
249 timer.restartOnce(std::chrono::seconds(1));
250 }
251
252 return;
253 }
254
255 } // namespace led
256 } // namespace phosphor
257