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