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