1 /*
2 * ser2net - A program for allowing telnet connection to serial ports
3 * Copyright (C) 2001-2016 Corey Minyard <minyard@acm.org>
4 * Copyright (C) 2015 I2SE GmbH <info@i2se.com>
5 * Copyright (C) 2016 Michael Heimpold <mhei@heimpold.de>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 /* This file contains a LED driver for Linux's sysfs based LEDs. */
23
24 #ifdef USE_SYSFS_LED_FEATURE
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <sys/types.h>
31 #include <syslog.h>
32 #include "led.h"
33
34 #define SYSFS_LED_BASE "/sys/class/leds"
35
36 #define BUFSIZE 4096
37
38 struct led_sysfs_s
39 {
40 char *device;
41 int state;
42 int duration;
43 };
44
45 static int
led_is_trigger_missing(const char * led)46 led_is_trigger_missing(const char *led)
47 {
48 char *buffer, *trigger;
49 int fd, c;
50
51 buffer = malloc(BUFSIZE);
52 if (!buffer)
53 return -1;
54
55 snprintf(buffer, BUFSIZE, "%s/%s/trigger", SYSFS_LED_BASE, led);
56
57 if ((fd = open(buffer, O_RDONLY)) == -1) {
58 free(buffer);
59 return -1;
60 }
61
62 if ((c = read(fd, buffer, BUFSIZE)) <= 0) {
63 free(buffer);
64 close(fd);
65 return -1;
66 }
67
68 if (close(fd) < 0) {
69 free(buffer);
70 return -1;
71 }
72
73 buffer[c] = '\0';
74 trigger = strstr(buffer, "transient");
75 free(buffer);
76 return trigger == NULL;
77 }
78
79 static int
led_write(const char * led,const char * property,const char * buf)80 led_write(const char *led, const char *property, const char *buf)
81 {
82 char filename[255];
83 int fd;
84
85 snprintf(filename, sizeof(filename), "%s/%s/%s", SYSFS_LED_BASE, led, property);
86
87 if ((fd = open(filename, O_WRONLY | O_TRUNC)) == -1)
88 return -1;
89
90 if (write(fd, buf, strlen(buf)) != strlen(buf)) {
91 close(fd);
92 return -1;
93 }
94
95 return close(fd);
96 }
97
98 static int
led_sysfs_init(struct led_s * led,char * parameters,int lineno)99 led_sysfs_init(struct led_s *led, char *parameters, int lineno)
100 {
101 struct led_sysfs_s *drv_data = NULL;
102 char *str1, *str2, *token, *subtoken;
103 char *saveptr1, *saveptr2;
104 char *key, *value;
105 int i;
106
107 drv_data = calloc(1, sizeof(*drv_data));
108 if (!drv_data) {
109 syslog(LOG_ERR,
110 "Out of memory handling LED %s on line %d.",
111 led->name, lineno);
112 return -1;
113 }
114
115 /* preset to detect default and/or wrong user input */
116 drv_data->state = -1;
117
118 /* parse parameter key=value pairs - seperated by whitespace */
119 for (str1 = parameters; ; str1 = NULL) {
120 token = strtok_r(str1, " \t", &saveptr1);
121 if (!token)
122 break;
123
124 /* parse single key=value pair */
125 for (i = 0, str2 = token; ; i++, str2 = NULL) {
126 subtoken = strtok_r(str2, "=", &saveptr2);
127 if (!subtoken)
128 break;
129
130 if (i == 0)
131 key = subtoken;
132
133 if (i == 1) {
134 value = subtoken;
135
136 if (strcasecmp(key, "device") == 0) {
137 /* if 'device' is given more than once, last wins */
138 if (drv_data->device)
139 free(drv_data->device);
140
141 drv_data->device = strdup(value);
142 if (!drv_data->device) {
143 syslog(LOG_ERR,
144 "Out of memory handling LED '%s' on line %d.",
145 led->name, lineno);
146 return -1;
147 }
148 }
149
150 if (strcasecmp(key, "duration") == 0)
151 drv_data->duration = atoi(value);
152
153 if (strcasecmp(key, "state") == 0)
154 drv_data->state = atoi(value);
155 }
156 }
157 }
158
159 if (!drv_data->device) {
160 syslog(LOG_ERR,
161 "LED '%s': parameter 'device' required, but missing on line %d.",
162 led->name, lineno);
163 free(drv_data);
164 return -1;
165 }
166
167 if (drv_data->duration < 0) {
168 syslog(LOG_ERR,
169 "LED '%s': invalid duration, using default on line %d.",
170 led->name, lineno);
171 drv_data->duration = 10;
172 }
173 if (drv_data->duration == 0)
174 drv_data->duration = 10;
175
176
177 if (drv_data->state == -1)
178 drv_data->state = 1;
179 if (drv_data->state < 0 || drv_data->state > 1) {
180 syslog(LOG_ERR,
181 "LED '%s': invalid state, using default on line %d.",
182 led->name, lineno);
183 drv_data->state = 1;
184 }
185
186 led->drv_data = (void *)drv_data;
187
188 return 0;
189 }
190
191 static int
led_sysfs_free(struct led_s * led)192 led_sysfs_free(struct led_s *led)
193 {
194 struct led_sysfs_s *ctx = (struct led_sysfs_s *)led->drv_data;
195
196 free(ctx->device);
197 free(ctx);
198
199 led->drv_data = NULL;
200 return 0;
201 }
202
203 static int
led_sysfs_configure(void * led_driver_data)204 led_sysfs_configure(void *led_driver_data)
205 {
206 struct led_sysfs_s *ctx = (struct led_sysfs_s *)led_driver_data;
207 char buffer[255];
208 int rv = 0;
209
210 /* check whether we can enable the transient trigger for this led */
211 rv = led_is_trigger_missing(ctx->device);
212 if (rv != 0)
213 return rv;
214
215 /*
216 * switch to transient trigger, this will kick creation of additional
217 * property file in sysfs
218 */
219 rv = led_write(ctx->device, "trigger", "transient");
220 if (rv)
221 return rv;
222
223 /* pre-configure the trigger for our needs */
224 snprintf(buffer, sizeof(buffer), "%d", ctx->duration);
225 rv |= led_write(ctx->device, "duration", buffer);
226
227 snprintf(buffer, sizeof(buffer), "%d", ctx->state);
228 rv |= led_write(ctx->device, "state", buffer);
229
230 return rv;
231 }
232
233 static int
led_sysfs_flash(void * led_driver_data)234 led_sysfs_flash(void *led_driver_data)
235 {
236 struct led_sysfs_s *ctx = (struct led_sysfs_s *)led_driver_data;
237
238 return led_write(ctx->device, "activate", "1");
239 }
240
241 static int
led_sysfs_deconfigure(void * led_driver_data)242 led_sysfs_deconfigure(void *led_driver_data)
243 {
244 struct led_sysfs_s *ctx = (struct led_sysfs_s *)led_driver_data;
245 int rv = 0;
246
247
248 rv |= led_write(ctx->device, "trigger", "none");
249 rv |= led_write(ctx->device, "brightness", "0");
250
251 return rv;
252 }
253
254 static struct led_driver_s led_sysfs_driver = {
255 .name = "sysfs",
256
257 .init = led_sysfs_init,
258 .free = led_sysfs_free,
259
260 .configure = led_sysfs_configure,
261 .flash = led_sysfs_flash,
262 .deconfigure = led_sysfs_deconfigure,
263 };
264
265 int
led_sysfs_register(void)266 led_sysfs_register(void)
267 {
268 return led_driver_register(&led_sysfs_driver);
269 }
270 #else
271 int
led_sysfs_register(void)272 led_sysfs_register(void)
273 {
274 return 0;
275 }
276 #endif
277