blob: 137218d4b32474c2ac02171a9c378031fbc32d72 [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
21#include <boost/algorithm/string.hpp>
22
Andrew Jefferye185efb2018-05-24 12:59:06 +093023#include <fstream>
Alexander Hansen24d124f2024-07-16 13:49:55 +020024#include <optional>
Andrew Jefferye185efb2018-05-24 12:59:06 +093025#include <string>
Alexander Hansen24d124f2024-07-16 13:49:55 +020026#include <vector>
Andrew Jefferye185efb2018-05-24 12:59:06 +093027
George Liu45eba6f2021-05-18 14:32:20 +080028namespace fs = std::filesystem;
Andrew Jefferye185efb2018-05-24 12:59:06 +093029
30namespace phosphor
31{
32namespace led
33{
Andrew Jefferye185efb2018-05-24 12:59:06 +093034template <typename T>
Andrew Jeffery36a8e842023-02-06 19:37:40 +103035T getSysfsAttr(const fs::path& path);
Andrew Jefferye185efb2018-05-24 12:59:06 +093036
37template <>
Andrew Jeffery36a8e842023-02-06 19:37:40 +103038std::string getSysfsAttr(const fs::path& path)
Andrew Jefferye185efb2018-05-24 12:59:06 +093039{
40 std::string content;
41 std::ifstream file(path);
George Liu001e2a32023-12-01 17:30:30 +080042 std::getline(file, content);
Andrew Jefferye185efb2018-05-24 12:59:06 +093043 return content;
44}
45
46template <>
Andrew Jeffery36a8e842023-02-06 19:37:40 +103047unsigned long getSysfsAttr(const fs::path& path)
Andrew Jefferye185efb2018-05-24 12:59:06 +093048{
Andrew Jefferycd4e6ca2023-02-06 19:43:52 +103049 std::string content = getSysfsAttr<std::string>(path);
Andrew Jefferye185efb2018-05-24 12:59:06 +093050 return std::strtoul(content.c_str(), nullptr, 0);
51}
52
53template <typename T>
Andrew Jeffery36a8e842023-02-06 19:37:40 +103054void setSysfsAttr(const fs::path& path, const T& value)
Andrew Jefferye185efb2018-05-24 12:59:06 +093055{
56 std::ofstream file(path);
57 file << value;
58}
59
60unsigned long SysfsLed::getBrightness()
61{
Andrew Jeffery8e852282023-02-06 19:29:43 +103062 return getSysfsAttr<unsigned long>(root / attrBrightness);
Andrew Jefferye185efb2018-05-24 12:59:06 +093063}
64
65void SysfsLed::setBrightness(unsigned long brightness)
66{
Andrew Jeffery8e852282023-02-06 19:29:43 +103067 setSysfsAttr<unsigned long>(root / attrBrightness, brightness);
Andrew Jefferye185efb2018-05-24 12:59:06 +093068}
69
70unsigned long SysfsLed::getMaxBrightness()
71{
Andrew Jeffery8e852282023-02-06 19:29:43 +103072 return getSysfsAttr<unsigned long>(root / attrMaxBrightness);
Andrew Jefferye185efb2018-05-24 12:59:06 +093073}
74
75std::string SysfsLed::getTrigger()
76{
George Liu001e2a32023-12-01 17:30:30 +080077 // Example content for `/sys/class/leds/<led_name>/trigger`:
78 //
79 // * `[none] timer heartbeat default-on`
80 // * `none [timer] heartbeat default-on`
81 //
82 // Refer to:
83 //
84 // * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/testing/sysfs-class-led?h=v6.6#n71
85 // * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/stable/sysfs-block?h=v6.6#n558
86 std::string triggerLine = getSysfsAttr<std::string>(root / attrTrigger);
87 size_t start = triggerLine.find_first_of('[');
88 size_t end = triggerLine.find_first_of(']');
89 if (start >= end || start == std::string::npos || end == std::string::npos)
90 {
91 return "none";
92 }
93
94 std::string rc = triggerLine.substr(start + 1, end - start - 1);
95 if (rc.empty())
96 {
97 return "none";
98 }
99
100 return rc;
Andrew Jefferye185efb2018-05-24 12:59:06 +0930101}
102
103void SysfsLed::setTrigger(const std::string& trigger)
104{
Andrew Jeffery8e852282023-02-06 19:29:43 +1030105 setSysfsAttr<std::string>(root / attrTrigger, trigger);
Andrew Jefferye185efb2018-05-24 12:59:06 +0930106}
107
108unsigned long SysfsLed::getDelayOn()
109{
Andrew Jeffery8e852282023-02-06 19:29:43 +1030110 return getSysfsAttr<unsigned long>(root / attrDelayOn);
Andrew Jefferye185efb2018-05-24 12:59:06 +0930111}
112
113void SysfsLed::setDelayOn(unsigned long ms)
114{
Andrew Jeffery8e852282023-02-06 19:29:43 +1030115 setSysfsAttr<unsigned long>(root / attrDelayOn, ms);
Andrew Jefferye185efb2018-05-24 12:59:06 +0930116}
117
118unsigned long SysfsLed::getDelayOff()
119{
Andrew Jeffery8e852282023-02-06 19:29:43 +1030120 return getSysfsAttr<unsigned long>(root / attrDelayOff);
Andrew Jefferye185efb2018-05-24 12:59:06 +0930121}
122
123void SysfsLed::setDelayOff(unsigned long ms)
124{
Andrew Jeffery8e852282023-02-06 19:29:43 +1030125 setSysfsAttr<unsigned long>(root / attrDelayOff, ms);
Andrew Jefferye185efb2018-05-24 12:59:06 +0930126}
Alexander Hansen24d124f2024-07-16 13:49:55 +0200127
128/* LED sysfs name can be any of
129 *
130 * - devicename:color:function
131 * - devicename::function
132 * - color:function (e.g. "red:fault")
133 * - label (e.g. "identify")
134 * - :function (e.g. ":boot")
135 * - color: (e.g. "green:")
136 *
137 * but no one prevents us from making all of this up and creating
138 * a label with colons inside, e.g. "mylabel:mynoncolorstring:lala".
139 *
140 * Reference: https://www.kernel.org/doc/html/latest/leds/leds-class.html
141 *
142 * Summary: It's bonkers (not my words, but describes it well)
143 */
144LedDescr SysfsLed::getLedDescr()
145{
146 std::string name = std::string(root).substr(strlen(devParent));
147 LedDescr ledDescr;
148
149 std::vector<std::optional<std::string>> words;
150 std::stringstream ss(name);
151 std::string item;
152
153 while (std::getline(ss, item, ':'))
154 {
155 if (item.empty())
156 {
157 words.emplace_back(std::nullopt);
158 }
159 else
160 {
161 words.emplace_back(item);
162 }
163 }
164
165 if (name.ends_with(":"))
166 {
167 words.emplace_back(std::nullopt);
168 }
169
170 if (name.empty())
171 {
172 lg2::warning("LED description '{DESC}' was empty", "DESC", name);
173 throw std::out_of_range("expected non-empty LED name");
174 }
175
176 if (words.size() != 3)
177 {
178 lg2::warning(
179 "LED description '{DESC}' not well formed, expected 3 parts but got {NPARTS}",
180 "DESC", name, "NPARTS", words.size());
181 }
182
183 switch (words.size())
184 {
185 default:
186 case 3:
187 ledDescr.function = words.at(2);
188 ledDescr.color = words.at(1);
189 ledDescr.devicename = words.at(0);
190 break;
191 case 2:
192 ledDescr.color = words.at(0);
193 ledDescr.function = words.at(1);
194 break;
195 case 1:
196 ledDescr.devicename = words.at(0);
197 break;
198 case 0:
199 throw std::out_of_range("expected non-empty LED name");
200 }
201
202 // if there is more than 3 parts we ignore the rest
203 return ledDescr;
204}
Andrew Jefferye185efb2018-05-24 12:59:06 +0930205} // namespace led
206} // namespace phosphor