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