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