1953315d2SPatrick Williams #include "lamptest.hpp"
2953315d2SPatrick Williams
3953315d2SPatrick Williams #include <phosphor-logging/lg2.hpp>
4953315d2SPatrick Williams
5953315d2SPatrick Williams #include <algorithm>
6953315d2SPatrick Williams
7953315d2SPatrick Williams namespace phosphor
8953315d2SPatrick Williams {
9953315d2SPatrick Williams namespace led
10953315d2SPatrick Williams {
11953315d2SPatrick Williams
12953315d2SPatrick Williams using Json = nlohmann::json;
13e3515c71SSunny Srivastava static const fs::path lampTestIndicator =
14e3515c71SSunny Srivastava "/var/lib/phosphor-led-manager/lamp-test-running";
15953315d2SPatrick Williams
processLEDUpdates(const ActionSet & ledsAssert,const ActionSet & ledsDeAssert)16158b2c14SPatrick Williams bool LampTest::processLEDUpdates(const ActionSet& ledsAssert,
17158b2c14SPatrick Williams const ActionSet& ledsDeAssert)
18953315d2SPatrick Williams {
19953315d2SPatrick Williams // If the physical LED status is updated during the lamp test, it should be
20953315d2SPatrick Williams // saved to Queue, and the queue will be processed after the lamp test is
21953315d2SPatrick Williams // stopped.
22953315d2SPatrick Williams if (isLampTestRunning)
23953315d2SPatrick Williams {
24953315d2SPatrick Williams // Physical LEDs will be updated during lamp test
25953315d2SPatrick Williams for (const auto& it : ledsDeAssert)
26953315d2SPatrick Williams {
271f0b715aSGeorge Liu std::string path = std::string(phyLedPath) + it.name;
28953315d2SPatrick Williams auto iter = std::find_if(
29953315d2SPatrick Williams forceUpdateLEDs.begin(), forceUpdateLEDs.end(),
30953315d2SPatrick Williams [&path](const auto& name) { return name == path; });
31953315d2SPatrick Williams
32953315d2SPatrick Williams if (iter != forceUpdateLEDs.end())
33953315d2SPatrick Williams {
34f0592559SGeorge Liu phosphor::led::Manager::drivePhysicalLED(
35f0592559SGeorge Liu path, Layout::Action::Off, it.dutyOn, it.period);
36953315d2SPatrick Williams }
37953315d2SPatrick Williams }
38953315d2SPatrick Williams
39953315d2SPatrick Williams for (const auto& it : ledsAssert)
40953315d2SPatrick Williams {
411f0b715aSGeorge Liu std::string path = std::string(phyLedPath) + it.name;
42953315d2SPatrick Williams auto iter = std::find_if(
43953315d2SPatrick Williams forceUpdateLEDs.begin(), forceUpdateLEDs.end(),
44953315d2SPatrick Williams [&path](const auto& name) { return name == path; });
45953315d2SPatrick Williams
46953315d2SPatrick Williams if (iter != forceUpdateLEDs.end())
47953315d2SPatrick Williams {
48f0592559SGeorge Liu phosphor::led::Manager::drivePhysicalLED(path, it.action,
49f0592559SGeorge Liu it.dutyOn, it.period);
50953315d2SPatrick Williams }
51953315d2SPatrick Williams }
52953315d2SPatrick Williams
53fcf08106SGeorge Liu updatedLEDsDuringLampTest.emplace(ledsAssert, ledsDeAssert);
54953315d2SPatrick Williams return true;
55953315d2SPatrick Williams }
56953315d2SPatrick Williams return false;
57953315d2SPatrick Williams }
58953315d2SPatrick Williams
stop()59953315d2SPatrick Williams void LampTest::stop()
60953315d2SPatrick Williams {
61953315d2SPatrick Williams if (!isLampTestRunning)
62953315d2SPatrick Williams {
63953315d2SPatrick Williams return;
64953315d2SPatrick Williams }
65953315d2SPatrick Williams
66953315d2SPatrick Williams timer.setEnabled(false);
67953315d2SPatrick Williams
68953315d2SPatrick Williams // Stop host lamp test
69953315d2SPatrick Williams doHostLampTest(false);
70953315d2SPatrick Williams
71953315d2SPatrick Williams // Set all the Physical action to Off
72953315d2SPatrick Williams for (const auto& path : physicalLEDPaths)
73953315d2SPatrick Williams {
74953315d2SPatrick Williams auto iter =
75953315d2SPatrick Williams std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
76953315d2SPatrick Williams [&path](const auto& skip) { return skip == path; });
77953315d2SPatrick Williams
78953315d2SPatrick Williams if (iter != skipUpdateLEDs.end())
79953315d2SPatrick Williams {
80953315d2SPatrick Williams // Skip update physical path
81953315d2SPatrick Williams continue;
82953315d2SPatrick Williams }
83953315d2SPatrick Williams
84f0592559SGeorge Liu phosphor::led::Manager::drivePhysicalLED(path, Layout::Action::Off, 0,
85f0592559SGeorge Liu 0);
86953315d2SPatrick Williams }
87953315d2SPatrick Williams
88e3515c71SSunny Srivastava if (std::filesystem::exists(lampTestIndicator))
89e3515c71SSunny Srivastava {
90e3515c71SSunny Srivastava if (!std::filesystem::remove(lampTestIndicator))
91e3515c71SSunny Srivastava {
92e3515c71SSunny Srivastava lg2::error(
93e3515c71SSunny Srivastava "Error removing lamp test on indicator file after lamp test execution.");
94e3515c71SSunny Srivastava }
95e3515c71SSunny Srivastava }
96e3515c71SSunny Srivastava
97953315d2SPatrick Williams isLampTestRunning = false;
98953315d2SPatrick Williams restorePhysicalLedStates();
99953315d2SPatrick Williams }
100953315d2SPatrick Williams
getActionFromString(const std::string & str)101953315d2SPatrick Williams Layout::Action LampTest::getActionFromString(const std::string& str)
102953315d2SPatrick Williams {
103ed80e885SPatrick Williams Layout::Action action = Layout::Action::Off;
104953315d2SPatrick Williams
105953315d2SPatrick Williams if (str == "xyz.openbmc_project.Led.Physical.Action.On")
106953315d2SPatrick Williams {
107ed80e885SPatrick Williams action = Layout::Action::On;
108953315d2SPatrick Williams }
109953315d2SPatrick Williams else if (str == "xyz.openbmc_project.Led.Physical.Action.Blink")
110953315d2SPatrick Williams {
111ed80e885SPatrick Williams action = Layout::Action::Blink;
112953315d2SPatrick Williams }
113953315d2SPatrick Williams
114953315d2SPatrick Williams return action;
115953315d2SPatrick Williams }
116953315d2SPatrick Williams
storePhysicalLEDsStates()117953315d2SPatrick Williams void LampTest::storePhysicalLEDsStates()
118953315d2SPatrick Williams {
119953315d2SPatrick Williams physicalLEDStatesPriorToLampTest.clear();
120953315d2SPatrick Williams
121953315d2SPatrick Williams for (const auto& path : physicalLEDPaths)
122953315d2SPatrick Williams {
123605600ebSPatrick Williams auto iter = std::find_if(
124605600ebSPatrick Williams skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
125605600ebSPatrick Williams [&path](const auto& skipLed) { return skipLed == path; });
126953315d2SPatrick Williams
127953315d2SPatrick Williams if (iter != skipUpdateLEDs.end())
128953315d2SPatrick Williams {
129953315d2SPatrick Williams // Physical LEDs will be skipped
130953315d2SPatrick Williams continue;
131953315d2SPatrick Williams }
132953315d2SPatrick Williams
133953315d2SPatrick Williams // Reverse intercept path, Get the name of each member of physical led
134953315d2SPatrick Williams // e.g: path = /xyz/openbmc_project/led/physical/front_fan
135953315d2SPatrick Williams // name = front_fan
136953315d2SPatrick Williams sdbusplus::message::object_path object_path(path);
137953315d2SPatrick Williams auto name = object_path.filename();
138953315d2SPatrick Williams if (name.empty())
139953315d2SPatrick Williams {
140953315d2SPatrick Williams lg2::error(
141953315d2SPatrick Williams "Failed to get the name of member of physical LED path, PATH = {PATH}, NAME = {NAME}",
142953315d2SPatrick Williams "PATH", path, "NAME", name);
143953315d2SPatrick Williams continue;
144953315d2SPatrick Williams }
145953315d2SPatrick Williams
146953315d2SPatrick Williams std::string state{};
147953315d2SPatrick Williams uint16_t period{};
148953315d2SPatrick Williams uint8_t dutyOn{};
149953315d2SPatrick Williams try
150953315d2SPatrick Williams {
151f0592559SGeorge Liu auto properties =
152f0592559SGeorge Liu phosphor::led::utils::DBusHandler::getAllProperties(path,
153f0592559SGeorge Liu phyLedIntf);
154953315d2SPatrick Williams
155953315d2SPatrick Williams state = std::get<std::string>(properties["State"]);
156953315d2SPatrick Williams period = std::get<uint16_t>(properties["Period"]);
157953315d2SPatrick Williams dutyOn = std::get<uint8_t>(properties["DutyOn"]);
158953315d2SPatrick Williams }
1593e073ba6SPatrick Williams catch (const sdbusplus::exception_t& e)
160953315d2SPatrick Williams {
161953315d2SPatrick Williams lg2::error(
162953315d2SPatrick Williams "Failed to get All properties, ERROR = {ERROR}, PATH = {PATH}",
163953315d2SPatrick Williams "ERROR", e, "PATH", path);
164953315d2SPatrick Williams continue;
165953315d2SPatrick Williams }
166953315d2SPatrick Williams
167953315d2SPatrick Williams phosphor::led::Layout::Action action = getActionFromString(state);
168ed80e885SPatrick Williams if (action != phosphor::led::Layout::Action::Off)
169953315d2SPatrick Williams {
170953315d2SPatrick Williams phosphor::led::Layout::LedAction ledAction{
171ed80e885SPatrick Williams name, action, dutyOn, period,
172ed80e885SPatrick Williams phosphor::led::Layout::Action::On};
173953315d2SPatrick Williams physicalLEDStatesPriorToLampTest.emplace(ledAction);
174953315d2SPatrick Williams }
175953315d2SPatrick Williams }
176953315d2SPatrick Williams }
177953315d2SPatrick Williams
start()178953315d2SPatrick Williams void LampTest::start()
179953315d2SPatrick Williams {
180953315d2SPatrick Williams if (isLampTestRunning)
181953315d2SPatrick Williams {
182953315d2SPatrick Williams // reset the timer and then return
183953315d2SPatrick Williams timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
184180090cfSPriyangaRamasamy
185180090cfSPriyangaRamasamy // Notify host to reset the timer
186180090cfSPriyangaRamasamy doHostLampTest(true);
187180090cfSPriyangaRamasamy
188953315d2SPatrick Williams return;
189953315d2SPatrick Williams }
190953315d2SPatrick Williams
191953315d2SPatrick Williams // Get paths of all the Physical LED objects
192785f5054SGeorge Liu try
193785f5054SGeorge Liu {
194f0592559SGeorge Liu physicalLEDPaths = phosphor::led::utils::DBusHandler::getSubTreePaths(
195f0592559SGeorge Liu phyLedPath, phyLedIntf);
196785f5054SGeorge Liu }
197785f5054SGeorge Liu catch (const sdbusplus::exception_t& e)
198785f5054SGeorge Liu {
199785f5054SGeorge Liu lg2::error(
200785f5054SGeorge Liu "Failed to call the SubTreePaths method: {ERROR}, ledPath: {PATH}, ledInterface: {INTERFACE}",
2011f0b715aSGeorge Liu "ERROR", e, "PATH", phyLedPath, "INTERFACE", phyLedIntf);
202785f5054SGeorge Liu return;
203785f5054SGeorge Liu }
204953315d2SPatrick Williams
205953315d2SPatrick Williams // Get physical LEDs states before lamp test
206953315d2SPatrick Williams storePhysicalLEDsStates();
207953315d2SPatrick Williams
208953315d2SPatrick Williams // restart lamp test, it contains initiate or reset the timer.
209953315d2SPatrick Williams timer.restart(std::chrono::seconds(LAMP_TEST_TIMEOUT_IN_SECS));
210953315d2SPatrick Williams isLampTestRunning = true;
211953315d2SPatrick Williams
212180090cfSPriyangaRamasamy // Notify host to start the lamp test
213953315d2SPatrick Williams doHostLampTest(true);
214953315d2SPatrick Williams
215e3515c71SSunny Srivastava // Create a file to maintain the state across reboots that Lamp test is on.
216e3515c71SSunny Srivastava // This is required as there was a scenario where it has been found that
217e3515c71SSunny Srivastava // LEDs remains in "on" state if lamp test is triggered and reboot takes
218e3515c71SSunny Srivastava // place.
219e3515c71SSunny Srivastava const auto ledDirectory = lampTestIndicator.parent_path();
220e3515c71SSunny Srivastava
221e3515c71SSunny Srivastava if (!fs::exists(ledDirectory))
222e3515c71SSunny Srivastava {
223e3515c71SSunny Srivastava fs::create_directories(ledDirectory);
224e3515c71SSunny Srivastava }
225e3515c71SSunny Srivastava
2268f538d9cSGeorge Liu std::ofstream ofs(lampTestIndicator.c_str());
227e3515c71SSunny Srivastava
228953315d2SPatrick Williams // Set all the Physical action to On for lamp test
229953315d2SPatrick Williams for (const auto& path : physicalLEDPaths)
230953315d2SPatrick Williams {
231953315d2SPatrick Williams auto iter =
232953315d2SPatrick Williams std::find_if(skipUpdateLEDs.begin(), skipUpdateLEDs.end(),
233953315d2SPatrick Williams [&path](const auto& skip) { return skip == path; });
234953315d2SPatrick Williams
235953315d2SPatrick Williams if (iter != skipUpdateLEDs.end())
236953315d2SPatrick Williams {
237953315d2SPatrick Williams // Skip update physical path
238953315d2SPatrick Williams continue;
239953315d2SPatrick Williams }
240953315d2SPatrick Williams
241f0592559SGeorge Liu phosphor::led::Manager::drivePhysicalLED(path, Layout::Action::On, 0,
242f0592559SGeorge Liu 0);
243953315d2SPatrick Williams }
244953315d2SPatrick Williams }
245953315d2SPatrick Williams
timeOutHandler()246953315d2SPatrick Williams void LampTest::timeOutHandler()
247953315d2SPatrick Williams {
248953315d2SPatrick Williams // set the Asserted property of lamp test to false
249*49875a26SGeorge Liu if (groupObj == nullptr)
250953315d2SPatrick Williams {
251953315d2SPatrick Williams lg2::error("the Group object is nullptr");
252953315d2SPatrick Williams throw std::runtime_error("the Group object is nullptr");
253953315d2SPatrick Williams }
254953315d2SPatrick Williams
255953315d2SPatrick Williams groupObj->asserted(false);
256953315d2SPatrick Williams }
257953315d2SPatrick Williams
requestHandler(Group * group,bool value)258180090cfSPriyangaRamasamy bool LampTest::requestHandler(Group* group, bool value)
259953315d2SPatrick Williams {
260aaa667f2SGeorge Liu if (groupObj == nullptr)
261953315d2SPatrick Williams {
262df3ab7c9SGeorge Liu groupObj = group;
263953315d2SPatrick Williams }
264953315d2SPatrick Williams
265953315d2SPatrick Williams if (value)
266953315d2SPatrick Williams {
267953315d2SPatrick Williams start();
268180090cfSPriyangaRamasamy
269180090cfSPriyangaRamasamy // Return true in both cases (F -> T && T -> T)
270180090cfSPriyangaRamasamy return true;
271953315d2SPatrick Williams }
272953315d2SPatrick Williams else
273953315d2SPatrick Williams {
274180090cfSPriyangaRamasamy if (timer.hasExpired())
275180090cfSPriyangaRamasamy {
276953315d2SPatrick Williams stop();
277180090cfSPriyangaRamasamy
278180090cfSPriyangaRamasamy // Return true as the request to stop the lamptest is handled
279180090cfSPriyangaRamasamy // successfully.
280180090cfSPriyangaRamasamy return true;
281180090cfSPriyangaRamasamy }
282180090cfSPriyangaRamasamy else if (timer.isEnabled())
283180090cfSPriyangaRamasamy {
284180090cfSPriyangaRamasamy lg2::info(
285180090cfSPriyangaRamasamy "Lamp test is still running. Cannot force stop the lamp test. Asserted is set back to true.");
286180090cfSPriyangaRamasamy
287180090cfSPriyangaRamasamy // Return false as the request to stop lamptest is not handled as
288180090cfSPriyangaRamasamy // the lamptest is still running.
289180090cfSPriyangaRamasamy return false;
290180090cfSPriyangaRamasamy }
291180090cfSPriyangaRamasamy return false;
292953315d2SPatrick Williams }
293953315d2SPatrick Williams }
294953315d2SPatrick Williams
restorePhysicalLedStates()295953315d2SPatrick Williams void LampTest::restorePhysicalLedStates()
296953315d2SPatrick Williams {
297953315d2SPatrick Williams // restore physical LEDs states before lamp test
298158b2c14SPatrick Williams ActionSet ledsDeAssert{};
299953315d2SPatrick Williams manager.driveLEDs(physicalLEDStatesPriorToLampTest, ledsDeAssert);
300953315d2SPatrick Williams physicalLEDStatesPriorToLampTest.clear();
301953315d2SPatrick Williams
302953315d2SPatrick Williams // restore physical LEDs states during lamp test
303953315d2SPatrick Williams while (!updatedLEDsDuringLampTest.empty())
304953315d2SPatrick Williams {
305953315d2SPatrick Williams auto& [ledsAssert, ledsDeAssert] = updatedLEDsDuringLampTest.front();
306953315d2SPatrick Williams manager.driveLEDs(ledsAssert, ledsDeAssert);
307953315d2SPatrick Williams updatedLEDsDuringLampTest.pop();
308953315d2SPatrick Williams }
309953315d2SPatrick Williams }
310953315d2SPatrick Williams
doHostLampTest(bool value)311953315d2SPatrick Williams void LampTest::doHostLampTest(bool value)
312953315d2SPatrick Williams {
313953315d2SPatrick Williams try
314953315d2SPatrick Williams {
315953315d2SPatrick Williams PropertyValue assertedValue{value};
316f0592559SGeorge Liu phosphor::led::utils::DBusHandler::setProperty(
317f0592559SGeorge Liu HOST_LAMP_TEST_OBJECT, "xyz.openbmc_project.Led.Group", "Asserted",
318953315d2SPatrick Williams assertedValue);
319953315d2SPatrick Williams }
3203e073ba6SPatrick Williams catch (const sdbusplus::exception_t& e)
321953315d2SPatrick Williams {
322953315d2SPatrick Williams lg2::error(
323953315d2SPatrick Williams "Failed to set Asserted property, ERROR = {ERROR}, PATH = {PATH}",
324953315d2SPatrick Williams "ERROR", e, "PATH", std::string(HOST_LAMP_TEST_OBJECT));
325953315d2SPatrick Williams }
326953315d2SPatrick Williams }
327953315d2SPatrick Williams
getPhysicalLEDNamesFromJson(const fs::path & path)328953315d2SPatrick Williams void LampTest::getPhysicalLEDNamesFromJson(const fs::path& path)
329953315d2SPatrick Williams {
330953315d2SPatrick Williams if (!fs::exists(path) || fs::is_empty(path))
331953315d2SPatrick Williams {
332953315d2SPatrick Williams lg2::info("The file does not exist or is empty, FILE_PATH = {PATH}",
333953315d2SPatrick Williams "PATH", path);
334953315d2SPatrick Williams return;
335953315d2SPatrick Williams }
336953315d2SPatrick Williams
337953315d2SPatrick Williams try
338953315d2SPatrick Williams {
339953315d2SPatrick Williams std::ifstream jsonFile(path);
340953315d2SPatrick Williams auto json = Json::parse(jsonFile);
341953315d2SPatrick Williams
342953315d2SPatrick Williams // define the default JSON as empty
343953315d2SPatrick Williams const std::vector<std::string> empty{};
344953315d2SPatrick Williams auto forceLEDs = json.value("forceLEDs", empty);
345953315d2SPatrick Williams std::ranges::transform(forceLEDs, std::back_inserter(forceUpdateLEDs),
3461f0b715aSGeorge Liu [](const auto& i) { return phyLedPath + i; });
347953315d2SPatrick Williams
348953315d2SPatrick Williams auto skipLEDs = json.value("skipLEDs", empty);
349953315d2SPatrick Williams std::ranges::transform(skipLEDs, std::back_inserter(skipUpdateLEDs),
3501f0b715aSGeorge Liu [](const auto& i) { return phyLedPath + i; });
351953315d2SPatrick Williams }
352953315d2SPatrick Williams catch (const std::exception& e)
353953315d2SPatrick Williams {
354953315d2SPatrick Williams lg2::error(
355953315d2SPatrick Williams "Failed to parse config file, ERROR = {ERROR}, FILE_PATH = {PATH}",
356953315d2SPatrick Williams "ERROR", e, "PATH", path);
357953315d2SPatrick Williams }
358953315d2SPatrick Williams return;
359953315d2SPatrick Williams }
360e3515c71SSunny Srivastava
clearLamps()361e3515c71SSunny Srivastava void LampTest::clearLamps()
362e3515c71SSunny Srivastava {
363e3515c71SSunny Srivastava if (std::filesystem::exists(lampTestIndicator))
364e3515c71SSunny Srivastava {
365e3515c71SSunny Srivastava // we need to off all the LEDs.
366f0592559SGeorge Liu std::vector<std::string> physicalLedPaths =
367f0592559SGeorge Liu phosphor::led::utils::DBusHandler::getSubTreePaths(
368e3515c71SSunny Srivastava phosphor::led::phyLedPath, phosphor::led::phyLedIntf);
369e3515c71SSunny Srivastava
370e3515c71SSunny Srivastava for (const auto& path : physicalLedPaths)
371e3515c71SSunny Srivastava {
372f0592559SGeorge Liu phosphor::led::Manager::drivePhysicalLED(
373f0592559SGeorge Liu path, phosphor::led::Layout::Action::Off, 0, 0);
374e3515c71SSunny Srivastava }
375e3515c71SSunny Srivastava
376e3515c71SSunny Srivastava // Also remove the lamp test on indicator file.
377e3515c71SSunny Srivastava if (!std::filesystem::remove(lampTestIndicator))
378e3515c71SSunny Srivastava {
379e3515c71SSunny Srivastava lg2::error(
380e3515c71SSunny Srivastava "Error removing lamp test on indicator file after lamp test execution.");
381e3515c71SSunny Srivastava }
382e3515c71SSunny Srivastava }
383e3515c71SSunny Srivastava }
384953315d2SPatrick Williams } // namespace led
385953315d2SPatrick Williams } // namespace phosphor
386