led.c revision 140966
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 140966 2005-01-29 16:33: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>
24140966Sphk#include <sys/sx.h>
25121941Sphk
26121941Sphkstruct ledsc {
27121941Sphk	LIST_ENTRY(ledsc)	list;
28121941Sphk	void			*private;
29140966Sphk	int			unit;
30121941Sphk	led_t			*func;
31130585Sphk	struct cdev *dev;
32121941Sphk	struct sbuf		*spec;
33121941Sphk	char			*str;
34121941Sphk	char			*ptr;
35121941Sphk	int			count;
36140966Sphk	time_t			last_second;
37121941Sphk};
38121941Sphk
39140966Sphkstatic struct unrhdr *led_unit;
40121941Sphkstatic struct mtx led_mtx;
41140966Sphkstatic struct sx led_sx;
42121941Sphkstatic LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(&led_list);
43140966Sphkstatic struct callout led_ch;
44121941Sphk
45121941SphkMALLOC_DEFINE(M_LED, "LED", "LED driver");
46121941Sphk
47121941Sphkstatic void
48121941Sphkled_timeout(void *p)
49121941Sphk{
50121941Sphk	struct ledsc	*sc;
51121941Sphk
52121941Sphk	mtx_lock(&led_mtx);
53121941Sphk	LIST_FOREACH(sc, &led_list, list) {
54121941Sphk		if (sc->ptr == NULL)
55121941Sphk			continue;
56121941Sphk		if (sc->count > 0) {
57121941Sphk			sc->count--;
58121941Sphk			continue;
59121941Sphk		}
60128678Sphk		if (*sc->ptr == '.') {
61128678Sphk			sc->ptr = NULL;
62128678Sphk			continue;
63140966Sphk		} else if (*sc->ptr == 'U' || *sc->ptr == 'u') {
64140966Sphk			if (sc->last_second == time_second)
65140966Sphk				continue;
66140966Sphk			sc->last_second = time_second;
67140966Sphk			sc->func(sc->private, *sc->ptr == 'U');
68128678Sphk		} else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') {
69123494Sphk			sc->func(sc->private, 0);
70140966Sphk			sc->count = (*sc->ptr & 0xf) - 1;
71128678Sphk		} else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') {
72123494Sphk			sc->func(sc->private, 1);
73140966Sphk			sc->count = (*sc->ptr & 0xf) - 1;
74128678Sphk		}
75123494Sphk		sc->ptr++;
76123494Sphk		if (*sc->ptr == '\0')
77121941Sphk			sc->ptr = sc->str;
78121941Sphk	}
79121941Sphk	mtx_unlock(&led_mtx);
80140966Sphk	callout_reset(&led_ch, hz / 10, led_timeout, p);
81121941Sphk	return;
82121941Sphk}
83121941Sphk
84121941Sphkstatic int
85140966Sphkled_state(struct cdev *dev, struct sbuf *sb, int state)
86140966Sphk{
87140966Sphk	struct sbuf *sb2 = NULL;
88140966Sphk	struct ledsc *sc;
89140966Sphk
90140966Sphk	mtx_lock(&led_mtx);
91140966Sphk	sc = dev->si_drv1;
92140966Sphk	if (sc != NULL) {
93140966Sphk		sb2 = sc->spec;
94140966Sphk		sc->spec = sb;
95140966Sphk		if (sb != NULL) {
96140966Sphk			sc->str = sbuf_data(sb);
97140966Sphk			sc->ptr = sc->str;
98140966Sphk		} else {
99140966Sphk			sc->str = NULL;
100140966Sphk			sc->ptr = NULL;
101140966Sphk			sc->func(sc->private, state);
102140966Sphk		}
103140966Sphk		sc->count = 0;
104140966Sphk	}
105140966Sphk	mtx_unlock(&led_mtx);
106140966Sphk	if (sb2 != NULL)
107140966Sphk		sbuf_delete(sb2);
108140966Sphk	if (sc == NULL)
109140966Sphk		return (ENXIO);
110140966Sphk	return(0);
111140966Sphk}
112140966Sphk
113140966Sphkstatic int
114130585Sphkled_write(struct cdev *dev, struct uio *uio, int ioflag)
115121941Sphk{
116121941Sphk	int error;
117123494Sphk	char *s, *s2;
118140966Sphk	struct sbuf *sb = NULL;
119121941Sphk	int i;
120121941Sphk
121140966Sphk	if (dev->si_drv1 == NULL)
122140966Sphk		return (ENXIO);
123121941Sphk
124121941Sphk	if (uio->uio_resid > 512)
125121941Sphk		return (EINVAL);
126123494Sphk	s2 = s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
127121941Sphk	s[uio->uio_resid] = '\0';
128121941Sphk	error = uiomove(s, uio->uio_resid, uio);
129121941Sphk	if (error) {
130123494Sphk		free(s2, M_DEVBUF);
131121941Sphk		return (error);
132121941Sphk	}
133121941Sphk
134121941Sphk	/*
135121941Sphk	 * Handle "on" and "off" immediately so people can flash really
136121941Sphk	 * fast from userland if they want to
137121941Sphk	 */
138121941Sphk	if (*s == '0' || *s == '1') {
139140966Sphk		error = led_state(dev, NULL, *s & 1);
140123494Sphk		free(s2, M_DEVBUF);
141140966Sphk		return(error);
142121941Sphk	}
143121941Sphk
144121941Sphk	sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND);
145121941Sphk	if (sb == NULL) {
146123494Sphk		free(s2, M_DEVBUF);
147121941Sphk		return (ENOMEM);
148121941Sphk	}
149121941Sphk
150121941Sphk	switch(s[0]) {
151121941Sphk		/*
152121941Sphk		 * Flash, default is 100msec/100msec.
153121941Sphk		 * 'f2' sets 200msec/200msec etc.
154121941Sphk		 */
155121941Sphk		case 'f':
156121941Sphk			if (s[1] >= '1' && s[1] <= '9')
157121941Sphk				i = s[1] - '1';
158121941Sphk			else
159121941Sphk				i = 0;
160123014Sphk			sbuf_printf(sb, "%c%c", 'A' + i, 'a' + i);
161121941Sphk			break;
162121941Sphk		/*
163121941Sphk		 * Digits, flashes out numbers.
164121941Sphk		 * 'd12' becomes -__________-_-______________________________
165121941Sphk		 */
166121941Sphk		case 'd':
167121941Sphk			for(s++; *s; s++) {
168121941Sphk				if (!isdigit(*s))
169121941Sphk					continue;
170121941Sphk				i = *s - '0';
171121941Sphk				if (i == 0)
172121941Sphk					i = 10;
173121941Sphk				for (; i > 1; i--)
174123014Sphk					sbuf_cat(sb, "Aa");
175123014Sphk				sbuf_cat(sb, "Aj");
176121941Sphk			}
177123014Sphk			sbuf_cat(sb, "jj");
178121941Sphk			break;
179121941Sphk		/*
180121941Sphk		 * String, roll your own.
181123014Sphk		 * 'a-j' gives "off" for n/10 sec.
182123014Sphk		 * 'A-J' gives "on" for n/10 sec.
183121941Sphk		 * no delay before repeat
184121941Sphk		 * 'sAaAbBa' becomes _-_--__-
185121941Sphk		 */
186121941Sphk		case 's':
187121941Sphk			for(s++; *s; s++) {
188128678Sphk				if ((*s >= 'a' && *s <= 'j') ||
189128678Sphk				    (*s >= 'A' && *s <= 'J') ||
190140966Sphk				    *s == 'U' || *s <= 'u' ||
191128678Sphk					*s == '.')
192128678Sphk					sbuf_bcat(sb, s, 1);
193121941Sphk			}
194121941Sphk			break;
195121941Sphk		/*
196121941Sphk		 * Morse.
197121941Sphk		 * '.' becomes _-
198121941Sphk		 * '-' becomes _---
199121941Sphk		 * ' ' becomes __
200121941Sphk		 * '\n' becomes ____
201121941Sphk		 * 1sec pause between repeats
202121941Sphk		 * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________
203121941Sphk		 */
204121941Sphk		case 'm':
205121941Sphk			for(s++; *s; s++) {
206121941Sphk				if (*s == '.')
207123014Sphk					sbuf_cat(sb, "aA");
208121941Sphk				else if (*s == '-')
209123014Sphk					sbuf_cat(sb, "aC");
210121941Sphk				else if (*s == ' ')
211123014Sphk					sbuf_cat(sb, "b");
212121956Sphk				else if (*s == '\n')
213123014Sphk					sbuf_cat(sb, "d");
214121941Sphk			}
215123014Sphk			sbuf_cat(sb, "j");
216121941Sphk			break;
217121941Sphk		default:
218123494Sphk			sbuf_delete(sb);
219123494Sphk			free(s2, M_DEVBUF);
220123494Sphk			return (EINVAL);
221121941Sphk	}
222121941Sphk	sbuf_finish(sb);
223123494Sphk	free(s2, M_DEVBUF);
224121941Sphk	if (sbuf_overflowed(sb)) {
225121941Sphk		sbuf_delete(sb);
226121941Sphk		return (ENOMEM);
227121941Sphk	}
228121941Sphk	if (sbuf_len(sb) == 0) {
229121941Sphk		sbuf_delete(sb);
230121941Sphk		return (0);
231121941Sphk	}
232121941Sphk
233140966Sphk	return (led_state(dev, sb, 0));
234121941Sphk}
235121941Sphk
236121941Sphkstatic struct cdevsw led_cdevsw = {
237126080Sphk	.d_version =	D_VERSION,
238125810Sphk	.d_write =	led_write,
239125810Sphk	.d_name =	"LED",
240121941Sphk};
241121941Sphk
242130585Sphkstruct cdev *
243121941Sphkled_create(led_t *func, void *priv, char const *name)
244121941Sphk{
245121941Sphk	struct ledsc	*sc;
246121941Sphk
247140966Sphk	sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO);
248121941Sphk
249140966Sphk	sx_xlock(&led_sx);
250140966Sphk	sc->unit = alloc_unr(led_unit);
251121941Sphk	sc->private = priv;
252121941Sphk	sc->func = func;
253140966Sphk	sc->dev = make_dev(&led_cdevsw, unit2minor(sc->unit),
254140966Sphk	    UID_ROOT, GID_WHEEL, 0600, "led/%s", name);
255140966Sphk	sx_xunlock(&led_sx);
256140966Sphk
257140966Sphk	mtx_lock(&led_mtx);
258121941Sphk	sc->dev->si_drv1 = sc;
259140966Sphk	if (LIST_EMPTY(&led_list))
260140966Sphk		callout_reset(&led_ch, hz / 10, led_timeout, NULL);
261121941Sphk	LIST_INSERT_HEAD(&led_list, sc, list);
262128678Sphk	sc->func(sc->private, 0);
263121941Sphk	mtx_unlock(&led_mtx);
264140966Sphk
265121941Sphk	return (sc->dev);
266121941Sphk}
267121941Sphk
268121941Sphkvoid
269130585Sphkled_destroy(struct cdev *dev)
270121941Sphk{
271121941Sphk	struct ledsc *sc;
272121941Sphk
273140966Sphk	mtx_lock(&led_mtx);
274121941Sphk	sc = dev->si_drv1;
275140966Sphk	dev->si_drv1 = NULL;
276140966Sphk
277121941Sphk	LIST_REMOVE(sc, list);
278140966Sphk	if (LIST_EMPTY(&led_list))
279140966Sphk		callout_stop(&led_ch);
280121941Sphk	mtx_unlock(&led_mtx);
281140966Sphk
282140966Sphk	sx_xlock(&led_sx);
283140966Sphk	free_unr(led_unit, sc->unit);
284140966Sphk	destroy_dev(dev);
285122963Sphk	if (sc->spec != NULL)
286122963Sphk		sbuf_delete(sc->spec);
287121941Sphk	free(sc, M_LED);
288140966Sphk	sx_xunlock(&led_sx);
289121941Sphk}
290140966Sphk
291140966Sphkstatic void
292140966Sphkled_drvinit(void *unused)
293140966Sphk{
294140966Sphk
295140966Sphk	led_unit = new_unrhdr(0, minor2unit(MAXMINOR));
296140966Sphk	mtx_init(&led_mtx, "LED mtx", NULL, MTX_DEF);
297140966Sphk	sx_init(&led_sx, "LED sx");
298140966Sphk	callout_init(&led_ch, CALLOUT_MPSAFE);
299140966Sphk}
300140966Sphk
301140966SphkSYSINIT(leddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, led_drvinit, NULL);
302