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 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 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 */ 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 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