1 /**
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
19 #include "phosphor-logging/lg2.hpp"
20
21 #include <cstring>
22 #include <fstream>
23 #include <optional>
24 #include <string>
25 #include <vector>
26
27 namespace fs = std::filesystem;
28
29 namespace phosphor
30 {
31 namespace led
32 {
33 template <typename T>
34 T getSysfsAttr(const fs::path& path);
35
36 template <>
getSysfsAttr(const fs::path & path)37 std::string getSysfsAttr(const fs::path& path)
38 {
39 std::string content;
40 std::ifstream file(path);
41 std::getline(file, content);
42 return content;
43 }
44
45 template <>
getSysfsAttr(const fs::path & path)46 unsigned long getSysfsAttr(const fs::path& path)
47 {
48 std::string content = getSysfsAttr<std::string>(path);
49 return std::strtoul(content.c_str(), nullptr, 0);
50 }
51
52 template <typename T>
setSysfsAttr(const fs::path & path,const T & value)53 void setSysfsAttr(const fs::path& path, const T& value)
54 {
55 std::ofstream file(path);
56 file << value;
57 }
58
getBrightness()59 unsigned long SysfsLed::getBrightness()
60 {
61 return getSysfsAttr<unsigned long>(root / attrBrightness);
62 }
63
setBrightness(unsigned long brightness)64 void SysfsLed::setBrightness(unsigned long brightness)
65 {
66 setSysfsAttr<unsigned long>(root / attrBrightness, brightness);
67 }
68
getMaxBrightness()69 unsigned long SysfsLed::getMaxBrightness()
70 {
71 return getSysfsAttr<unsigned long>(root / attrMaxBrightness);
72 }
73
getTrigger()74 std::string SysfsLed::getTrigger()
75 {
76 // 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;
100 }
101
setTrigger(const std::string & trigger)102 void SysfsLed::setTrigger(const std::string& trigger)
103 {
104 setSysfsAttr<std::string>(root / attrTrigger, trigger);
105 }
106
getDelayOn()107 unsigned long SysfsLed::getDelayOn()
108 {
109 return getSysfsAttr<unsigned long>(root / attrDelayOn);
110 }
111
setDelayOn(unsigned long ms)112 void SysfsLed::setDelayOn(unsigned long ms)
113 {
114 setSysfsAttr<unsigned long>(root / attrDelayOn, ms);
115 }
116
getDelayOff()117 unsigned long SysfsLed::getDelayOff()
118 {
119 return getSysfsAttr<unsigned long>(root / attrDelayOff);
120 }
121
setDelayOff(unsigned long ms)122 void SysfsLed::setDelayOff(unsigned long ms)
123 {
124 setSysfsAttr<unsigned long>(root / attrDelayOff, ms);
125 }
126
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 */
getLedDescr()143 LedDescr 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 }
204 } // namespace led
205 } // namespace phosphor
206