blob: b306e51eab4c9b955bb1c62eb5f18df8eaa4dc47 [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 {
34 manager.drivePhysicalLED(path, Layout::Action::Off, it.dutyOn,
35 it.period);
36 }
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 {
48 manager.drivePhysicalLED(path, it.action, it.dutyOn, it.period);
49 }
50 }
51
George Liub6151622020-11-23 18:16:18 +080052 updatedLEDsDuringLampTest.emplace(
53 std::make_pair(ledsAssert, ledsDeAssert));
54 return true;
55 }
56 return false;
57}
58
George Liuc777bef2020-11-23 17:04:21 +080059void LampTest::stop()
60{
George Liub6151622020-11-23 18:16:18 +080061 if (!isLampTestRunning)
62 {
63 return;
64 }
65
George Liuc777bef2020-11-23 17:04:21 +080066 timer.setEnabled(false);
George Liu87fd11c2020-11-23 16:40:14 +080067
George Liuce4d1c52021-01-25 11:32:37 +080068 // Stop host lamp test
69 doHostLampTest(false);
70
George Liu87fd11c2020-11-23 16:40:14 +080071 // Set all the Physical action to Off
72 for (const auto& path : physicalLEDPaths)
73 {
George Liu82150322021-03-03 17:13:13 +080074 auto iter =
75 std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
76 [&path](const auto& skip) { return skip == path; });
77
78 if (iter != skipUpdateLEDs.end())
79 {
80 // Skip update physical path
81 continue;
82 }
83
George Liu87fd11c2020-11-23 16:40:14 +080084 manager.drivePhysicalLED(path, Layout::Action::Off, 0, 0);
85 }
George Liub6151622020-11-23 18:16:18 +080086
Sunny Srivastavae3515c72022-10-15 12:45:40 -050087 if (std::filesystem::exists(lampTestIndicator))
88 {
89 if (!std::filesystem::remove(lampTestIndicator))
90 {
91 lg2::error(
92 "Error removing lamp test on indicator file after lamp test execution.");
93 }
94 }
95
George Liub6151622020-11-23 18:16:18 +080096 isLampTestRunning = false;
97 restorePhysicalLedStates();
98}
99
100Layout::Action LampTest::getActionFromString(const std::string& str)
101{
Patrick Williamsed80e882022-03-17 05:03:51 -0500102 Layout::Action action = Layout::Action::Off;
George Liub6151622020-11-23 18:16:18 +0800103
104 if (str == "xyz.openbmc_project.Led.Physical.Action.On")
105 {
Patrick Williamsed80e882022-03-17 05:03:51 -0500106 action = Layout::Action::On;
George Liub6151622020-11-23 18:16:18 +0800107 }
108 else if (str == "xyz.openbmc_project.Led.Physical.Action.Blink")
109 {
Patrick Williamsed80e882022-03-17 05:03:51 -0500110 action = Layout::Action::Blink;
George Liub6151622020-11-23 18:16:18 +0800111 }
112
113 return action;
114}
115
116void LampTest::storePhysicalLEDsStates()
117{
118 physicalLEDStatesPriorToLampTest.clear();
119
120 for (const auto& path : physicalLEDPaths)
121 {
Patrick Williams605600e2023-10-20 11:18:04 -0500122 auto iter = std::find_if(
123 skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
124 [&path](const auto& skipLed) { return skipLed == path; });
George Liu82150322021-03-03 17:13:13 +0800125
126 if (iter != skipUpdateLEDs.end())
127 {
128 // Physical LEDs will be skipped
129 continue;
130 }
131
George Liub6151622020-11-23 18:16:18 +0800132 // Reverse intercept path, Get the name of each member of physical led
133 // e.g: path = /xyz/openbmc_project/led/physical/front_fan
134 // name = front_fan
135 sdbusplus::message::object_path object_path(path);
136 auto name = object_path.filename();
137 if (name.empty())
138 {
George Liue9fb5c62021-07-01 14:05:32 +0800139 lg2::error(
140 "Failed to get the name of member of physical LED path, PATH = {PATH}, NAME = {NAME}",
141 "PATH", path, "NAME", name);
George Liub6151622020-11-23 18:16:18 +0800142 continue;
143 }
144
145 std::string state{};
146 uint16_t period{};
147 uint8_t dutyOn{};
148 try
149 {
George Liu1f0b7152023-07-18 09:24:34 +0800150 auto properties = dBusHandler.getAllProperties(path, phyLedIntf);
George Liub6151622020-11-23 18:16:18 +0800151
152 state = std::get<std::string>(properties["State"]);
153 period = std::get<uint16_t>(properties["Period"]);
154 dutyOn = std::get<uint8_t>(properties["DutyOn"]);
155 }
Patrick Williams3e073ba2022-07-22 19:26:52 -0500156 catch (const sdbusplus::exception_t& e)
George Liub6151622020-11-23 18:16:18 +0800157 {
George Liue9fb5c62021-07-01 14:05:32 +0800158 lg2::error(
159 "Failed to get All properties, ERROR = {ERROR}, PATH = {PATH}",
160 "ERROR", e, "PATH", path);
George Liub6151622020-11-23 18:16:18 +0800161 continue;
162 }
163
164 phosphor::led::Layout::Action action = getActionFromString(state);
Patrick Williamsed80e882022-03-17 05:03:51 -0500165 if (action != phosphor::led::Layout::Action::Off)
George Liub6151622020-11-23 18:16:18 +0800166 {
167 phosphor::led::Layout::LedAction ledAction{
Patrick Williamsed80e882022-03-17 05:03:51 -0500168 name, action, dutyOn, period,
169 phosphor::led::Layout::Action::On};
George Liub6151622020-11-23 18:16:18 +0800170 physicalLEDStatesPriorToLampTest.emplace(ledAction);
171 }
172 }
George Liuc777bef2020-11-23 17:04:21 +0800173}
174
175void LampTest::start()
176{
George Liub6151622020-11-23 18:16:18 +0800177 if (isLampTestRunning)
178 {
179 // reset the timer and then return
180 timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530181
182 // Notify host to reset the timer
183 doHostLampTest(true);
184
George Liub6151622020-11-23 18:16:18 +0800185 return;
186 }
187
George Liu87fd11c2020-11-23 16:40:14 +0800188 // Get paths of all the Physical LED objects
George Liu785f5052023-07-18 09:38:43 +0800189 try
190 {
George Liu1f0b7152023-07-18 09:24:34 +0800191 physicalLEDPaths = dBusHandler.getSubTreePaths(phyLedPath, phyLedIntf);
George Liu785f5052023-07-18 09:38:43 +0800192 }
193 catch (const sdbusplus::exception_t& e)
194 {
195 lg2::error(
196 "Failed to call the SubTreePaths method: {ERROR}, ledPath: {PATH}, ledInterface: {INTERFACE}",
George Liu1f0b7152023-07-18 09:24:34 +0800197 "ERROR", e, "PATH", phyLedPath, "INTERFACE", phyLedIntf);
George Liu785f5052023-07-18 09:38:43 +0800198 return;
199 }
George Liu87fd11c2020-11-23 16:40:14 +0800200
George Liub6151622020-11-23 18:16:18 +0800201 // Get physical LEDs states before lamp test
202 storePhysicalLEDsStates();
203
George Liuc777bef2020-11-23 17:04:21 +0800204 // restart lamp test, it contains initiate or reset the timer.
205 timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
George Liub6151622020-11-23 18:16:18 +0800206 isLampTestRunning = true;
George Liu87fd11c2020-11-23 16:40:14 +0800207
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530208 // Notify host to start the lamp test
George Liuce4d1c52021-01-25 11:32:37 +0800209 doHostLampTest(true);
210
Sunny Srivastavae3515c72022-10-15 12:45:40 -0500211 // Create a file to maintain the state across reboots that Lamp test is on.
212 // This is required as there was a scenario where it has been found that
213 // LEDs remains in "on" state if lamp test is triggered and reboot takes
214 // place.
215 const auto ledDirectory = lampTestIndicator.parent_path();
216
217 if (!fs::exists(ledDirectory))
218 {
219 fs::create_directories(ledDirectory);
220 }
221
222 std::ofstream(lampTestIndicator.c_str());
223
George Liu87fd11c2020-11-23 16:40:14 +0800224 // Set all the Physical action to On for lamp test
225 for (const auto& path : physicalLEDPaths)
226 {
George Liu82150322021-03-03 17:13:13 +0800227 auto iter =
228 std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
229 [&path](const auto& skip) { return skip == path; });
230
231 if (iter != skipUpdateLEDs.end())
232 {
233 // Skip update physical path
234 continue;
235 }
236
George Liu87fd11c2020-11-23 16:40:14 +0800237 manager.drivePhysicalLED(path, Layout::Action::On, 0, 0);
238 }
George Liuc777bef2020-11-23 17:04:21 +0800239}
240
241void LampTest::timeOutHandler()
242{
243 // set the Asserted property of lamp test to false
George Liu87fd11c2020-11-23 16:40:14 +0800244 if (!groupObj)
245 {
George Liue9fb5c62021-07-01 14:05:32 +0800246 lg2::error("the Group object is nullptr");
George Liu87fd11c2020-11-23 16:40:14 +0800247 throw std::runtime_error("the Group object is nullptr");
248 }
249
250 groupObj->asserted(false);
George Liuc777bef2020-11-23 17:04:21 +0800251}
252
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530253bool LampTest::requestHandler(Group* group, bool value)
George Liuc777bef2020-11-23 17:04:21 +0800254{
George Liu87fd11c2020-11-23 16:40:14 +0800255 if (groupObj == NULL)
256 {
257 groupObj = std::move(group);
258 }
259
George Liuc777bef2020-11-23 17:04:21 +0800260 if (value)
261 {
262 start();
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530263
264 // Return true in both cases (F -> T && T -> T)
265 return true;
George Liuc777bef2020-11-23 17:04:21 +0800266 }
267 else
268 {
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530269 if (timer.hasExpired())
270 {
271 stop();
272
273 // Return true as the request to stop the lamptest is handled
274 // successfully.
275 return true;
276 }
277 else if (timer.isEnabled())
278 {
279 lg2::info(
280 "Lamp test is still running. Cannot force stop the lamp test. Asserted is set back to true.");
281
282 // Return false as the request to stop lamptest is not handled as
283 // the lamptest is still running.
284 return false;
285 }
286 return false;
George Liuc777bef2020-11-23 17:04:21 +0800287 }
288}
289
George Liub6151622020-11-23 18:16:18 +0800290void LampTest::restorePhysicalLedStates()
291{
292 // restore physical LEDs states before lamp test
Patrick Williams158b2c12022-03-17 05:57:44 -0500293 ActionSet ledsDeAssert{};
George Liu82150322021-03-03 17:13:13 +0800294 manager.driveLEDs(physicalLEDStatesPriorToLampTest, ledsDeAssert);
George Liub6151622020-11-23 18:16:18 +0800295 physicalLEDStatesPriorToLampTest.clear();
296
297 // restore physical LEDs states during lamp test
298 while (!updatedLEDsDuringLampTest.empty())
299 {
300 auto& [ledsAssert, ledsDeAssert] = updatedLEDsDuringLampTest.front();
301 manager.driveLEDs(ledsAssert, ledsDeAssert);
302 updatedLEDsDuringLampTest.pop();
303 }
304}
305
George Liuce4d1c52021-01-25 11:32:37 +0800306void LampTest::doHostLampTest(bool value)
307{
308 try
309 {
310 PropertyValue assertedValue{value};
311 dBusHandler.setProperty(HOST_LAMP_TEST_OBJECT,
312 "xyz.openbmc_project.Led.Group", "Asserted",
313 assertedValue);
314 }
Patrick Williams3e073ba2022-07-22 19:26:52 -0500315 catch (const sdbusplus::exception_t& e)
George Liuce4d1c52021-01-25 11:32:37 +0800316 {
George Liue9fb5c62021-07-01 14:05:32 +0800317 lg2::error(
318 "Failed to set Asserted property, ERROR = {ERROR}, PATH = {PATH}",
319 "ERROR", e, "PATH", std::string(HOST_LAMP_TEST_OBJECT));
George Liuce4d1c52021-01-25 11:32:37 +0800320 }
321}
322
George Liu82150322021-03-03 17:13:13 +0800323void LampTest::getPhysicalLEDNamesFromJson(const fs::path& path)
324{
325 if (!fs::exists(path) || fs::is_empty(path))
326 {
George Liue9fb5c62021-07-01 14:05:32 +0800327 lg2::info("The file does not exist or is empty, FILE_PATH = {PATH}",
328 "PATH", path);
George Liu82150322021-03-03 17:13:13 +0800329 return;
330 }
331
332 try
333 {
334 std::ifstream jsonFile(path);
335 auto json = Json::parse(jsonFile);
336
337 // define the default JSON as empty
338 const std::vector<std::string> empty{};
339 auto forceLEDs = json.value("forceLEDs", empty);
George Liuc5e0f312021-12-27 15:54:31 +0800340 std::ranges::transform(forceLEDs, std::back_inserter(forceUpdateLEDs),
George Liu1f0b7152023-07-18 09:24:34 +0800341 [](const auto& i) { return phyLedPath + i; });
George Liu82150322021-03-03 17:13:13 +0800342
343 auto skipLEDs = json.value("skipLEDs", empty);
George Liuc5e0f312021-12-27 15:54:31 +0800344 std::ranges::transform(skipLEDs, std::back_inserter(skipUpdateLEDs),
George Liu1f0b7152023-07-18 09:24:34 +0800345 [](const auto& i) { return phyLedPath + i; });
George Liu82150322021-03-03 17:13:13 +0800346 }
347 catch (const std::exception& e)
348 {
George Liue9fb5c62021-07-01 14:05:32 +0800349 lg2::error(
350 "Failed to parse config file, ERROR = {ERROR}, FILE_PATH = {PATH}",
351 "ERROR", e, "PATH", path);
George Liu82150322021-03-03 17:13:13 +0800352 }
353 return;
354}
Sunny Srivastavae3515c72022-10-15 12:45:40 -0500355
356void LampTest::clearLamps()
357{
358 if (std::filesystem::exists(lampTestIndicator))
359 {
360 // we need to off all the LEDs.
361 phosphor::led::utils::DBusHandler dBusHandler;
362 std::vector<std::string> physicalLedPaths = dBusHandler.getSubTreePaths(
363 phosphor::led::phyLedPath, phosphor::led::phyLedIntf);
364
365 for (const auto& path : physicalLedPaths)
366 {
367 manager.drivePhysicalLED(path, phosphor::led::Layout::Action::Off,
368 0, 0);
369 }
370
371 // Also remove the lamp test on indicator file.
372 if (!std::filesystem::remove(lampTestIndicator))
373 {
374 lg2::error(
375 "Error removing lamp test on indicator file after lamp test execution.");
376 }
377 }
378}
George Liuc777bef2020-11-23 17:04:21 +0800379} // namespace led
380} // namespace phosphor