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         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 
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 */
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     // This order of LED operation is important.
122     if (ledsDeAssert.size())
123     {
124         for (const auto& it : ledsDeAssert)
125         {
126             std::string objPath = std::string(PHY_LED_PATH) + it.name;
127             lg2::debug("De-Asserting LED, NAME = {NAME}", "NAME", it.name);
128             drivePhysicalLED(objPath, Layout::Action::Off, it.dutyOn,
129                              it.period);
130         }
131     }
132 
133     if (ledsAssert.size())
134     {
135         for (const auto& it : ledsAssert)
136         {
137             std::string objPath = std::string(PHY_LED_PATH) + it.name;
138             lg2::debug("Asserting LED, NAME = {NAME}", "NAME", it.name);
139             drivePhysicalLED(objPath, it.action, it.dutyOn, it.period);
140         }
141     }
142     return;
143 }
144 
145 // Calls into driving physical LED post choosing the action
146 void Manager::drivePhysicalLED(const std::string& objPath,
147                                Layout::Action action, uint8_t dutyOn,
148                                const uint16_t period)
149 {
150     try
151     {
152         // If Blink, set its property
153         if (action == Layout::Action::Blink)
154         {
155             PropertyValue dutyOnValue{dutyOn};
156             PropertyValue periodValue{period};
157 
158             dBusHandler.setProperty(objPath, PHY_LED_IFACE, "DutyOn",
159                                     dutyOnValue);
160             dBusHandler.setProperty(objPath, PHY_LED_IFACE, "Period",
161                                     periodValue);
162         }
163 
164         PropertyValue actionValue{getPhysicalAction(action)};
165         dBusHandler.setProperty(objPath, PHY_LED_IFACE, "State", actionValue);
166     }
167     catch (const std::exception& e)
168     {
169         lg2::error(
170             "Error setting property for physical LED, ERROR = {ERROR}, OBJECT_PATH = {PATH}",
171             "ERROR", e, "PATH", objPath);
172     }
173 
174     return;
175 }
176 
177 /** @brief Returns action string based on enum */
178 std::string Manager::getPhysicalAction(Layout::Action action)
179 {
180     namespace server = sdbusplus::xyz::openbmc_project::Led::server;
181 
182     // TODO: openbmc/phosphor-led-manager#5
183     //    Somehow need to use the generated Action enum than giving one
184     //    in ledlayout.
185     if (action == Layout::Action::On)
186     {
187         return server::convertForMessage(server::Physical::Action::On);
188     }
189     else if (action == Layout::Action::Blink)
190     {
191         return server::convertForMessage(server::Physical::Action::Blink);
192     }
193     else
194     {
195         return server::convertForMessage(server::Physical::Action::Off);
196     }
197 }
198 
199 } // namespace led
200 } // namespace phosphor
201