blob: e7c9d5aacf803ab2c17191645e6071e5a4f3cdf8 [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 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
George Liu87fd11c2020-11-23 16:40:14 +080083 manager.drivePhysicalLED(path, Layout::Action::Off, 0, 0);
84 }
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 Liu1f0b7152023-07-18 09:24:34 +0800149 auto properties = dBusHandler.getAllProperties(path, phyLedIntf);
George Liub6151622020-11-23 18:16:18 +0800150
151 state = std::get<std::string>(properties["State"]);
152 period = std::get<uint16_t>(properties["Period"]);
153 dutyOn = std::get<uint8_t>(properties["DutyOn"]);
154 }
Patrick Williams3e073ba2022-07-22 19:26:52 -0500155 catch (const sdbusplus::exception_t& e)
George Liub6151622020-11-23 18:16:18 +0800156 {
George Liue9fb5c62021-07-01 14:05:32 +0800157 lg2::error(
158 "Failed to get All properties, ERROR = {ERROR}, PATH = {PATH}",
159 "ERROR", e, "PATH", path);
George Liub6151622020-11-23 18:16:18 +0800160 continue;
161 }
162
163 phosphor::led::Layout::Action action = getActionFromString(state);
Patrick Williamsed80e882022-03-17 05:03:51 -0500164 if (action != phosphor::led::Layout::Action::Off)
George Liub6151622020-11-23 18:16:18 +0800165 {
166 phosphor::led::Layout::LedAction ledAction{
Patrick Williamsed80e882022-03-17 05:03:51 -0500167 name, action, dutyOn, period,
168 phosphor::led::Layout::Action::On};
George Liub6151622020-11-23 18:16:18 +0800169 physicalLEDStatesPriorToLampTest.emplace(ledAction);
170 }
171 }
George Liuc777bef2020-11-23 17:04:21 +0800172}
173
174void LampTest::start()
175{
George Liub6151622020-11-23 18:16:18 +0800176 if (isLampTestRunning)
177 {
178 // reset the timer and then return
179 timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530180
181 // Notify host to reset the timer
182 doHostLampTest(true);
183
George Liub6151622020-11-23 18:16:18 +0800184 return;
185 }
186
George Liu87fd11c2020-11-23 16:40:14 +0800187 // Get paths of all the Physical LED objects
George Liu785f5052023-07-18 09:38:43 +0800188 try
189 {
George Liu1f0b7152023-07-18 09:24:34 +0800190 physicalLEDPaths = dBusHandler.getSubTreePaths(phyLedPath, phyLedIntf);
George Liu785f5052023-07-18 09:38:43 +0800191 }
192 catch (const sdbusplus::exception_t& e)
193 {
194 lg2::error(
195 "Failed to call the SubTreePaths method: {ERROR}, ledPath: {PATH}, ledInterface: {INTERFACE}",
George Liu1f0b7152023-07-18 09:24:34 +0800196 "ERROR", e, "PATH", phyLedPath, "INTERFACE", phyLedIntf);
George Liu785f5052023-07-18 09:38:43 +0800197 return;
198 }
George Liu87fd11c2020-11-23 16:40:14 +0800199
George Liub6151622020-11-23 18:16:18 +0800200 // Get physical LEDs states before lamp test
201 storePhysicalLEDsStates();
202
George Liuc777bef2020-11-23 17:04:21 +0800203 // restart lamp test, it contains initiate or reset the timer.
204 timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
George Liub6151622020-11-23 18:16:18 +0800205 isLampTestRunning = true;
George Liu87fd11c2020-11-23 16:40:14 +0800206
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530207 // Notify host to start the lamp test
George Liuce4d1c52021-01-25 11:32:37 +0800208 doHostLampTest(true);
209
Sunny Srivastavae3515c72022-10-15 12:45:40 -0500210 // Create a file to maintain the state across reboots that Lamp test is on.
211 // This is required as there was a scenario where it has been found that
212 // LEDs remains in "on" state if lamp test is triggered and reboot takes
213 // place.
214 const auto ledDirectory = lampTestIndicator.parent_path();
215
216 if (!fs::exists(ledDirectory))
217 {
218 fs::create_directories(ledDirectory);
219 }
220
George Liu8f538d92024-08-22 15:41:22 +0800221 std::ofstream ofs(lampTestIndicator.c_str());
Sunny Srivastavae3515c72022-10-15 12:45:40 -0500222
George Liu87fd11c2020-11-23 16:40:14 +0800223 // Set all the Physical action to On for lamp test
224 for (const auto& path : physicalLEDPaths)
225 {
George Liu82150322021-03-03 17:13:13 +0800226 auto iter =
227 std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
228 [&path](const auto& skip) { return skip == path; });
229
230 if (iter != skipUpdateLEDs.end())
231 {
232 // Skip update physical path
233 continue;
234 }
235
George Liu87fd11c2020-11-23 16:40:14 +0800236 manager.drivePhysicalLED(path, Layout::Action::On, 0, 0);
237 }
George Liuc777bef2020-11-23 17:04:21 +0800238}
239
240void LampTest::timeOutHandler()
241{
242 // set the Asserted property of lamp test to false
George Liu87fd11c2020-11-23 16:40:14 +0800243 if (!groupObj)
244 {
George Liue9fb5c62021-07-01 14:05:32 +0800245 lg2::error("the Group object is nullptr");
George Liu87fd11c2020-11-23 16:40:14 +0800246 throw std::runtime_error("the Group object is nullptr");
247 }
248
249 groupObj->asserted(false);
George Liuc777bef2020-11-23 17:04:21 +0800250}
251
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530252bool LampTest::requestHandler(Group* group, bool value)
George Liuc777bef2020-11-23 17:04:21 +0800253{
George Liuaaa667f2024-08-22 17:37:51 +0800254 if (groupObj == nullptr)
George Liu87fd11c2020-11-23 16:40:14 +0800255 {
George Liudf3ab7c2024-08-22 19:05:27 +0800256 groupObj = group;
George Liu87fd11c2020-11-23 16:40:14 +0800257 }
258
George Liuc777bef2020-11-23 17:04:21 +0800259 if (value)
260 {
261 start();
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530262
263 // Return true in both cases (F -> T && T -> T)
264 return true;
George Liuc777bef2020-11-23 17:04:21 +0800265 }
266 else
267 {
PriyangaRamasamy180090c2022-10-10 10:50:52 +0530268 if (timer.hasExpired())
269 {
270 stop();
271
272 // Return true as the request to stop the lamptest is handled
273 // successfully.
274 return true;
275 }
276 else if (timer.isEnabled())
277 {
278 lg2::info(
279 "Lamp test is still running. Cannot force stop the lamp test. Asserted is set back to true.");
280
281 // Return false as the request to stop lamptest is not handled as
282 // the lamptest is still running.
283 return false;
284 }
285 return false;
George Liuc777bef2020-11-23 17:04:21 +0800286 }
287}
288
George Liub6151622020-11-23 18:16:18 +0800289void LampTest::restorePhysicalLedStates()
290{
291 // restore physical LEDs states before lamp test
Patrick Williams158b2c12022-03-17 05:57:44 -0500292 ActionSet ledsDeAssert{};
George Liu82150322021-03-03 17:13:13 +0800293 manager.driveLEDs(physicalLEDStatesPriorToLampTest, ledsDeAssert);
George Liub6151622020-11-23 18:16:18 +0800294 physicalLEDStatesPriorToLampTest.clear();
295
296 // restore physical LEDs states during lamp test
297 while (!updatedLEDsDuringLampTest.empty())
298 {
299 auto& [ledsAssert, ledsDeAssert] = updatedLEDsDuringLampTest.front();
300 manager.driveLEDs(ledsAssert, ledsDeAssert);
301 updatedLEDsDuringLampTest.pop();
302 }
303}
304
George Liuce4d1c52021-01-25 11:32:37 +0800305void LampTest::doHostLampTest(bool value)
306{
307 try
308 {
309 PropertyValue assertedValue{value};
310 dBusHandler.setProperty(HOST_LAMP_TEST_OBJECT,
311 "xyz.openbmc_project.Led.Group", "Asserted",
312 assertedValue);
313 }
Patrick Williams3e073ba2022-07-22 19:26:52 -0500314 catch (const sdbusplus::exception_t& e)
George Liuce4d1c52021-01-25 11:32:37 +0800315 {
George Liue9fb5c62021-07-01 14:05:32 +0800316 lg2::error(
317 "Failed to set Asserted property, ERROR = {ERROR}, PATH = {PATH}",
318 "ERROR", e, "PATH", std::string(HOST_LAMP_TEST_OBJECT));
George Liuce4d1c52021-01-25 11:32:37 +0800319 }
320}
321
George Liu82150322021-03-03 17:13:13 +0800322void LampTest::getPhysicalLEDNamesFromJson(const fs::path& path)
323{
324 if (!fs::exists(path) || fs::is_empty(path))
325 {
George Liue9fb5c62021-07-01 14:05:32 +0800326 lg2::info("The file does not exist or is empty, FILE_PATH = {PATH}",
327 "PATH", path);
George Liu82150322021-03-03 17:13:13 +0800328 return;
329 }
330
331 try
332 {
333 std::ifstream jsonFile(path);
334 auto json = Json::parse(jsonFile);
335
336 // define the default JSON as empty
337 const std::vector<std::string> empty{};
338 auto forceLEDs = json.value("forceLEDs", empty);
George Liuc5e0f312021-12-27 15:54:31 +0800339 std::ranges::transform(forceLEDs, std::back_inserter(forceUpdateLEDs),
George Liu1f0b7152023-07-18 09:24:34 +0800340 [](const auto& i) { return phyLedPath + i; });
George Liu82150322021-03-03 17:13:13 +0800341
342 auto skipLEDs = json.value("skipLEDs", empty);
George Liuc5e0f312021-12-27 15:54:31 +0800343 std::ranges::transform(skipLEDs, std::back_inserter(skipUpdateLEDs),
George Liu1f0b7152023-07-18 09:24:34 +0800344 [](const auto& i) { return phyLedPath + i; });
George Liu82150322021-03-03 17:13:13 +0800345 }
346 catch (const std::exception& e)
347 {
George Liue9fb5c62021-07-01 14:05:32 +0800348 lg2::error(
349 "Failed to parse config file, ERROR = {ERROR}, FILE_PATH = {PATH}",
350 "ERROR", e, "PATH", path);
George Liu82150322021-03-03 17:13:13 +0800351 }
352 return;
353}
Sunny Srivastavae3515c72022-10-15 12:45:40 -0500354
355void LampTest::clearLamps()
356{
357 if (std::filesystem::exists(lampTestIndicator))
358 {
359 // we need to off all the LEDs.
360 phosphor::led::utils::DBusHandler dBusHandler;
361 std::vector<std::string> physicalLedPaths = dBusHandler.getSubTreePaths(
362 phosphor::led::phyLedPath, phosphor::led::phyLedIntf);
363
364 for (const auto& path : physicalLedPaths)
365 {
366 manager.drivePhysicalLED(path, phosphor::led::Layout::Action::Off,
367 0, 0);
368 }
369
370 // Also remove the lamp test on indicator file.
371 if (!std::filesystem::remove(lampTestIndicator))
372 {
373 lg2::error(
374 "Error removing lamp test on indicator file after lamp test execution.");
375 }
376 }
377}
George Liuc777bef2020-11-23 17:04:21 +0800378} // namespace led
379} // namespace phosphor