led.c revision 122963
1121941Sphk/*-
2121941Sphk * ----------------------------------------------------------------------------
3121941Sphk * "THE BEER-WARE LICENSE" (Revision 42):
4121941Sphk * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
5121941Sphk * can do whatever you want with this stuff. If we meet some day, and you think
6121941Sphk * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7121941Sphk * ----------------------------------------------------------------------------
8121941Sphk *
9121941Sphk */
10121941Sphk
11121941Sphk#include <sys/cdefs.h>
12121941Sphk__FBSDID("$FreeBSD: head/sys/dev/led/led.c 122963 2003-11-23 10:22:51Z phk $");
13121941Sphk
14121941Sphk#include <sys/param.h>
15121941Sphk#include <sys/conf.h>
16121941Sphk#include <sys/kernel.h>
17121941Sphk#include <sys/systm.h>
18121941Sphk#include <sys/malloc.h>
19121941Sphk#include <sys/ctype.h>
20121941Sphk#include <sys/sbuf.h>
21121941Sphk#include <sys/queue.h>
22121941Sphk#include <dev/led/led.h>
23121941Sphk#include <sys/uio.h>
24121941Sphk
25121941Sphkstruct ledsc {
26121941Sphk	LIST_ENTRY(ledsc)	list;
27121941Sphk	void			*private;
28121941Sphk	led_t			*func;
29121941Sphk	dev_t			dev;
30121941Sphk	struct sbuf		*spec;
31121941Sphk	char			*str;
32121941Sphk	char			*ptr;
33121941Sphk	int			count;
34121941Sphk};
35121941Sphk
36121941Sphkstatic unsigned next_minor;
37121941Sphkstatic struct mtx led_mtx;
38121941Sphkstatic LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(&led_list);
39121941Sphk
40121941SphkMALLOC_DEFINE(M_LED, "LED", "LED driver");
41121941Sphk
42121941Sphkstatic void
43121941Sphkled_timeout(void *p)
44121941Sphk{
45121941Sphk	struct ledsc	*sc;
46121941Sphk
47121941Sphk	mtx_lock(&led_mtx);
48121941Sphk	LIST_FOREACH(sc, &led_list, list) {
49121941Sphk		if (sc->ptr == NULL)
50121941Sphk			continue;
51121941Sphk		if (sc->count > 0) {
52121941Sphk			sc->count--;
53121941Sphk			continue;
54121941Sphk		}
55121941Sphk		sc->func(sc->private, sc->ptr[0] >= 'a' ? 1 : 0);
56121941Sphk		sc->count = sc->ptr[0] & 0xf;
57121941Sphk		if (*(++sc->ptr) == '\0')
58121941Sphk			sc->ptr = sc->str;
59121941Sphk	}
60121941Sphk	mtx_unlock(&led_mtx);
61121941Sphk	timeout(led_timeout, p, hz / 10);
62121941Sphk	return;
63121941Sphk}
64121941Sphk
65121941Sphkstatic int
66121941Sphkled_write(dev_t dev, struct uio *uio, int ioflag)
67121941Sphk{
68121941Sphk	int error;
69121941Sphk	char *s;
70121941Sphk	struct ledsc *sc;
71121941Sphk	struct sbuf *sb;
72121941Sphk	struct sbuf *sb2;
73121941Sphk	int i;
74121941Sphk
75121941Sphk	sc = dev->si_drv1;
76121941Sphk
77121941Sphk	if (uio->uio_resid > 512)
78121941Sphk		return (EINVAL);
79121941Sphk	s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
80121941Sphk	if (s == NULL)
81121941Sphk		return (ENOMEM);
82121941Sphk	s[uio->uio_resid] = '\0';
83121941Sphk	error = uiomove(s, uio->uio_resid, uio);
84121941Sphk	if (error) {
85121941Sphk		free(s, M_DEVBUF);
86121941Sphk		return (error);
87121941Sphk	}
88121941Sphk
89121941Sphk	/*
90121941Sphk	 * Handle "on" and "off" immediately so people can flash really
91121941Sphk	 * fast from userland if they want to
92121941Sphk	 */
93121941Sphk	if (*s == '0' || *s == '1') {
94121941Sphk		mtx_lock(&led_mtx);
95121941Sphk		sb2 = sc->spec;
96121941Sphk		sc->spec = NULL;
97121941Sphk		sc->str = NULL;
98121941Sphk		sc->ptr = NULL;
99121941Sphk		sc->count = 0;
100121941Sphk		sc->func(sc->private, *s & 1);
101121941Sphk		mtx_unlock(&led_mtx);
102121941Sphk		if (sb2 != NULL)
103121941Sphk			sbuf_delete(sb2);
104121941Sphk		free(s, M_DEVBUF);
105121941Sphk		return(0);
106121941Sphk	}
107121941Sphk
108121941Sphk	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
109121941Sphk	if (sb == NULL) {
110121941Sphk		free(s, M_DEVBUF);
111121941Sphk		return (ENOMEM);
112121941Sphk	}
113121941Sphk
114121941Sphk	switch(s[0]) {
115121941Sphk		/*
116121941Sphk		 * Flash, default is 100msec/100msec.
117121941Sphk		 * 'f2' sets 200msec/200msec etc.
118121941Sphk		 */
119121941Sphk		case 'f':
120121941Sphk			if (s[1] >= '1' && s[1] <= '9')
121121941Sphk				i = s[1] - '1';
122121941Sphk			else
123121941Sphk				i = 0;
124121941Sphk			sbuf_printf(sb, "%c%c", 'a' + i, 'A' + i);
125121941Sphk			break;
126121941Sphk		/*
127121941Sphk		 * Digits, flashes out numbers.
128121941Sphk		 * 'd12' becomes -__________-_-______________________________
129121941Sphk		 */
130121941Sphk		case 'd':
131121941Sphk			for(s++; *s; s++) {
132121941Sphk				if (!isdigit(*s))
133121941Sphk					continue;
134121941Sphk				i = *s - '0';
135121941Sphk				if (i == 0)
136121941Sphk					i = 10;
137121941Sphk				for (; i > 1; i--)
138121941Sphk					sbuf_cat(sb, "aA");
139121941Sphk				sbuf_cat(sb, "aJ");
140121941Sphk			}
141121941Sphk			sbuf_cat(sb, "JJ");
142121941Sphk			break;
143121941Sphk		/*
144121941Sphk		 * String, roll your own.
145121941Sphk		 * 'A-J' gives "off" for n/10 sec.
146121941Sphk		 * 'a-j' gives "on" for n/10 sec.
147121941Sphk		 * no delay before repeat
148121941Sphk		 * 'sAaAbBa' becomes _-_--__-
149121941Sphk		 */
150121941Sphk		case 's':
151121941Sphk			for(s++; *s; s++) {
152121941Sphk				if ((*s & 0x0f) > 10)
153121941Sphk					continue;
154121941Sphk				if ((*s & 0xf0) < ' ')
155121941Sphk					continue;
156121941Sphk				sbuf_bcat(sb, s, 1);
157121941Sphk			}
158121941Sphk			break;
159121941Sphk		/*
160121941Sphk		 * Morse.
161121941Sphk		 * '.' becomes _-
162121941Sphk		 * '-' becomes _---
163121941Sphk		 * ' ' becomes __
164121941Sphk		 * '\n' becomes ____
165121941Sphk		 * 1sec pause between repeats
166121941Sphk		 * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________
167121941Sphk		 */
168121941Sphk		case 'm':
169121941Sphk			for(s++; *s; s++) {
170121941Sphk				if (*s == '.')
171121941Sphk					sbuf_cat(sb, "Aa");
172121941Sphk				else if (*s == '-')
173121941Sphk					sbuf_cat(sb, "Ac");
174121941Sphk				else if (*s == ' ')
175121941Sphk					sbuf_cat(sb, "B");
176121956Sphk				else if (*s == '\n')
177121956Sphk					sbuf_cat(sb, "D");
178121941Sphk			}
179121941Sphk			sbuf_cat(sb, "J");
180121941Sphk			break;
181121941Sphk		default:
182121941Sphk			break;
183121941Sphk	}
184121941Sphk	sbuf_finish(sb);
185121941Sphk	free(s, M_DEVBUF);
186121941Sphk	if (sbuf_overflowed(sb)) {
187121941Sphk		sbuf_delete(sb);
188121941Sphk		return (ENOMEM);
189121941Sphk	}
190121941Sphk	if (sbuf_len(sb) == 0) {
191121941Sphk		sbuf_delete(sb);
192121941Sphk		return (0);
193121941Sphk	}
194121941Sphk
195121941Sphk	mtx_lock(&led_mtx);
196121941Sphk	sb2 = sc->spec;
197121941Sphk	sc->spec = sb;
198121941Sphk	sc->str = sbuf_data(sb);
199121941Sphk	sc->ptr = sc->str;
200121941Sphk	sc->count = 0;
201121941Sphk	mtx_unlock(&led_mtx);
202121941Sphk	if (sb2 != NULL)
203121941Sphk		sbuf_delete(sb2);
204121941Sphk	return(0);
205121941Sphk}
206121941Sphk
207121941Sphkstatic struct cdevsw led_cdevsw = {
208121941Sphk        .d_write =      led_write,
209121941Sphk        .d_name =       "LED",
210121941Sphk};
211121941Sphk
212121941Sphkdev_t
213121941Sphkled_create(led_t *func, void *priv, char const *name)
214121941Sphk{
215121941Sphk	struct ledsc	*sc;
216121941Sphk	struct sbuf *sb;
217121941Sphk
218121941Sphk	if (next_minor == 0) {
219121941Sphk		mtx_init(&led_mtx, "LED mtx", MTX_DEF, 0);
220121941Sphk		timeout(led_timeout, NULL, hz / 10);
221121941Sphk	}
222121941Sphk
223121941Sphk	sb = sbuf_new(NULL, NULL, SPECNAMELEN, SBUF_FIXEDLEN);
224121941Sphk	if (sb == NULL)
225121941Sphk		return (NODEV);
226121941Sphk	sbuf_cpy(sb, "led/");
227121941Sphk	sbuf_cat(sb, name);
228121941Sphk	sbuf_finish(sb);
229121941Sphk	if (sbuf_overflowed(sb)) {
230121941Sphk		sbuf_delete(sb);
231121941Sphk		return (NODEV);
232121941Sphk	}
233121941Sphk
234121941Sphk	sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO);
235121941Sphk	sc->private = priv;
236121941Sphk	sc->func = func;
237121941Sphk	sc->dev = make_dev(&led_cdevsw, unit2minor(next_minor),
238121941Sphk	    UID_ROOT, GID_WHEEL, 0600, sbuf_data(sb));
239121941Sphk	sc->dev->si_drv1 = sc;
240121941Sphk	next_minor++;
241121941Sphk	sbuf_delete(sb);
242121941Sphk	mtx_lock(&led_mtx);
243121941Sphk	LIST_INSERT_HEAD(&led_list, sc, list);
244121941Sphk	mtx_unlock(&led_mtx);
245121941Sphk	return (sc->dev);
246121941Sphk}
247121941Sphk
248121941Sphkvoid
249121941Sphkled_destroy(dev_t dev)
250121941Sphk{
251121941Sphk	struct ledsc *sc;
252121941Sphk
253121941Sphk	sc = dev->si_drv1;
254121941Sphk	mtx_lock(&led_mtx);
255121941Sphk	LIST_REMOVE(sc, list);
256121941Sphk	mtx_unlock(&led_mtx);
257122963Sphk	if (sc->spec != NULL)
258122963Sphk		sbuf_delete(sc->spec);
259121941Sphk	destroy_dev(dev);
260121941Sphk	free(sc, M_LED);
261121941Sphk}
262