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