blob: 8178f75770e051477f6ee71e2c37d187973dab91 [file] [log] [blame]
George Liuc777bef2020-11-23 17:04:21 +08001#include "lamptest.hpp"
2
George Liue9fb5c62021-07-01 14:05:32 +08003#include <phosphor-logging/lg2.hpp>
George Liu87fd11c2020-11-23 16:40:14 +08004
George Liuc5e0f312021-12-27 15:54:31 +08005#include <algorithm>
6
George Liuc777bef2020-11-23 17:04:21 +08007namespace phosphor
8{
9namespace led
10{
11
George Liu82150322021-03-03 17:13:13 +080012using Json = nlohmann::json;
Sunny Srivastavae3515c72022-10-15 12:45:40 -050013static const fs::path lampTestIndicator =
14 "/var/lib/phosphor-led-manager/lamp-test-running";
George Liub6151622020-11-23 18:16:18 +080015
Patrick Williams158b2c12022-03-17 05:57:44 -050016bool LampTest::processLEDUpdates(const ActionSet& ledsAssert,
17 const ActionSet& ledsDeAssert)
George Liub6151622020-11-23 18:16:18 +080018{
19 // If the physical LED status is updated during the lamp test, it should be
20 // saved to Queue, and the queue will be processed after the lamp test is
21 // stopped.
22 if (isLampTestRunning)
23 {
George Liu82150322021-03-03 17:13:13 +080024 // Physical LEDs will be updated during lamp test
25 for (const auto& it : ledsDeAssert)
26 {
George Liu1f0b7152023-07-18 09:24:34 +080027 std::string path = std::string(phyLedPath) + it.name;
George Liu82150322021-03-03 17:13:13 +080028 auto iter = std::find_if(
29 forceUpdateLEDs.begin(), forceUpdateLEDs.end(),
30 [&path](const auto& name) { return name == path; });
31
32 if (iter != forceUpdateLEDs.end())
33 {
Patrick Williams858e5732025-05-14 15:25:07 -040034 manager.drivePhysicalLED(path, Layout::Action::Off, it.dutyOn,
35 it.period);
George Liu82150322021-03-03 17:13:13 +080036 }
37 }
38
39 for (const auto& it : ledsAssert)
40 {
George Liu1f0b7152023-07-18 09:24:34 +080041 std::string path = std::string(phyLedPath) + it.name;
George Liu82150322021-03-03 17:13:13 +080042 auto iter = std::find_if(
43 forceUpdateLEDs.begin(), forceUpdateLEDs.end(),
44 [&path](const auto& name) { return name == path; });
45
46 if (iter != forceUpdateLEDs.end())
47 {
Patrick Williams858e5732025-05-14 15:25:07 -040048 manager.drivePhysicalLED(path, it.action, it.dutyOn, it.period);
George Liu82150322021-03-03 17:13:13 +080049 }
50 }
51
George Liufcf08102024-08-22 17:00:05 +080052 updatedLEDsDuringLampTest.emplace(ledsAssert, ledsDeAssert);
George Liub6151622020-11-23 18:16:18 +080053 return true;
54 }
55 return false;
56}
57
George Liuc777bef2020-11-23 17:04:21 +080058void LampTest::stop()
59{
George Liub6151622020-11-23 18:16:18 +080060 if (!isLampTestRunning)
61 {
62 return;
63 }
64
George Liuc777bef2020-11-23 17:04:21 +080065 timer.setEnabled(false);
George Liu87fd11c2020-11-23 16:40:14 +080066
George Liuce4d1c52021-01-25 11:32:37 +080067 // Stop host lamp test
68 doHostLampTest(false);
69
George Liu87fd11c2020-11-23 16:40:14 +080070 // Set all the Physical action to Off
71 for (const auto& path : physicalLEDPaths)
72 {
George Liu82150322021-03-03 17:13:13 +080073 auto iter =
74 std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
75 [&path](const auto& skip) { return skip == path; });
76
77 if (iter != skipUpdateLEDs.end())
78 {
79 // Skip update physical path
80 continue;
81 }
82
Patrick Williams858e5732025-05-14 15:25:07 -040083 manager.drivePhysicalLED(path, Layout::Action::Off, 0, 0);
George Liu87fd11c2020-11-23 16:40:14 +080084 }
George Liub6151622020-11-23 18:16:18 +080085
Sunny Srivastavae3515c72022-10-15 12:45:40 -050086 if (std::filesystem::exists(lampTestIndicator))
87 {
88 if (!std::filesystem::remove(lampTestIndicator))
89 {
90 lg2::error(
91 "Error removing lamp test on indicator file after lamp test execution.");
92 }
93 }
94
George Liub6151622020-11-23 18:16:18 +080095 isLampTestRunning = false;
96 restorePhysicalLedStates();
97}
98
99Layout::Action LampTest::getActionFromString(const std::string& str)
100{
Patrick Williamsed80e882022-03-17 05:03:51 -0500101 Layout::Action action = Layout::Action::Off;
George Liub6151622020-11-23 18:16:18 +0800102
103 if (str == "xyz.openbmc_project.Led.Physical.Action.On")
104 {
Patrick Williamsed80e882022-03-17 05:03:51 -0500105 action = Layout::Action::On;
George Liub6151622020-11-23 18:16:18 +0800106 }
107 else if (str == "xyz.openbmc_project.Led.Physical.Action.Blink")
108 {
Patrick Williamsed80e882022-03-17 05:03:51 -0500109 action = Layout::Action::Blink;
George Liub6151622020-11-23 18:16:18 +0800110 }
111
112 return action;
113}
114
115void LampTest::storePhysicalLEDsStates()
116{
117 physicalLEDStatesPriorToLampTest.clear();
118
119 for (const auto& path : physicalLEDPaths)
120 {
Patrick Williams605600e2023-10-20 11:18:04 -0500121 auto iter = std::find_if(
122 skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
123 [&path](const auto& skipLed) { return skipLed == path; });
George Liu82150322021-03-03 17:13:13 +0800124
125 if (iter != skipUpdateLEDs.end())
126 {
127 // Physical LEDs will be skipped
128 continue;
129 }
130
George Liub6151622020-11-23 18:16:18 +0800131 // Reverse intercept path, Get the name of each member of physical led
132 // e.g: path = /xyz/openbmc_project/led/physical/front_fan
133 // name = front_fan
134 sdbusplus::message::object_path object_path(path);
135 auto name = object_path.filename();
136 if (name.empty())
137 {
George Liue9fb5c62021-07-01 14:05:32 +0800138 lg2::error(
139 "Failed to get the name of member of physical LED path, PATH = {PATH}, NAME = {NAME}",
140 "PATH", path, "NAME", name);
George Liub6151622020-11-23 18:16:18 +0800141 continue;
142 }
143
144 std::string state{};
145 uint16_t period{};
146 uint8_t dutyOn{};
147 try
148 {
George Liuf0592552024-08-23 09:46:17 +0800149 auto properties =
150 phosphor::led::utils::DBusHandler::getAllProperties(path,
151 phyLedIntf);
George Liub6151622020-11-23 18:16:18 +0800152
153 state = std::get<std::string>(properties["State"]);
154 period = std::get<uint16_t>(properties["Period"]);
155 dutyOn = std::get<uint8_t>(properties["DutyOn"]);
156 }
Patrick Williams3e073ba2022-07-22 19:26:52 -0500157 catch (const sdbusplus::exception_t& e)
George Liub6151622020-11-23 18:16:18 +0800158 {
George Liue9fb5c62021-07-01 14:05:32 +0800159 lg2::error(
160 "Failed to get All properties, ERROR = {ERROR}, PATH = {PATH}",
161 "ERROR", e, "PATH", path);
George Liub6151622020-11-23 18:16:18 +0800162 continue;
163 }
164
165 phosphor::led::Layout::Action action = getActionFromString(state);
Patrick Williamsed80e882022-03-17 05:03:51 -0500166 if (action != phosphor::led::Layout::Action::Off)
George Liub6151622020-11-23 18:16:18 +0800167 {
168 phosphor::led::Layout::LedAction ledAction{
Patrick Williamsed80e882022-03-17 05:03:51 -0500169 name, action, dutyOn, period,
170 phosphor::led::Layout::Action::On};
George Liub6151622020-11-23 18:16:18 +0800171 physicalLEDStatesPriorToLampTest.emplace(ledAction);
172 }
173 }
George Liuc777bef2020-11-23 17:04:21 +0800174}
175
176void LampTest::start()
177{
George Liub6151622020-11-23 18:16:18 +0800178 if (isLampTestRunning)
179 {
180 // reset the timer and then return
181 timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530182
183 // Notify host to reset the timer
184 doHostLampTest(true);
185
George Liub6151622020-11-23 18:16:18 +0800186 return;
187 }
188
George Liu87fd11c2020-11-23 16:40:14 +0800189 // Get paths of all the Physical LED objects
George Liu785f5052023-07-18 09:38:43 +0800190 try
191 {
George Liuf0592552024-08-23 09:46:17 +0800192 physicalLEDPaths = phosphor::led::utils::DBusHandler::getSubTreePaths(
193 phyLedPath, phyLedIntf);
George Liu785f5052023-07-18 09:38:43 +0800194 }
195 catch (const sdbusplus::exception_t& e)
196 {
197 lg2::error(
198 "Failed to call the SubTreePaths method: {ERROR}, ledPath: {PATH}, ledInterface: {INTERFACE}",
George Liu1f0b7152023-07-18 09:24:34 +0800199 "ERROR", e, "PATH", phyLedPath, "INTERFACE", phyLedIntf);
George Liu785f5052023-07-18 09:38:43 +0800200 return;
201 }
George Liu87fd11c2020-11-23 16:40:14 +0800202
George Liub6151622020-11-23 18:16:18 +0800203 // Get physical LEDs states before lamp test
204 storePhysicalLEDsStates();
205
George Liuc777bef2020-11-23 17:04:21 +0800206 // restart lamp test, it contains initiate or reset the timer.
207 timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
George Liub6151622020-11-23 18:16:18 +0800208 isLampTestRunning = true;
George Liu87fd11c2020-11-23 16:40:14 +0800209
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530210 // Notify host to start the lamp test
George Liuce4d1c52021-01-25 11:32:37 +0800211 doHostLampTest(true);
212
Sunny Srivastavae3515c72022-10-15 12:45:40 -0500213 // Create a file to maintain the state across reboots that Lamp test is on.
214 // This is required as there was a scenario where it has been found that
215 // LEDs remains in "on" state if lamp test is triggered and reboot takes
216 // place.
217 const auto ledDirectory = lampTestIndicator.parent_path();
218
219 if (!fs::exists(ledDirectory))
220 {
221 fs::create_directories(ledDirectory);
222 }
223
George Liu8f538d92024-08-22 15:41:22 +0800224 std::ofstream ofs(lampTestIndicator.c_str());
Sunny Srivastavae3515c72022-10-15 12:45:40 -0500225
George Liu87fd11c2020-11-23 16:40:14 +0800226 // Set all the Physical action to On for lamp test
227 for (const auto& path : physicalLEDPaths)
228 {
George Liu82150322021-03-03 17:13:13 +0800229 auto iter =
230 std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
231 [&path](const auto& skip) { return skip == path; });
232
233 if (iter != skipUpdateLEDs.end())
234 {
235 // Skip update physical path
236 continue;
237 }
238
Patrick Williams858e5732025-05-14 15:25:07 -0400239 manager.drivePhysicalLED(path, Layout::Action::On, 0, 0);
George Liu87fd11c2020-11-23 16:40:14 +0800240 }
George Liuc777bef2020-11-23 17:04:21 +0800241}
242
243void LampTest::timeOutHandler()
244{
245 // set the Asserted property of lamp test to false
George Liu49875a22024-08-23 08:58:59 +0800246 if (groupObj == nullptr)
George Liu87fd11c2020-11-23 16:40:14 +0800247 {
George Liue9fb5c62021-07-01 14:05:32 +0800248 lg2::error("the Group object is nullptr");
George Liu87fd11c2020-11-23 16:40:14 +0800249 throw std::runtime_error("the Group object is nullptr");
250 }
251
252 groupObj->asserted(false);
George Liuc777bef2020-11-23 17:04:21 +0800253}
254
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530255bool LampTest::requestHandler(Group* group, bool value)
George Liuc777bef2020-11-23 17:04:21 +0800256{
George Liuaaa667f2024-08-22 17:37:51 +0800257 if (groupObj == nullptr)
George Liu87fd11c2020-11-23 16:40:14 +0800258 {
George Liudf3ab7c2024-08-22 19:05:27 +0800259 groupObj = group;
George Liu87fd11c2020-11-23 16:40:14 +0800260 }
261
George Liuc777bef2020-11-23 17:04:21 +0800262 if (value)
263 {
264 start();
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530265
266 // Return true in both cases (F -> T && T -> T)
267 return true;
George Liuc777bef2020-11-23 17:04:21 +0800268 }
269 else
270 {
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530271 if (timer.hasExpired())
272 {
273 stop();
274
275 // Return true as the request to stop the lamptest is handled
276 // successfully.
277 return true;
278 }
279 else if (timer.isEnabled())
280 {
281 lg2::info(
282 "Lamp test is still running. Cannot force stop the lamp test. Asserted is set back to true.");
283
284 // Return false as the request to stop lamptest is not handled as
285 // the lamptest is still running.
286 return false;
287 }
288 return false;
George Liuc777bef2020-11-23 17:04:21 +0800289 }
290}
291
George Liub6151622020-11-23 18:16:18 +0800292void LampTest::restorePhysicalLedStates()
293{
294 // restore physical LEDs states before lamp test
Patrick Williams158b2c12022-03-17 05:57:44 -0500295 ActionSet ledsDeAssert{};
George Liu82150322021-03-03 17:13:13 +0800296 manager.driveLEDs(physicalLEDStatesPriorToLampTest, ledsDeAssert);
George Liub6151622020-11-23 18:16:18 +0800297 physicalLEDStatesPriorToLampTest.clear();
298
299 // restore physical LEDs states during lamp test
300 while (!updatedLEDsDuringLampTest.empty())
301 {
302 auto& [ledsAssert, ledsDeAssert] = updatedLEDsDuringLampTest.front();
303 manager.driveLEDs(ledsAssert, ledsDeAssert);
304 updatedLEDsDuringLampTest.pop();
305 }
306}
307
George Liuce4d1c52021-01-25 11:32:37 +0800308void LampTest::doHostLampTest(bool value)
309{
310 try
311 {
312 PropertyValue assertedValue{value};
George Liuf0592552024-08-23 09:46:17 +0800313 phosphor::led::utils::DBusHandler::setProperty(
314 HOST_LAMP_TEST_OBJECT, "xyz.openbmc_project.Led.Group", "Asserted",
315 assertedValue);
George Liuce4d1c52021-01-25 11:32:37 +0800316 }
Patrick Williams3e073ba2022-07-22 19:26:52 -0500317 catch (const sdbusplus::exception_t& e)
George Liuce4d1c52021-01-25 11:32:37 +0800318 {
George Liue9fb5c62021-07-01 14:05:32 +0800319 lg2::error(
320 "Failed to set Asserted property, ERROR = {ERROR}, PATH = {PATH}",
321 "ERROR", e, "PATH", std::string(HOST_LAMP_TEST_OBJECT));
George Liuce4d1c52021-01-25 11:32:37 +0800322 }
323}
324
George Liu82150322021-03-03 17:13:13 +0800325void LampTest::getPhysicalLEDNamesFromJson(const fs::path& path)
326{
327 if (!fs::exists(path) || fs::is_empty(path))
328 {
George Liue9fb5c62021-07-01 14:05:32 +0800329 lg2::info("The file does not exist or is empty, FILE_PATH = {PATH}",
330 "PATH", path);
George Liu82150322021-03-03 17:13:13 +0800331 return;
332 }
333
334 try
335 {
336 std::ifstream jsonFile(path);
337 auto json = Json::parse(jsonFile);
338
339 // define the default JSON as empty
340 const std::vector<std::string> empty{};
341 auto forceLEDs = json.value("forceLEDs", empty);
George Liuc5e0f312021-12-27 15:54:31 +0800342 std::ranges::transform(forceLEDs, std::back_inserter(forceUpdateLEDs),
George Liu1f0b7152023-07-18 09:24:34 +0800343 [](const auto& i) { return phyLedPath + i; });
George Liu82150322021-03-03 17:13:13 +0800344
345 auto skipLEDs = json.value("skipLEDs", empty);
George Liuc5e0f312021-12-27 15:54:31 +0800346 std::ranges::transform(skipLEDs, std::back_inserter(skipUpdateLEDs),
George Liu1f0b7152023-07-18 09:24:34 +0800347 [](const auto& i) { return phyLedPath + i; });
George Liu82150322021-03-03 17:13:13 +0800348 }
349 catch (const std::exception& e)
350 {
George Liue9fb5c62021-07-01 14:05:32 +0800351 lg2::error(
352 "Failed to parse config file, ERROR = {ERROR}, FILE_PATH = {PATH}",
353 "ERROR", e, "PATH", path);
George Liu82150322021-03-03 17:13:13 +0800354 }
355 return;
356}
Sunny Srivastavae3515c72022-10-15 12:45:40 -0500357
358void LampTest::clearLamps()
359{
360 if (std::filesystem::exists(lampTestIndicator))
361 {
362 // we need to off all the LEDs.
George Liuf0592552024-08-23 09:46:17 +0800363 std::vector<std::string> physicalLedPaths =
364 phosphor::led::utils::DBusHandler::getSubTreePaths(
365 phosphor::led::phyLedPath, phosphor::led::phyLedIntf);
Sunny Srivastavae3515c72022-10-15 12:45:40 -0500366
367 for (const auto& path : physicalLedPaths)
368 {
Patrick Williams858e5732025-05-14 15:25:07 -0400369 manager.drivePhysicalLED(path, phosphor::led::Layout::Action::Off,
370 0, 0);
Sunny Srivastavae3515c72022-10-15 12:45:40 -0500371 }
372
373 // Also remove the lamp test on indicator file.
374 if (!std::filesystem::remove(lampTestIndicator))
375 {
376 lg2::error(
377 "Error removing lamp test on indicator file after lamp test execution.");
378 }
379 }
380}
George Liuc777bef2020-11-23 17:04:21 +0800381} // namespace led
382} // namespace phosphor