led.c revision 183381
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 183381 2008-09-26 14:19:52Z ed $");
13121941Sphk
14121941Sphk#include <sys/param.h>
15121941Sphk#include <sys/conf.h>
16121941Sphk#include <sys/kernel.h>
17121941Sphk#include <sys/systm.h>
18179413Sed#include <sys/limits.h>
19121941Sphk#include <sys/malloc.h>
20121941Sphk#include <sys/ctype.h>
21121941Sphk#include <sys/sbuf.h>
22121941Sphk#include <sys/queue.h>
23121941Sphk#include <dev/led/led.h>
24121941Sphk#include <sys/uio.h>
25140966Sphk#include <sys/sx.h>
26121941Sphk
27121941Sphkstruct ledsc {
28121941Sphk	LIST_ENTRY(ledsc)	list;
29121941Sphk	void			*private;
30140966Sphk	int			unit;
31121941Sphk	led_t			*func;
32130585Sphk	struct cdev *dev;
33121941Sphk	struct sbuf		*spec;
34121941Sphk	char			*str;
35121941Sphk	char			*ptr;
36121941Sphk	int			count;
37140966Sphk	time_t			last_second;
38121941Sphk};
39121941Sphk
40140966Sphkstatic struct unrhdr *led_unit;
41121941Sphkstatic struct mtx led_mtx;
42140966Sphkstatic struct sx led_sx;
43121941Sphkstatic LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(&led_list);
44140966Sphkstatic struct callout led_ch;
45121941Sphk
46141616Sphkstatic MALLOC_DEFINE(M_LED, "LED", "LED driver");
47121941Sphk
48121941Sphkstatic void
49121941Sphkled_timeout(void *p)
50121941Sphk{
51121941Sphk	struct ledsc	*sc;
52121941Sphk
53121941Sphk	mtx_lock(&led_mtx);
54121941Sphk	LIST_FOREACH(sc, &led_list, list) {
55121941Sphk		if (sc->ptr == NULL)
56121941Sphk			continue;
57121941Sphk		if (sc->count > 0) {
58121941Sphk			sc->count--;
59121941Sphk			continue;
60121941Sphk		}
61128678Sphk		if (*sc->ptr == '.') {
62128678Sphk			sc->ptr = NULL;
63128678Sphk			continue;
64140966Sphk		} else if (*sc->ptr == 'U' || *sc->ptr == 'u') {
65140966Sphk			if (sc->last_second == time_second)
66140966Sphk				continue;
67140966Sphk			sc->last_second = time_second;
68140966Sphk			sc->func(sc->private, *sc->ptr == 'U');
69128678Sphk		} else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') {
70123494Sphk			sc->func(sc->private, 0);
71140966Sphk			sc->count = (*sc->ptr & 0xf) - 1;
72128678Sphk		} else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') {
73123494Sphk			sc->func(sc->private, 1);
74140966Sphk			sc->count = (*sc->ptr & 0xf) - 1;
75128678Sphk		}
76123494Sphk		sc->ptr++;
77123494Sphk		if (*sc->ptr == '\0')
78121941Sphk			sc->ptr = sc->str;
79121941Sphk	}
80121941Sphk	mtx_unlock(&led_mtx);
81140966Sphk	callout_reset(&led_ch, hz / 10, led_timeout, p);
82121941Sphk	return;
83121941Sphk}
84121941Sphk
85121941Sphkstatic int
86140966Sphkled_state(struct cdev *dev, struct sbuf *sb, int state)
87140966Sphk{
88140966Sphk	struct sbuf *sb2 = NULL;
89140966Sphk	struct ledsc *sc;
90140966Sphk
91140966Sphk	mtx_lock(&led_mtx);
92140966Sphk	sc = dev->si_drv1;
93140966Sphk	if (sc != NULL) {
94140966Sphk		sb2 = sc->spec;
95140966Sphk		sc->spec = sb;
96140966Sphk		if (sb != NULL) {
97140966Sphk			sc->str = sbuf_data(sb);
98140966Sphk			sc->ptr = sc->str;
99140966Sphk		} else {
100140966Sphk			sc->str = NULL;
101140966Sphk			sc->ptr = NULL;
102140966Sphk			sc->func(sc->private, state);
103140966Sphk		}
104140966Sphk		sc->count = 0;
105140966Sphk	}
106140966Sphk	mtx_unlock(&led_mtx);
107140966Sphk	if (sb2 != NULL)
108140966Sphk		sbuf_delete(sb2);
109140966Sphk	if (sc == NULL)
110140966Sphk		return (ENXIO);
111140966Sphk	return(0);
112140966Sphk}
113140966Sphk
114140966Sphkstatic int
115130585Sphkled_write(struct cdev *dev, struct uio *uio, int ioflag)
116121941Sphk{
117121941Sphk	int error;
118123494Sphk	char *s, *s2;
119140966Sphk	struct sbuf *sb = NULL;
120121941Sphk	int i;
121121941Sphk
122140966Sphk	if (dev->si_drv1 == NULL)
123140966Sphk		return (ENXIO);
124121941Sphk
125121941Sphk	if (uio->uio_resid > 512)
126121941Sphk		return (EINVAL);
127123494Sphk	s2 = s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
128121941Sphk	s[uio->uio_resid] = '\0';
129121941Sphk	error = uiomove(s, uio->uio_resid, uio);
130121941Sphk	if (error) {
131123494Sphk		free(s2, M_DEVBUF);
132121941Sphk		return (error);
133121941Sphk	}
134121941Sphk
135121941Sphk	/*
136121941Sphk	 * Handle "on" and "off" immediately so people can flash really
137121941Sphk	 * fast from userland if they want to
138121941Sphk	 */
139121941Sphk	if (*s == '0' || *s == '1') {
140140966Sphk		error = led_state(dev, NULL, *s & 1);
141123494Sphk		free(s2, M_DEVBUF);
142140966Sphk		return(error);
143121941Sphk	}
144121941Sphk
145181463Sdes	sb = sbuf_new_auto();
146121941Sphk	if (sb == NULL) {
147123494Sphk		free(s2, M_DEVBUF);
148121941Sphk		return (ENOMEM);
149121941Sphk	}
150121941Sphk
151121941Sphk	switch(s[0]) {
152121941Sphk		/*
153121941Sphk		 * Flash, default is 100msec/100msec.
154121941Sphk		 * 'f2' sets 200msec/200msec etc.
155121941Sphk		 */
156121941Sphk		case 'f':
157121941Sphk			if (s[1] >= '1' && s[1] <= '9')
158121941Sphk				i = s[1] - '1';
159121941Sphk			else
160121941Sphk				i = 0;
161123014Sphk			sbuf_printf(sb, "%c%c", 'A' + i, 'a' + i);
162121941Sphk			break;
163121941Sphk		/*
164121941Sphk		 * Digits, flashes out numbers.
165121941Sphk		 * 'd12' becomes -__________-_-______________________________
166121941Sphk		 */
167121941Sphk		case 'd':
168121941Sphk			for(s++; *s; s++) {
169121941Sphk				if (!isdigit(*s))
170121941Sphk					continue;
171121941Sphk				i = *s - '0';
172121941Sphk				if (i == 0)
173121941Sphk					i = 10;
174121941Sphk				for (; i > 1; i--)
175123014Sphk					sbuf_cat(sb, "Aa");
176123014Sphk				sbuf_cat(sb, "Aj");
177121941Sphk			}
178123014Sphk			sbuf_cat(sb, "jj");
179121941Sphk			break;
180121941Sphk		/*
181121941Sphk		 * String, roll your own.
182123014Sphk		 * 'a-j' gives "off" for n/10 sec.
183123014Sphk		 * 'A-J' gives "on" for n/10 sec.
184121941Sphk		 * no delay before repeat
185121941Sphk		 * 'sAaAbBa' becomes _-_--__-
186121941Sphk		 */
187121941Sphk		case 's':
188121941Sphk			for(s++; *s; s++) {
189128678Sphk				if ((*s >= 'a' && *s <= 'j') ||
190128678Sphk				    (*s >= 'A' && *s <= 'J') ||
191140966Sphk				    *s == 'U' || *s <= 'u' ||
192128678Sphk					*s == '.')
193128678Sphk					sbuf_bcat(sb, s, 1);
194121941Sphk			}
195121941Sphk			break;
196121941Sphk		/*
197121941Sphk		 * Morse.
198121941Sphk		 * '.' becomes _-
199121941Sphk		 * '-' becomes _---
200121941Sphk		 * ' ' becomes __
201121941Sphk		 * '\n' becomes ____
202121941Sphk		 * 1sec pause between repeats
203121941Sphk		 * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________
204121941Sphk		 */
205121941Sphk		case 'm':
206121941Sphk			for(s++; *s; s++) {
207121941Sphk				if (*s == '.')
208123014Sphk					sbuf_cat(sb, "aA");
209121941Sphk				else if (*s == '-')
210123014Sphk					sbuf_cat(sb, "aC");
211121941Sphk				else if (*s == ' ')
212123014Sphk					sbuf_cat(sb, "b");
213121956Sphk				else if (*s == '\n')
214123014Sphk					sbuf_cat(sb, "d");
215121941Sphk			}
216123014Sphk			sbuf_cat(sb, "j");
217121941Sphk			break;
218121941Sphk		default:
219123494Sphk			sbuf_delete(sb);
220123494Sphk			free(s2, M_DEVBUF);
221123494Sphk			return (EINVAL);
222121941Sphk	}
223121941Sphk	sbuf_finish(sb);
224123494Sphk	free(s2, M_DEVBUF);
225121941Sphk	if (sbuf_overflowed(sb)) {
226121941Sphk		sbuf_delete(sb);
227121941Sphk		return (ENOMEM);
228121941Sphk	}
229121941Sphk	if (sbuf_len(sb) == 0) {
230121941Sphk		sbuf_delete(sb);
231121941Sphk		return (0);
232121941Sphk	}
233121941Sphk
234140966Sphk	return (led_state(dev, sb, 0));
235121941Sphk}
236121941Sphk
237121941Sphkstatic struct cdevsw led_cdevsw = {
238126080Sphk	.d_version =	D_VERSION,
239125810Sphk	.d_write =	led_write,
240125810Sphk	.d_name =	"LED",
241121941Sphk};
242121941Sphk
243130585Sphkstruct cdev *
244121941Sphkled_create(led_t *func, void *priv, char const *name)
245121941Sphk{
246168974Sphk
247168974Sphk	return (led_create_state(func, priv, name, 0));
248168974Sphk}
249168974Sphkstruct cdev *
250168974Sphkled_create_state(led_t *func, void *priv, char const *name, int state)
251168974Sphk{
252121941Sphk	struct ledsc	*sc;
253121941Sphk
254140966Sphk	sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO);
255121941Sphk
256140966Sphk	sx_xlock(&led_sx);
257140966Sphk	sc->unit = alloc_unr(led_unit);
258121941Sphk	sc->private = priv;
259121941Sphk	sc->func = func;
260183381Sed	sc->dev = make_dev(&led_cdevsw, sc->unit,
261140966Sphk	    UID_ROOT, GID_WHEEL, 0600, "led/%s", name);
262140966Sphk	sx_xunlock(&led_sx);
263140966Sphk
264140966Sphk	mtx_lock(&led_mtx);
265121941Sphk	sc->dev->si_drv1 = sc;
266140966Sphk	if (LIST_EMPTY(&led_list))
267140966Sphk		callout_reset(&led_ch, hz / 10, led_timeout, NULL);
268121941Sphk	LIST_INSERT_HEAD(&led_list, sc, list);
269168974Sphk	sc->func(sc->private, state != 0);
270121941Sphk	mtx_unlock(&led_mtx);
271140966Sphk
272121941Sphk	return (sc->dev);
273121941Sphk}
274121941Sphk
275121941Sphkvoid
276130585Sphkled_destroy(struct cdev *dev)
277121941Sphk{
278121941Sphk	struct ledsc *sc;
279121941Sphk
280140966Sphk	mtx_lock(&led_mtx);
281121941Sphk	sc = dev->si_drv1;
282140966Sphk	dev->si_drv1 = NULL;
283140966Sphk
284121941Sphk	LIST_REMOVE(sc, list);
285140966Sphk	if (LIST_EMPTY(&led_list))
286140966Sphk		callout_stop(&led_ch);
287121941Sphk	mtx_unlock(&led_mtx);
288140966Sphk
289140966Sphk	sx_xlock(&led_sx);
290140966Sphk	free_unr(led_unit, sc->unit);
291140966Sphk	destroy_dev(dev);
292122963Sphk	if (sc->spec != NULL)
293122963Sphk		sbuf_delete(sc->spec);
294121941Sphk	free(sc, M_LED);
295140966Sphk	sx_xunlock(&led_sx);
296121941Sphk}
297140966Sphk
298140966Sphkstatic void
299140966Sphkled_drvinit(void *unused)
300140966Sphk{
301140966Sphk
302179413Sed	led_unit = new_unrhdr(0, INT_MAX, NULL);
303140966Sphk	mtx_init(&led_mtx, "LED mtx", NULL, MTX_DEF);
304140966Sphk	sx_init(&led_sx, "LED sx");
305140966Sphk	callout_init(&led_ch, CALLOUT_MPSAFE);
306140966Sphk}
307140966Sphk
308140966SphkSYSINIT(leddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, led_drvinit, NULL);
309