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 // apply the led action to the map 18 static void applyGroupAction(std::map<LedName, Layout::LedAction>& newState, 19 Layout::LedAction action) 20 { 21 if (!newState.contains(action.name)) 22 { 23 newState[action.name] = action; 24 return; 25 } 26 27 auto currentAction = newState[action.name]; 28 29 if (currentAction.action == action.priority) 30 { 31 // if the current action is already the priority action, 32 // we cannot override it 33 return; 34 } 35 36 newState[action.name] = action; 37 } 38 39 // create the resulting new map from all currently asserted groups 40 static auto getNewMapWithGroupPriorities( 41 std::set<const Layout::GroupLayout*, Layout::CompareGroupLayout> sorted) 42 -> std::map<LedName, Layout::LedAction> 43 { 44 std::map<LedName, Layout::LedAction> newState; 45 46 // update the new map with the desired state 47 for (const auto it : sorted) 48 { 49 // apply all led actions of that group to the map 50 for (Layout::LedAction action : it->actionSet) 51 { 52 newState[action.name] = action; 53 } 54 } 55 return newState; 56 } 57 58 static std::map<LedName, Layout::LedAction> getNewMapWithLEDPriorities( 59 std::set<const Layout::GroupLayout*> assertedGroups) 60 { 61 std::map<LedName, Layout::LedAction> newState; 62 // update the new map with the desired state 63 for (const Layout::GroupLayout* it : assertedGroups) 64 { 65 // apply all led actions of that group to the map 66 for (Layout::LedAction action : it->actionSet) 67 { 68 applyGroupAction(newState, action); 69 } 70 } 71 return newState; 72 } 73 74 // create the resulting new map from all currently asserted groups 75 std::map<LedName, Layout::LedAction> 76 Manager::getNewMap(std::set<const Layout::GroupLayout*> assertedGroups) 77 { 78 std::map<LedName, Layout::LedAction> newState; 79 80 std::set<const Layout::GroupLayout*, Layout::CompareGroupLayout> sorted; 81 82 bool groupPriorities = false; 83 84 for (const Layout::GroupLayout* it : assertedGroups) 85 { 86 sorted.insert(it); 87 88 if (it->priority != 0) 89 { 90 groupPriorities = true; 91 } 92 } 93 94 if (groupPriorities) 95 { 96 newState = getNewMapWithGroupPriorities(sorted); 97 } 98 else 99 { 100 newState = getNewMapWithLEDPriorities(assertedGroups); 101 } 102 103 return newState; 104 } 105 106 // Assert -or- De-assert 107 bool Manager::setGroupState(const std::string& path, bool assert, 108 ActionSet& ledsAssert, ActionSet& ledsDeAssert) 109 { 110 if (assert) 111 { 112 assertedGroups.insert(&ledMap.at(path)); 113 } 114 else 115 { 116 if (assertedGroups.contains(&ledMap.at(path))) 117 { 118 assertedGroups.erase(&ledMap.at(path)); 119 } 120 } 121 122 // create the new map from the asserted groups 123 auto newState = getNewMap(assertedGroups); 124 125 // the ledsAssert are those that are in the new map and change state 126 // + those in the new map and not in the old map 127 for (const auto& [name, action] : newState) 128 { 129 if (ledStateMap.contains(name)) 130 { 131 // check if the led action has changed 132 auto& currentAction = ledStateMap[name]; 133 134 if (currentAction.action == action.action) 135 continue; 136 } 137 138 ledsAssert.insert(action); 139 } 140 141 // the ledsDeAssert are those in the old map but not in the new map 142 for (const auto& [name, action] : ledStateMap) 143 { 144 if (!newState.contains(name)) 145 { 146 ledsDeAssert.insert(action); 147 } 148 } 149 150 ledStateMap = newState; 151 152 // If we survive, then set the state accordingly. 153 return assert; 154 } 155 156 void Manager::setLampTestCallBack( 157 std::function<bool(ActionSet& ledsAssert, ActionSet& ledsDeAssert)> 158 callBack) 159 { 160 lampTestCallBack = callBack; 161 } 162 163 /** @brief Run through the map and apply action on the LEDs */ 164 void Manager::driveLEDs(ActionSet& ledsAssert, ActionSet& ledsDeAssert) 165 { 166 #ifdef USE_LAMP_TEST 167 // Use the lampTestCallBack method and trigger the callback method in the 168 // lamp test(processLEDUpdates), in this way, all lamp test operations 169 // are performed in the lamp test class. 170 if (lampTestCallBack(ledsAssert, ledsDeAssert)) 171 { 172 return; 173 } 174 #endif 175 ActionSet newReqChangedLeds; 176 std::vector<std::pair<ActionSet&, ActionSet&>> actionsVec = { 177 {reqLedsAssert, ledsAssert}, {reqLedsDeAssert, ledsDeAssert}}; 178 179 timer.setEnabled(false); 180 std::set_union(ledsAssert.begin(), ledsAssert.end(), ledsDeAssert.begin(), 181 ledsDeAssert.end(), 182 std::inserter(newReqChangedLeds, newReqChangedLeds.begin()), 183 ledLess); 184 185 // prepare reqLedsAssert & reqLedsDeAssert 186 for (auto pair : actionsVec) 187 { 188 ActionSet tmpSet; 189 190 // Discard current required LED actions, if these LEDs have new actions 191 // in newReqChangedLeds. 192 std::set_difference(pair.first.begin(), pair.first.end(), 193 newReqChangedLeds.begin(), newReqChangedLeds.end(), 194 std::inserter(tmpSet, tmpSet.begin()), ledLess); 195 196 // Union the remaining LED actions with new LED actions. 197 pair.first.clear(); 198 std::set_union(tmpSet.begin(), tmpSet.end(), pair.second.begin(), 199 pair.second.end(), 200 std::inserter(pair.first, pair.first.begin()), ledLess); 201 } 202 203 driveLedsHandler(); 204 return; 205 } 206 207 // Calls into driving physical LED post choosing the action 208 int Manager::drivePhysicalLED(const std::string& objPath, Layout::Action action, 209 uint8_t dutyOn, const uint16_t period) 210 { 211 try 212 { 213 // If Blink, set its property 214 if (action == Layout::Action::Blink) 215 { 216 PropertyValue dutyOnValue{dutyOn}; 217 PropertyValue periodValue{period}; 218 219 dBusHandler.setProperty(objPath, phyLedIntf, "DutyOn", dutyOnValue); 220 dBusHandler.setProperty(objPath, phyLedIntf, "Period", periodValue); 221 } 222 223 PropertyValue actionValue{getPhysicalAction(action)}; 224 dBusHandler.setProperty(objPath, phyLedIntf, "State", actionValue); 225 } 226 catch (const sdbusplus::exception_t& e) 227 { 228 lg2::error( 229 "Error setting property for physical LED, ERROR = {ERROR}, OBJECT_PATH = {PATH}", 230 "ERROR", e, "PATH", objPath); 231 return -1; 232 } 233 234 return 0; 235 } 236 237 /** @brief Returns action string based on enum */ 238 std::string Manager::getPhysicalAction(Layout::Action action) 239 { 240 namespace server = sdbusplus::xyz::openbmc_project::Led::server; 241 242 // TODO: openbmc/phosphor-led-manager#5 243 // Somehow need to use the generated Action enum than giving one 244 // in ledlayout. 245 if (action == Layout::Action::On) 246 { 247 return server::convertForMessage(server::Physical::Action::On); 248 } 249 else if (action == Layout::Action::Blink) 250 { 251 return server::convertForMessage(server::Physical::Action::Blink); 252 } 253 else 254 { 255 return server::convertForMessage(server::Physical::Action::Off); 256 } 257 } 258 259 void Manager::driveLedsHandler(void) 260 { 261 ActionSet failedLedsAssert; 262 ActionSet failedLedsDeAssert; 263 264 // This order of LED operation is important. 265 for (const auto& it : reqLedsDeAssert) 266 { 267 std::string objPath = std::string(phyLedPath) + it.name; 268 lg2::debug("De-Asserting LED, NAME = {NAME}, ACTION = {ACTION}", "NAME", 269 it.name, "ACTION", it.action); 270 if (drivePhysicalLED(objPath, Layout::Action::Off, it.dutyOn, 271 it.period)) 272 { 273 failedLedsDeAssert.insert(it); 274 } 275 } 276 277 for (const auto& it : reqLedsAssert) 278 { 279 std::string objPath = std::string(phyLedPath) + it.name; 280 lg2::debug("Asserting LED, NAME = {NAME}, ACTION = {ACTION}", "NAME", 281 it.name, "ACTION", it.action); 282 if (drivePhysicalLED(objPath, it.action, it.dutyOn, it.period)) 283 { 284 failedLedsAssert.insert(it); 285 } 286 } 287 288 reqLedsAssert = failedLedsAssert; 289 reqLedsDeAssert = failedLedsDeAssert; 290 291 if (reqLedsDeAssert.empty() && reqLedsAssert.empty()) 292 { 293 timer.setEnabled(false); 294 } 295 else 296 { 297 timer.restartOnce(std::chrono::seconds(1)); 298 } 299 300 return; 301 } 302 303 } // namespace led 304 } // namespace phosphor 305