led.c revision 125810
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 125810 2004-02-14 20:03:36Z 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		}
55123494Sphk		if (*sc->ptr >= 'a' && *sc->ptr <= 'j')
56123494Sphk			sc->func(sc->private, 0);
57123494Sphk		else if (*sc->ptr >= 'A' && *sc->ptr <= 'J')
58123494Sphk			sc->func(sc->private, 1);
59123494Sphk		sc->count = *sc->ptr & 0xf;
60123494Sphk		sc->ptr++;
61123494Sphk		if (*sc->ptr == '\0')
62121941Sphk			sc->ptr = sc->str;
63121941Sphk	}
64121941Sphk	mtx_unlock(&led_mtx);
65121941Sphk	timeout(led_timeout, p, hz / 10);
66121941Sphk	return;
67121941Sphk}
68121941Sphk
69121941Sphkstatic int
70121941Sphkled_write(dev_t dev, struct uio *uio, int ioflag)
71121941Sphk{
72121941Sphk	int error;
73123494Sphk	char *s, *s2;
74121941Sphk	struct ledsc *sc;
75121941Sphk	struct sbuf *sb;
76121941Sphk	struct sbuf *sb2;
77121941Sphk	int i;
78121941Sphk
79121941Sphk	sc = dev->si_drv1;
80121941Sphk
81121941Sphk	if (uio->uio_resid > 512)
82121941Sphk		return (EINVAL);
83123494Sphk	s2 = s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
84121941Sphk	if (s == NULL)
85121941Sphk		return (ENOMEM);
86121941Sphk	s[uio->uio_resid] = '\0';
87121941Sphk	error = uiomove(s, uio->uio_resid, uio);
88121941Sphk	if (error) {
89123494Sphk		free(s2, M_DEVBUF);
90121941Sphk		return (error);
91121941Sphk	}
92121941Sphk
93121941Sphk	/*
94121941Sphk	 * Handle "on" and "off" immediately so people can flash really
95121941Sphk	 * fast from userland if they want to
96121941Sphk	 */
97121941Sphk	if (*s == '0' || *s == '1') {
98121941Sphk		mtx_lock(&led_mtx);
99121941Sphk		sb2 = sc->spec;
100121941Sphk		sc->spec = NULL;
101121941Sphk		sc->str = NULL;
102121941Sphk		sc->ptr = NULL;
103121941Sphk		sc->count = 0;
104121941Sphk		sc->func(sc->private, *s & 1);
105121941Sphk		mtx_unlock(&led_mtx);
106121941Sphk		if (sb2 != NULL)
107121941Sphk			sbuf_delete(sb2);
108123494Sphk		free(s2, M_DEVBUF);
109121941Sphk		return(0);
110121941Sphk	}
111121941Sphk
112121941Sphk	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
113121941Sphk	if (sb == NULL) {
114123494Sphk		free(s2, M_DEVBUF);
115121941Sphk		return (ENOMEM);
116121941Sphk	}
117121941Sphk
118121941Sphk	switch(s[0]) {
119121941Sphk		/*
120121941Sphk		 * Flash, default is 100msec/100msec.
121121941Sphk		 * 'f2' sets 200msec/200msec etc.
122121941Sphk		 */
123121941Sphk		case 'f':
124121941Sphk			if (s[1] >= '1' && s[1] <= '9')
125121941Sphk				i = s[1] - '1';
126121941Sphk			else
127121941Sphk				i = 0;
128123014Sphk			sbuf_printf(sb, "%c%c", 'A' + i, 'a' + i);
129121941Sphk			break;
130121941Sphk		/*
131121941Sphk		 * Digits, flashes out numbers.
132121941Sphk		 * 'd12' becomes -__________-_-______________________________
133121941Sphk		 */
134121941Sphk		case 'd':
135121941Sphk			for(s++; *s; s++) {
136121941Sphk				if (!isdigit(*s))
137121941Sphk					continue;
138121941Sphk				i = *s - '0';
139121941Sphk				if (i == 0)
140121941Sphk					i = 10;
141121941Sphk				for (; i > 1; i--)
142123014Sphk					sbuf_cat(sb, "Aa");
143123014Sphk				sbuf_cat(sb, "Aj");
144121941Sphk			}
145123014Sphk			sbuf_cat(sb, "jj");
146121941Sphk			break;
147121941Sphk		/*
148121941Sphk		 * String, roll your own.
149123014Sphk		 * 'a-j' gives "off" for n/10 sec.
150123014Sphk		 * 'A-J' gives "on" for n/10 sec.
151121941Sphk		 * no delay before repeat
152121941Sphk		 * 'sAaAbBa' becomes _-_--__-
153121941Sphk		 */
154121941Sphk		case 's':
155121941Sphk			for(s++; *s; s++) {
156121941Sphk				if ((*s & 0x0f) > 10)
157121941Sphk					continue;
158121941Sphk				if ((*s & 0xf0) < ' ')
159121941Sphk					continue;
160121941Sphk				sbuf_bcat(sb, s, 1);
161121941Sphk			}
162121941Sphk			break;
163121941Sphk		/*
164121941Sphk		 * Morse.
165121941Sphk		 * '.' becomes _-
166121941Sphk		 * '-' becomes _---
167121941Sphk		 * ' ' becomes __
168121941Sphk		 * '\n' becomes ____
169121941Sphk		 * 1sec pause between repeats
170121941Sphk		 * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________
171121941Sphk		 */
172121941Sphk		case 'm':
173121941Sphk			for(s++; *s; s++) {
174121941Sphk				if (*s == '.')
175123014Sphk					sbuf_cat(sb, "aA");
176121941Sphk				else if (*s == '-')
177123014Sphk					sbuf_cat(sb, "aC");
178121941Sphk				else if (*s == ' ')
179123014Sphk					sbuf_cat(sb, "b");
180121956Sphk				else if (*s == '\n')
181123014Sphk					sbuf_cat(sb, "d");
182121941Sphk			}
183123014Sphk			sbuf_cat(sb, "j");
184121941Sphk			break;
185121941Sphk		default:
186123494Sphk			sbuf_delete(sb);
187123494Sphk			free(s2, M_DEVBUF);
188123494Sphk			return (EINVAL);
189121941Sphk	}
190121941Sphk	sbuf_finish(sb);
191123494Sphk	free(s2, M_DEVBUF);
192121941Sphk	if (sbuf_overflowed(sb)) {
193121941Sphk		sbuf_delete(sb);
194121941Sphk		return (ENOMEM);
195121941Sphk	}
196121941Sphk	if (sbuf_len(sb) == 0) {
197121941Sphk		sbuf_delete(sb);
198121941Sphk		return (0);
199121941Sphk	}
200121941Sphk
201121941Sphk	mtx_lock(&led_mtx);
202121941Sphk	sb2 = sc->spec;
203121941Sphk	sc->spec = sb;
204121941Sphk	sc->str = sbuf_data(sb);
205121941Sphk	sc->ptr = sc->str;
206121941Sphk	sc->count = 0;
207121941Sphk	mtx_unlock(&led_mtx);
208121941Sphk	if (sb2 != NULL)
209121941Sphk		sbuf_delete(sb2);
210121941Sphk	return(0);
211121941Sphk}
212121941Sphk
213121941Sphkstatic struct cdevsw led_cdevsw = {
214125810Sphk	.d_write =	led_write,
215125810Sphk	.d_name =	"LED",
216121941Sphk};
217121941Sphk
218121941Sphkdev_t
219121941Sphkled_create(led_t *func, void *priv, char const *name)
220121941Sphk{
221121941Sphk	struct ledsc	*sc;
222121941Sphk	struct sbuf *sb;
223121941Sphk
224121941Sphk	if (next_minor == 0) {
225123272Struckman		mtx_init(&led_mtx, "LED mtx", NULL, MTX_DEF);
226121941Sphk		timeout(led_timeout, NULL, hz / 10);
227121941Sphk	}
228121941Sphk
229121941Sphk	sb = sbuf_new(NULL, NULL, SPECNAMELEN, SBUF_FIXEDLEN);
230121941Sphk	if (sb == NULL)
231121941Sphk		return (NODEV);
232121941Sphk	sbuf_cpy(sb, "led/");
233121941Sphk	sbuf_cat(sb, name);
234121941Sphk	sbuf_finish(sb);
235121941Sphk	if (sbuf_overflowed(sb)) {
236121941Sphk		sbuf_delete(sb);
237121941Sphk		return (NODEV);
238121941Sphk	}
239121941Sphk
240121941Sphk	sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO);
241121941Sphk	sc->private = priv;
242121941Sphk	sc->func = func;
243121941Sphk	sc->dev = make_dev(&led_cdevsw, unit2minor(next_minor),
244121941Sphk	    UID_ROOT, GID_WHEEL, 0600, sbuf_data(sb));
245121941Sphk	sc->dev->si_drv1 = sc;
246121941Sphk	next_minor++;
247121941Sphk	sbuf_delete(sb);
248121941Sphk	mtx_lock(&led_mtx);
249121941Sphk	LIST_INSERT_HEAD(&led_list, sc, list);
250121941Sphk	mtx_unlock(&led_mtx);
251121941Sphk	return (sc->dev);
252121941Sphk}
253121941Sphk
254121941Sphkvoid
255121941Sphkled_destroy(dev_t dev)
256121941Sphk{
257121941Sphk	struct ledsc *sc;
258121941Sphk
259121941Sphk	sc = dev->si_drv1;
260121941Sphk	mtx_lock(&led_mtx);
261121941Sphk	LIST_REMOVE(sc, list);
262121941Sphk	mtx_unlock(&led_mtx);
263122963Sphk	if (sc->spec != NULL)
264122963Sphk		sbuf_delete(sc->spec);
265121941Sphk	destroy_dev(dev);
266121941Sphk	free(sc, M_LED);
267121941Sphk}
268