xref: /openbmc/phosphor-led-sysfs/sysfs.cpp (revision a16bef7d)
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