blob: e361d64b3b852bc273a7c928bcaf62be2333d22d [file] [log] [blame]
Andrew Jefferye185efb2018-05-24 12:59:06 +09301/**
2 * Copyright © 2019 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "sysfs.hpp"
18
Alexander Hansen24d124f2024-07-16 13:49:55 +020019#include "phosphor-logging/lg2.hpp"
20
Alexander Hansena16bef72024-08-01 18:31:23 +020021#include <cstring>
Andrew Jefferye185efb2018-05-24 12:59:06 +093022#include <fstream>
Alexander Hansen24d124f2024-07-16 13:49:55 +020023#include <optional>
Andrew Jefferye185efb2018-05-24 12:59:06 +093024#include <string>
Alexander Hansen24d124f2024-07-16 13:49:55 +020025#include <vector>
Andrew Jefferye185efb2018-05-24 12:59:06 +093026
George Liu45eba6f2021-05-18 14:32:20 +080027namespace fs = std::filesystem;
Andrew Jefferye185efb2018-05-24 12:59:06 +093028
29namespace phosphor
30{
31namespace led
32{
Andrew Jefferye185efb2018-05-24 12:59:06 +093033template <typename T>
Andrew Jeffery36a8e842023-02-06 19:37:40 +103034T getSysfsAttr(const fs::path& path);
Andrew Jefferye185efb2018-05-24 12:59:06 +093035
36template <>
Andrew Jeffery36a8e842023-02-06 19:37:40 +103037std::string getSysfsAttr(const fs::path& path)
Andrew Jefferye185efb2018-05-24 12:59:06 +093038{
39 std::string content;
40 std::ifstream file(path);
George Liu001e2a32023-12-01 17:30:30 +080041 std::getline(file, content);
Andrew Jefferye185efb2018-05-24 12:59:06 +093042 return content;
43}
44
45template <>
Andrew Jeffery36a8e842023-02-06 19:37:40 +103046unsigned long getSysfsAttr(const fs::path& path)
Andrew Jefferye185efb2018-05-24 12:59:06 +093047{
Andrew Jefferycd4e6ca2023-02-06 19:43:52 +103048 std::string content = getSysfsAttr<std::string>(path);
Andrew Jefferye185efb2018-05-24 12:59:06 +093049 return std::strtoul(content.c_str(), nullptr, 0);
50}
51
52template <typename T>
Andrew Jeffery36a8e842023-02-06 19:37:40 +103053void setSysfsAttr(const fs::path& path, const T& value)
Andrew Jefferye185efb2018-05-24 12:59:06 +093054{
55 std::ofstream file(path);
56 file << value;
57}
58
59unsigned long SysfsLed::getBrightness()
60{
Andrew Jeffery8e852282023-02-06 19:29:43 +103061 return getSysfsAttr<unsigned long>(root / attrBrightness);
Andrew Jefferye185efb2018-05-24 12:59:06 +093062}
63
64void SysfsLed::setBrightness(unsigned long brightness)
65{
Andrew Jeffery8e852282023-02-06 19:29:43 +103066 setSysfsAttr<unsigned long>(root / attrBrightness, brightness);
Andrew Jefferye185efb2018-05-24 12:59:06 +093067}
68
69unsigned long SysfsLed::getMaxBrightness()
70{
Andrew Jeffery8e852282023-02-06 19:29:43 +103071 return getSysfsAttr<unsigned long>(root / attrMaxBrightness);
Andrew Jefferye185efb2018-05-24 12:59:06 +093072}
73
74std::string SysfsLed::getTrigger()
75{
George Liu001e2a32023-12-01 17:30:30 +080076 // Example content for `/sys/class/leds/<led_name>/trigger`:
77 //
78 // * `[none] timer heartbeat default-on`
79 // * `none [timer] heartbeat default-on`
80 //
81 // Refer to:
82 //
83 // * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/testing/sysfs-class-led?h=v6.6#n71
84 // * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/stable/sysfs-block?h=v6.6#n558
85 std::string triggerLine = getSysfsAttr<std::string>(root / attrTrigger);
86 size_t start = triggerLine.find_first_of('[');
87 size_t end = triggerLine.find_first_of(']');
88 if (start >= end || start == std::string::npos || end == std::string::npos)
89 {
90 return "none";
91 }
92
93 std::string rc = triggerLine.substr(start + 1, end - start - 1);
94 if (rc.empty())
95 {
96 return "none";
97 }
98
99 return rc;
Andrew Jefferye185efb2018-05-24 12:59:06 +0930100}
101
102void SysfsLed::setTrigger(const std::string& trigger)
103{
Andrew Jeffery8e852282023-02-06 19:29:43 +1030104 setSysfsAttr<std::string>(root / attrTrigger, trigger);
Andrew Jefferye185efb2018-05-24 12:59:06 +0930105}
106
107unsigned long SysfsLed::getDelayOn()
108{
Andrew Jeffery8e852282023-02-06 19:29:43 +1030109 return getSysfsAttr<unsigned long>(root / attrDelayOn);
Andrew Jefferye185efb2018-05-24 12:59:06 +0930110}
111
112void SysfsLed::setDelayOn(unsigned long ms)
113{
Andrew Jeffery8e852282023-02-06 19:29:43 +1030114 setSysfsAttr<unsigned long>(root / attrDelayOn, ms);
Andrew Jefferye185efb2018-05-24 12:59:06 +0930115}
116
117unsigned long SysfsLed::getDelayOff()
118{
Andrew Jeffery8e852282023-02-06 19:29:43 +1030119 return getSysfsAttr<unsigned long>(root / attrDelayOff);
Andrew Jefferye185efb2018-05-24 12:59:06 +0930120}
121
122void SysfsLed::setDelayOff(unsigned long ms)
123{
Andrew Jeffery8e852282023-02-06 19:29:43 +1030124 setSysfsAttr<unsigned long>(root / attrDelayOff, ms);
Andrew Jefferye185efb2018-05-24 12:59:06 +0930125}
Alexander Hansen24d124f2024-07-16 13:49:55 +0200126
127/* LED sysfs name can be any of
128 *
129 * - devicename:color:function
130 * - devicename::function
131 * - color:function (e.g. "red:fault")
132 * - label (e.g. "identify")
133 * - :function (e.g. ":boot")
134 * - color: (e.g. "green:")
135 *
136 * but no one prevents us from making all of this up and creating
137 * a label with colons inside, e.g. "mylabel:mynoncolorstring:lala".
138 *
139 * Reference: https://www.kernel.org/doc/html/latest/leds/leds-class.html
140 *
141 * Summary: It's bonkers (not my words, but describes it well)
142 */
143LedDescr SysfsLed::getLedDescr()
144{
145 std::string name = std::string(root).substr(strlen(devParent));
146 LedDescr ledDescr;
147
148 std::vector<std::optional<std::string>> words;
149 std::stringstream ss(name);
150 std::string item;
151
152 while (std::getline(ss, item, ':'))
153 {
154 if (item.empty())
155 {
156 words.emplace_back(std::nullopt);
157 }
158 else
159 {
160 words.emplace_back(item);
161 }
162 }
163
164 if (name.ends_with(":"))
165 {
166 words.emplace_back(std::nullopt);
167 }
168
169 if (name.empty())
170 {
171 lg2::warning("LED description '{DESC}' was empty", "DESC", name);
172 throw std::out_of_range("expected non-empty LED name");
173 }
174
175 if (words.size() != 3)
176 {
177 lg2::warning(
178 "LED description '{DESC}' not well formed, expected 3 parts but got {NPARTS}",
179 "DESC", name, "NPARTS", words.size());
180 }
181
182 switch (words.size())
183 {
184 default:
185 case 3:
186 ledDescr.function = words.at(2);
187 ledDescr.color = words.at(1);
188 ledDescr.devicename = words.at(0);
189 break;
190 case 2:
191 ledDescr.color = words.at(0);
192 ledDescr.function = words.at(1);
193 break;
194 case 1:
195 ledDescr.devicename = words.at(0);
196 break;
197 case 0:
198 throw std::out_of_range("expected non-empty LED name");
199 }
200
201 // if there is more than 3 parts we ignore the rest
202 return ledDescr;
203}
Andrew Jefferye185efb2018-05-24 12:59:06 +0930204} // namespace led
205} // namespace phosphor