led.c revision 130585
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 130585 2004-06-16 09:47:26Z 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;
29130585Sphk	struct cdev *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		}
55128678Sphk		if (*sc->ptr == '.') {
56128678Sphk			sc->ptr = NULL;
57128678Sphk			continue;
58128678Sphk		} else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') {
59123494Sphk			sc->func(sc->private, 0);
60128678Sphk		} else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') {
61123494Sphk			sc->func(sc->private, 1);
62128678Sphk		}
63123494Sphk		sc->count = *sc->ptr & 0xf;
64128867Sphk		sc->count--;
65123494Sphk		sc->ptr++;
66123494Sphk		if (*sc->ptr == '\0')
67121941Sphk			sc->ptr = sc->str;
68121941Sphk	}
69121941Sphk	mtx_unlock(&led_mtx);
70121941Sphk	timeout(led_timeout, p, hz / 10);
71121941Sphk	return;
72121941Sphk}
73121941Sphk
74121941Sphkstatic int
75130585Sphkled_write(struct cdev *dev, struct uio *uio, int ioflag)
76121941Sphk{
77121941Sphk	int error;
78123494Sphk	char *s, *s2;
79121941Sphk	struct ledsc *sc;
80121941Sphk	struct sbuf *sb;
81121941Sphk	struct sbuf *sb2;
82121941Sphk	int i;
83121941Sphk
84121941Sphk	sc = dev->si_drv1;
85121941Sphk
86121941Sphk	if (uio->uio_resid > 512)
87121941Sphk		return (EINVAL);
88123494Sphk	s2 = s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
89121941Sphk	if (s == NULL)
90121941Sphk		return (ENOMEM);
91121941Sphk	s[uio->uio_resid] = '\0';
92121941Sphk	error = uiomove(s, uio->uio_resid, uio);
93121941Sphk	if (error) {
94123494Sphk		free(s2, M_DEVBUF);
95121941Sphk		return (error);
96121941Sphk	}
97121941Sphk
98121941Sphk	/*
99121941Sphk	 * Handle "on" and "off" immediately so people can flash really
100121941Sphk	 * fast from userland if they want to
101121941Sphk	 */
102121941Sphk	if (*s == '0' || *s == '1') {
103121941Sphk		mtx_lock(&led_mtx);
104121941Sphk		sb2 = sc->spec;
105121941Sphk		sc->spec = NULL;
106121941Sphk		sc->str = NULL;
107121941Sphk		sc->ptr = NULL;
108121941Sphk		sc->count = 0;
109121941Sphk		sc->func(sc->private, *s & 1);
110121941Sphk		mtx_unlock(&led_mtx);
111121941Sphk		if (sb2 != NULL)
112121941Sphk			sbuf_delete(sb2);
113123494Sphk		free(s2, M_DEVBUF);
114121941Sphk		return(0);
115121941Sphk	}
116121941Sphk
117121941Sphk	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
118121941Sphk	if (sb == NULL) {
119123494Sphk		free(s2, M_DEVBUF);
120121941Sphk		return (ENOMEM);
121121941Sphk	}
122121941Sphk
123121941Sphk	switch(s[0]) {
124121941Sphk		/*
125121941Sphk		 * Flash, default is 100msec/100msec.
126121941Sphk		 * 'f2' sets 200msec/200msec etc.
127121941Sphk		 */
128121941Sphk		case 'f':
129121941Sphk			if (s[1] >= '1' && s[1] <= '9')
130121941Sphk				i = s[1] - '1';
131121941Sphk			else
132121941Sphk				i = 0;
133123014Sphk			sbuf_printf(sb, "%c%c", 'A' + i, 'a' + i);
134121941Sphk			break;
135121941Sphk		/*
136121941Sphk		 * Digits, flashes out numbers.
137121941Sphk		 * 'd12' becomes -__________-_-______________________________
138121941Sphk		 */
139121941Sphk		case 'd':
140121941Sphk			for(s++; *s; s++) {
141121941Sphk				if (!isdigit(*s))
142121941Sphk					continue;
143121941Sphk				i = *s - '0';
144121941Sphk				if (i == 0)
145121941Sphk					i = 10;
146121941Sphk				for (; i > 1; i--)
147123014Sphk					sbuf_cat(sb, "Aa");
148123014Sphk				sbuf_cat(sb, "Aj");
149121941Sphk			}
150123014Sphk			sbuf_cat(sb, "jj");
151121941Sphk			break;
152121941Sphk		/*
153121941Sphk		 * String, roll your own.
154123014Sphk		 * 'a-j' gives "off" for n/10 sec.
155123014Sphk		 * 'A-J' gives "on" for n/10 sec.
156121941Sphk		 * no delay before repeat
157121941Sphk		 * 'sAaAbBa' becomes _-_--__-
158121941Sphk		 */
159121941Sphk		case 's':
160121941Sphk			for(s++; *s; s++) {
161128678Sphk				if ((*s >= 'a' && *s <= 'j') ||
162128678Sphk				    (*s >= 'A' && *s <= 'J') ||
163128678Sphk					*s == '.')
164128678Sphk					sbuf_bcat(sb, s, 1);
165121941Sphk			}
166121941Sphk			break;
167121941Sphk		/*
168121941Sphk		 * Morse.
169121941Sphk		 * '.' becomes _-
170121941Sphk		 * '-' becomes _---
171121941Sphk		 * ' ' becomes __
172121941Sphk		 * '\n' becomes ____
173121941Sphk		 * 1sec pause between repeats
174121941Sphk		 * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________
175121941Sphk		 */
176121941Sphk		case 'm':
177121941Sphk			for(s++; *s; s++) {
178121941Sphk				if (*s == '.')
179123014Sphk					sbuf_cat(sb, "aA");
180121941Sphk				else if (*s == '-')
181123014Sphk					sbuf_cat(sb, "aC");
182121941Sphk				else if (*s == ' ')
183123014Sphk					sbuf_cat(sb, "b");
184121956Sphk				else if (*s == '\n')
185123014Sphk					sbuf_cat(sb, "d");
186121941Sphk			}
187123014Sphk			sbuf_cat(sb, "j");
188121941Sphk			break;
189121941Sphk		default:
190123494Sphk			sbuf_delete(sb);
191123494Sphk			free(s2, M_DEVBUF);
192123494Sphk			return (EINVAL);
193121941Sphk	}
194121941Sphk	sbuf_finish(sb);
195123494Sphk	free(s2, M_DEVBUF);
196121941Sphk	if (sbuf_overflowed(sb)) {
197121941Sphk		sbuf_delete(sb);
198121941Sphk		return (ENOMEM);
199121941Sphk	}
200121941Sphk	if (sbuf_len(sb) == 0) {
201121941Sphk		sbuf_delete(sb);
202121941Sphk		return (0);
203121941Sphk	}
204121941Sphk
205121941Sphk	mtx_lock(&led_mtx);
206121941Sphk	sb2 = sc->spec;
207121941Sphk	sc->spec = sb;
208121941Sphk	sc->str = sbuf_data(sb);
209121941Sphk	sc->ptr = sc->str;
210121941Sphk	sc->count = 0;
211121941Sphk	mtx_unlock(&led_mtx);
212121941Sphk	if (sb2 != NULL)
213121941Sphk		sbuf_delete(sb2);
214121941Sphk	return(0);
215121941Sphk}
216121941Sphk
217121941Sphkstatic struct cdevsw led_cdevsw = {
218126080Sphk	.d_version =	D_VERSION,
219126080Sphk	.d_flags =	D_NEEDGIANT,
220125810Sphk	.d_write =	led_write,
221125810Sphk	.d_name =	"LED",
222121941Sphk};
223121941Sphk
224130585Sphkstruct cdev *
225121941Sphkled_create(led_t *func, void *priv, char const *name)
226121941Sphk{
227121941Sphk	struct ledsc	*sc;
228121941Sphk	struct sbuf *sb;
229121941Sphk
230121941Sphk	if (next_minor == 0) {
231123272Struckman		mtx_init(&led_mtx, "LED mtx", NULL, MTX_DEF);
232121941Sphk		timeout(led_timeout, NULL, hz / 10);
233121941Sphk	}
234121941Sphk
235121941Sphk	sb = sbuf_new(NULL, NULL, SPECNAMELEN, SBUF_FIXEDLEN);
236121941Sphk	if (sb == NULL)
237121941Sphk		return (NODEV);
238121941Sphk	sbuf_cpy(sb, "led/");
239121941Sphk	sbuf_cat(sb, name);
240121941Sphk	sbuf_finish(sb);
241121941Sphk	if (sbuf_overflowed(sb)) {
242121941Sphk		sbuf_delete(sb);
243121941Sphk		return (NODEV);
244121941Sphk	}
245121941Sphk
246121941Sphk	sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO);
247121941Sphk	sc->private = priv;
248121941Sphk	sc->func = func;
249121941Sphk	sc->dev = make_dev(&led_cdevsw, unit2minor(next_minor),
250121941Sphk	    UID_ROOT, GID_WHEEL, 0600, sbuf_data(sb));
251121941Sphk	sc->dev->si_drv1 = sc;
252121941Sphk	next_minor++;
253121941Sphk	sbuf_delete(sb);
254121941Sphk	mtx_lock(&led_mtx);
255121941Sphk	LIST_INSERT_HEAD(&led_list, sc, list);
256128678Sphk	sc->func(sc->private, 0);
257121941Sphk	mtx_unlock(&led_mtx);
258121941Sphk	return (sc->dev);
259121941Sphk}
260121941Sphk
261121941Sphkvoid
262130585Sphkled_destroy(struct cdev *dev)
263121941Sphk{
264121941Sphk	struct ledsc *sc;
265121941Sphk
266121941Sphk	sc = dev->si_drv1;
267121941Sphk	mtx_lock(&led_mtx);
268121941Sphk	LIST_REMOVE(sc, list);
269121941Sphk	mtx_unlock(&led_mtx);
270122963Sphk	if (sc->spec != NULL)
271122963Sphk		sbuf_delete(sc->spec);
272121941Sphk	destroy_dev(dev);
273121941Sphk	free(sc, M_LED);
274121941Sphk}
275