]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/led/led.c
Merge libc++ trunk r351319, and resolve conflicts.
[FreeBSD/FreeBSD.git] / sys / dev / led / led.c
1 /*-
2  * SPDX-License-Identifier: Beerware
3  *
4  * ----------------------------------------------------------------------------
5  * "THE BEER-WARE LICENSE" (Revision 42):
6  * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
7  * can do whatever you want with this stuff. If we meet some day, and you think
8  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
9  * ----------------------------------------------------------------------------
10  *
11  */
12
13 #include <sys/cdefs.h>
14 __FBSDID("$FreeBSD$");
15
16 #include <sys/param.h>
17 #include <sys/conf.h>
18 #include <sys/kernel.h>
19 #include <sys/systm.h>
20 #include <sys/limits.h>
21 #include <sys/malloc.h>
22 #include <sys/ctype.h>
23 #include <sys/sbuf.h>
24 #include <sys/queue.h>
25 #include <dev/led/led.h>
26 #include <sys/uio.h>
27 #include <sys/sx.h>
28
29 struct ledsc {
30         LIST_ENTRY(ledsc)       list;
31         char                    *name;
32         void                    *private;
33         int                     unit;
34         led_t                   *func;
35         struct cdev *dev;
36         struct sbuf             *spec;
37         char                    *str;
38         char                    *ptr;
39         int                     count;
40         time_t                  last_second;
41 };
42
43 static struct unrhdr *led_unit;
44 static struct mtx led_mtx;
45 static struct sx led_sx;
46 static LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(led_list);
47 static struct callout led_ch;
48 static int blinkers = 0;
49
50 static MALLOC_DEFINE(M_LED, "LED", "LED driver");
51
52 static void
53 led_timeout(void *p)
54 {
55         struct ledsc    *sc;
56
57         LIST_FOREACH(sc, &led_list, list) {
58                 if (sc->ptr == NULL)
59                         continue;
60                 if (sc->count > 0) {
61                         sc->count--;
62                         continue;
63                 }
64                 if (*sc->ptr == '.') {
65                         sc->ptr = NULL;
66                         blinkers--;
67                         continue;
68                 } else if (*sc->ptr == 'U' || *sc->ptr == 'u') {
69                         if (sc->last_second == time_second)
70                                 continue;
71                         sc->last_second = time_second;
72                         sc->func(sc->private, *sc->ptr == 'U');
73                 } else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') {
74                         sc->func(sc->private, 0);
75                         sc->count = (*sc->ptr & 0xf) - 1;
76                 } else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') {
77                         sc->func(sc->private, 1);
78                         sc->count = (*sc->ptr & 0xf) - 1;
79                 }
80                 sc->ptr++;
81                 if (*sc->ptr == '\0')
82                         sc->ptr = sc->str;
83         }
84         if (blinkers > 0)
85                 callout_reset(&led_ch, hz / 10, led_timeout, p);
86 }
87
88 static int
89 led_state(struct ledsc *sc, struct sbuf **sb, int state)
90 {
91         struct sbuf *sb2 = NULL;
92
93         sb2 = sc->spec;
94         sc->spec = *sb;
95         if (*sb != NULL) {
96                 sc->str = sbuf_data(*sb);
97                 if (sc->ptr == NULL) {
98                         blinkers++;
99                         callout_reset(&led_ch, hz / 10, led_timeout, NULL);
100                 }
101                 sc->ptr = sc->str;
102         } else {
103                 sc->str = NULL;
104                 if (sc->ptr != NULL)
105                         blinkers--;
106                 sc->ptr = NULL;
107                 sc->func(sc->private, state);
108         }
109         sc->count = 0;
110         *sb = sb2;
111         return(0);
112 }
113
114 static int
115 led_parse(const char *s, struct sbuf **sb, int *state)
116 {
117         int i, error;
118
119         /*
120          * Handle "on" and "off" immediately so people can flash really
121          * fast from userland if they want to
122          */
123         if (*s == '0' || *s == '1') {
124                 *state = *s & 1;
125                 return (0);
126         }
127
128         *state = 0;
129         *sb = sbuf_new_auto();
130         if (*sb == NULL)
131                 return (ENOMEM);
132         switch(s[0]) {
133                 /*
134                  * Flash, default is 100msec/100msec.
135                  * 'f2' sets 200msec/200msec etc.
136                  */
137                 case 'f':
138                         if (s[1] >= '1' && s[1] <= '9')
139                                 i = s[1] - '1';
140                         else
141                                 i = 0;
142                         sbuf_printf(*sb, "%c%c", 'A' + i, 'a' + i);
143                         break;
144                 /*
145                  * Digits, flashes out numbers.
146                  * 'd12' becomes -__________-_-______________________________
147                  */
148                 case 'd':
149                         for(s++; *s; s++) {
150                                 if (!isdigit(*s))
151                                         continue;
152                                 i = *s - '0';
153                                 if (i == 0)
154                                         i = 10;
155                                 for (; i > 1; i--) 
156                                         sbuf_cat(*sb, "Aa");
157                                 sbuf_cat(*sb, "Aj");
158                         }
159                         sbuf_cat(*sb, "jj");
160                         break;
161                 /*
162                  * String, roll your own.
163                  * 'a-j' gives "off" for n/10 sec.
164                  * 'A-J' gives "on" for n/10 sec.
165                  * no delay before repeat
166                  * 'sAaAbBa' becomes _-_--__-
167                  */
168                 case 's':
169                         for(s++; *s; s++) {
170                                 if ((*s >= 'a' && *s <= 'j') ||
171                                     (*s >= 'A' && *s <= 'J') ||
172                                     *s == 'U' || *s <= 'u' ||
173                                         *s == '.')
174                                         sbuf_bcat(*sb, s, 1);
175                         }
176                         break;
177                 /*
178                  * Morse.
179                  * '.' becomes _-
180                  * '-' becomes _---
181                  * ' ' becomes __
182                  * '\n' becomes ____
183                  * 1sec pause between repeats
184                  * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________
185                  */
186                 case 'm':
187                         for(s++; *s; s++) {
188                                 if (*s == '.')
189                                         sbuf_cat(*sb, "aA");
190                                 else if (*s == '-')
191                                         sbuf_cat(*sb, "aC");
192                                 else if (*s == ' ')
193                                         sbuf_cat(*sb, "b");
194                                 else if (*s == '\n')
195                                         sbuf_cat(*sb, "d");
196                         }
197                         sbuf_cat(*sb, "j");
198                         break;
199                 default:
200                         sbuf_delete(*sb);
201                         return (EINVAL);
202         }
203         error = sbuf_finish(*sb);
204         if (error != 0 || sbuf_len(*sb) == 0) {
205                 *sb = NULL;
206                 return (error);
207         }
208         return (0);
209 }
210
211 static int
212 led_write(struct cdev *dev, struct uio *uio, int ioflag)
213 {
214         struct ledsc    *sc;
215         char *s;
216         struct sbuf *sb = NULL;
217         int error, state = 0;
218
219         if (uio->uio_resid > 512)
220                 return (EINVAL);
221         s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
222         s[uio->uio_resid] = '\0';
223         error = uiomove(s, uio->uio_resid, uio);
224         if (error) {
225                 free(s, M_DEVBUF);
226                 return (error);
227         }
228         error = led_parse(s, &sb, &state);
229         free(s, M_DEVBUF);
230         if (error)
231                 return (error);
232         mtx_lock(&led_mtx);
233         sc = dev->si_drv1;
234         if (sc != NULL)
235                 error = led_state(sc, &sb, state);
236         mtx_unlock(&led_mtx);
237         if (sb != NULL)
238                 sbuf_delete(sb);
239         return (error);
240 }
241
242 int
243 led_set(char const *name, char const *cmd)
244 {
245         struct ledsc    *sc;
246         struct sbuf *sb = NULL;
247         int error, state = 0;
248
249         error = led_parse(cmd, &sb, &state);
250         if (error)
251                 return (error);
252         mtx_lock(&led_mtx);
253         LIST_FOREACH(sc, &led_list, list) {
254                 if (strcmp(sc->name, name) == 0)
255                         break;
256         }
257         if (sc != NULL)
258                 error = led_state(sc, &sb, state);
259         else
260                 error = ENOENT;
261         mtx_unlock(&led_mtx);
262         if (sb != NULL)
263                 sbuf_delete(sb);
264         return (error);
265 }
266
267 static struct cdevsw led_cdevsw = {
268         .d_version =    D_VERSION,
269         .d_write =      led_write,
270         .d_name =       "LED",
271 };
272
273 struct cdev *
274 led_create(led_t *func, void *priv, char const *name)
275 {
276
277         return (led_create_state(func, priv, name, 0));
278 }
279 struct cdev *
280 led_create_state(led_t *func, void *priv, char const *name, int state)
281 {
282         struct ledsc    *sc;
283
284         sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO);
285
286         sx_xlock(&led_sx);
287         sc->name = strdup(name, M_LED);
288         sc->unit = alloc_unr(led_unit);
289         sc->private = priv;
290         sc->func = func;
291         sc->dev = make_dev(&led_cdevsw, sc->unit,
292             UID_ROOT, GID_WHEEL, 0600, "led/%s", name);
293         sx_xunlock(&led_sx);
294
295         mtx_lock(&led_mtx);
296         sc->dev->si_drv1 = sc;
297         LIST_INSERT_HEAD(&led_list, sc, list);
298         if (state != -1)
299                 sc->func(sc->private, state != 0);
300         mtx_unlock(&led_mtx);
301
302         return (sc->dev);
303 }
304
305 void
306 led_destroy(struct cdev *dev)
307 {
308         struct ledsc *sc;
309
310         mtx_lock(&led_mtx);
311         sc = dev->si_drv1;
312         dev->si_drv1 = NULL;
313         if (sc->ptr != NULL)
314                 blinkers--;
315         LIST_REMOVE(sc, list);
316         if (LIST_EMPTY(&led_list))
317                 callout_stop(&led_ch);
318         mtx_unlock(&led_mtx);
319
320         sx_xlock(&led_sx);
321         free_unr(led_unit, sc->unit);
322         destroy_dev(dev);
323         if (sc->spec != NULL)
324                 sbuf_delete(sc->spec);
325         free(sc->name, M_LED);
326         free(sc, M_LED);
327         sx_xunlock(&led_sx);
328 }
329
330 static void
331 led_drvinit(void *unused)
332 {
333
334         led_unit = new_unrhdr(0, INT_MAX, NULL);
335         mtx_init(&led_mtx, "LED mtx", NULL, MTX_DEF);
336         sx_init(&led_sx, "LED sx");
337         callout_init_mtx(&led_ch, &led_mtx, 0);
338 }
339
340 SYSINIT(leddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, led_drvinit, NULL);