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