led.c revision 247008
189099Sfjoe/*-
289099Sfjoe * ----------------------------------------------------------------------------
389099Sfjoe * "THE BEER-WARE LICENSE" (Revision 42):
4139823Simp * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
589099Sfjoe * can do whatever you want with this stuff. If we meet some day, and you think
689099Sfjoe * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
789099Sfjoe * ----------------------------------------------------------------------------
889099Sfjoe *
989099Sfjoe */
1089099Sfjoe
1189099Sfjoe#include <sys/cdefs.h>
1289099Sfjoe__FBSDID("$FreeBSD: head/sys/dev/led/led.c 247008 2013-02-19 19:25:50Z mav $");
1389099Sfjoe
1489099Sfjoe#include <sys/param.h>
1589099Sfjoe#include <sys/conf.h>
1689099Sfjoe#include <sys/kernel.h>
1789099Sfjoe#include <sys/systm.h>
1889099Sfjoe#include <sys/limits.h>
1989099Sfjoe#include <sys/malloc.h>
2089099Sfjoe#include <sys/ctype.h>
2189099Sfjoe#include <sys/sbuf.h>
2289099Sfjoe#include <sys/queue.h>
2389099Sfjoe#include <dev/led/led.h>
2489099Sfjoe#include <sys/uio.h>
2589099Sfjoe#include <sys/sx.h>
2689099Sfjoe
2789099Sfjoestruct ledsc {
2889099Sfjoe	LIST_ENTRY(ledsc)	list;
2989099Sfjoe	char			*name;
3089099Sfjoe	void			*private;
3189099Sfjoe	int			unit;
3289099Sfjoe	led_t			*func;
3389099Sfjoe	struct cdev *dev;
3489099Sfjoe	struct sbuf		*spec;
3589099Sfjoe	char			*str;
3689099Sfjoe	char			*ptr;
3789099Sfjoe	int			count;
3889099Sfjoe	time_t			last_second;
3989099Sfjoe};
4089099Sfjoe
4189099Sfjoestatic struct unrhdr *led_unit;
4289099Sfjoestatic struct mtx led_mtx;
4389099Sfjoestatic struct sx led_sx;
4489099Sfjoestatic LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(led_list);
4589099Sfjoestatic struct callout led_ch;
4689099Sfjoestatic int blinkers = 0;
47129880Sphk
4889099Sfjoestatic MALLOC_DEFINE(M_LED, "LED", "LED driver");
4989099Sfjoe
5089099Sfjoestatic void
5189099Sfjoeled_timeout(void *p)
5289099Sfjoe{
5389099Sfjoe	struct ledsc	*sc;
5489099Sfjoe
5589099Sfjoe	LIST_FOREACH(sc, &led_list, list) {
5689099Sfjoe		if (sc->ptr == NULL)
5789099Sfjoe			continue;
5889099Sfjoe		if (sc->count > 0) {
59257176Sglebius			sc->count--;
6089099Sfjoe			continue;
6189099Sfjoe		}
6289099Sfjoe		if (*sc->ptr == '.') {
6389099Sfjoe			sc->ptr = NULL;
6489099Sfjoe			blinkers--;
6589099Sfjoe			continue;
6689099Sfjoe		} else if (*sc->ptr == 'U' || *sc->ptr == 'u') {
67186119Sqingli			if (sc->last_second == time_second)
6889099Sfjoe				continue;
6989099Sfjoe			sc->last_second = time_second;
7089099Sfjoe			sc->func(sc->private, *sc->ptr == 'U');
7189099Sfjoe		} else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') {
7289099Sfjoe			sc->func(sc->private, 0);
7389099Sfjoe			sc->count = (*sc->ptr & 0xf) - 1;
7489099Sfjoe		} else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') {
7589099Sfjoe			sc->func(sc->private, 1);
7689099Sfjoe			sc->count = (*sc->ptr & 0xf) - 1;
7789099Sfjoe		}
7889099Sfjoe		sc->ptr++;
7989099Sfjoe		if (*sc->ptr == '\0')
8089099Sfjoe			sc->ptr = sc->str;
8192725Salfred	}
82109771Sfjoe	if (blinkers > 0)
83109771Sfjoe		callout_reset(&led_ch, hz / 10, led_timeout, p);
8489099Sfjoe}
8589099Sfjoe
8689099Sfjoestatic int
87110106Sfjoeled_state(struct ledsc *sc, struct sbuf **sb, int state)
88110106Sfjoe{
8989099Sfjoe	struct sbuf *sb2 = NULL;
90249925Sglebius
9189099Sfjoe	sb2 = sc->spec;
9289099Sfjoe	sc->spec = *sb;
9389099Sfjoe	if (*sb != NULL) {
9489099Sfjoe		sc->str = sbuf_data(*sb);
9589099Sfjoe		if (sc->ptr == NULL) {
9689099Sfjoe			blinkers++;
9789099Sfjoe			callout_reset(&led_ch, hz / 10, led_timeout, NULL);
98249925Sglebius		}
99191148Skmacy		sc->ptr = sc->str;
10089099Sfjoe	} else {
10189099Sfjoe		sc->str = NULL;
10289099Sfjoe		if (sc->ptr != NULL)
10389099Sfjoe			blinkers--;
104109771Sfjoe		sc->ptr = NULL;
105110106Sfjoe		sc->func(sc->private, state);
106287861Smelifaro	}
107287861Smelifaro	sc->count = 0;
108275211Sbz	*sb = sb2;
10989099Sfjoe	return(0);
110148887Srwatson}
111148887Srwatson
11289099Sfjoestatic int
11389099Sfjoeled_parse(const char *s, struct sbuf **sb, int *state)
11489099Sfjoe{
115287861Smelifaro	int i, error;
116293544Smelifaro
117293544Smelifaro	/*
118287861Smelifaro	 * Handle "on" and "off" immediately so people can flash really
11989099Sfjoe	 * fast from userland if they want to
12089099Sfjoe	 */
12189099Sfjoe	if (*s == '0' || *s == '1') {
12289099Sfjoe		*state = *s & 1;
12389099Sfjoe		return (0);
12489099Sfjoe	}
12589099Sfjoe
12689099Sfjoe	*state = 0;
12789099Sfjoe	*sb = sbuf_new_auto();
12889099Sfjoe	if (*sb == NULL)
12989099Sfjoe		return (ENOMEM);
13089099Sfjoe	switch(s[0]) {
131128636Sluigi		/*
132301217Sgnn		 * Flash, default is 100msec/100msec.
133301217Sgnn		 * 'f2' sets 200msec/200msec etc.
134128636Sluigi		 */
135128636Sluigi		case 'f':
136128636Sluigi			if (s[1] >= '1' && s[1] <= '9')
13789099Sfjoe				i = s[1] - '1';
13889099Sfjoe			else
13989099Sfjoe				i = 0;
14089099Sfjoe			sbuf_printf(*sb, "%c%c", 'A' + i, 'a' + i);
141127260Smdodd			break;
142127260Smdodd		/*
143127260Smdodd		 * Digits, flashes out numbers.
144127260Smdodd		 * 'd12' becomes -__________-_-______________________________
145127260Smdodd		 */
146127260Smdodd		case 'd':
147127260Smdodd			for(s++; *s; s++) {
148127260Smdodd				if (!isdigit(*s))
149127260Smdodd					continue;
150127260Smdodd				i = *s - '0';
151127260Smdodd				if (i == 0)
152127275Smdodd					i = 10;
153127260Smdodd				for (; i > 1; i--)
154127260Smdodd					sbuf_cat(*sb, "Aa");
155127260Smdodd				sbuf_cat(*sb, "Aj");
156127260Smdodd			}
157127275Smdodd			sbuf_cat(*sb, "jj");
158127260Smdodd			break;
159127260Smdodd		/*
160127260Smdodd		 * String, roll your own.
161127260Smdodd		 * 'a-j' gives "off" for n/10 sec.
162127290Smdodd		 * 'A-J' gives "on" for n/10 sec.
163127260Smdodd		 * no delay before repeat
164127290Smdodd		 * 'sAaAbBa' becomes _-_--__-
165127260Smdodd		 */
166127260Smdodd		case 's':
167127260Smdodd			for(s++; *s; s++) {
16889099Sfjoe				if ((*s >= 'a' && *s <= 'j') ||
16989099Sfjoe				    (*s >= 'A' && *s <= 'J') ||
17089099Sfjoe				    *s == 'U' || *s <= 'u' ||
171276896Smelifaro					*s == '.')
172276886Smelifaro					sbuf_bcat(*sb, s, 1);
173287861Smelifaro			}
174301217Sgnn			break;
175301217Sgnn		/*
176287861Smelifaro		 * Morse.
177287861Smelifaro		 * '.' becomes _-
178287861Smelifaro		 * '-' becomes _---
17989099Sfjoe		 * ' ' becomes __
18089099Sfjoe		 * '\n' becomes ____
18189099Sfjoe		 * 1sec pause between repeats
18289099Sfjoe		 * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________
183249925Sglebius		 */
184249925Sglebius		case 'm':
185249925Sglebius			for(s++; *s; s++) {
186109771Sfjoe				if (*s == '.')
187249925Sglebius					sbuf_cat(*sb, "aA");
18889099Sfjoe				else if (*s == '-')
18989099Sfjoe					sbuf_cat(*sb, "aC");
19089099Sfjoe				else if (*s == ' ')
19189099Sfjoe					sbuf_cat(*sb, "b");
19289099Sfjoe				else if (*s == '\n')
19389099Sfjoe					sbuf_cat(*sb, "d");
19489099Sfjoe			}
19589099Sfjoe			sbuf_cat(*sb, "j");
19689099Sfjoe			break;
19789099Sfjoe		default:
19889099Sfjoe			sbuf_delete(*sb);
19989099Sfjoe			return (EINVAL);
20089099Sfjoe	}
20189099Sfjoe	error = sbuf_finish(*sb);
20289099Sfjoe	if (error != 0 || sbuf_len(*sb) == 0) {
203109771Sfjoe		*sb = NULL;
20489099Sfjoe		return (error);
20589099Sfjoe	}
20689099Sfjoe	return (0);
207249925Sglebius}
20889099Sfjoe
209105598Sbrooksstatic int
21089099Sfjoeled_write(struct cdev *dev, struct uio *uio, int ioflag)
21189099Sfjoe{
21289099Sfjoe	struct ledsc	*sc;
213110106Sfjoe	char *s;
214243882Sglebius	struct sbuf *sb = NULL;
215298075Spfg	int error, state = 0;
21689099Sfjoe
21789099Sfjoe	if (uio->uio_resid > 512)
21889099Sfjoe		return (EINVAL);
21989099Sfjoe	s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK);
220110106Sfjoe	s[uio->uio_resid] = '\0';
221110106Sfjoe	error = uiomove(s, uio->uio_resid, uio);
222110106Sfjoe	if (error) {
223110106Sfjoe		free(s, M_DEVBUF);
224110106Sfjoe		return (error);
22589099Sfjoe	}
226109771Sfjoe	error = led_parse(s, &sb, &state);
227109771Sfjoe	free(s, M_DEVBUF);
228109771Sfjoe	if (error)
229109771Sfjoe		return (error);
230109771Sfjoe	mtx_lock(&led_mtx);
231109771Sfjoe	sc = dev->si_drv1;
232109771Sfjoe	if (sc != NULL)
233109771Sfjoe		error = led_state(sc, &sb, state);
234109771Sfjoe	mtx_unlock(&led_mtx);
235109771Sfjoe	if (sb != NULL)
236109771Sfjoe		sbuf_delete(sb);
237106939Ssam	return (error);
23889099Sfjoe}
239191605Skmacy
24089099Sfjoeint
24189099Sfjoeled_set(char const *name, char const *cmd)
24289099Sfjoe{
24389099Sfjoe	struct ledsc	*sc;
24489099Sfjoe	struct sbuf *sb = NULL;
24589099Sfjoe	int error, state = 0;
24689099Sfjoe
24789099Sfjoe	error = led_parse(cmd, &sb, &state);
24889099Sfjoe	if (error)
24989099Sfjoe		return (error);
250157681Srwatson	mtx_lock(&led_mtx);
25189099Sfjoe	LIST_FOREACH(sc, &led_list, list) {
25289099Sfjoe		if (strcmp(sc->name, name) == 0)
25389099Sfjoe			break;
254147256Sbrooks	}
25589099Sfjoe	if (sc != NULL)
25689099Sfjoe		error = led_state(sc, &sb, state);
25789099Sfjoe	else
25889099Sfjoe		error = ENOENT;
259157681Srwatson	mtx_unlock(&led_mtx);
26089099Sfjoe	if (sb != NULL)
26189099Sfjoe		sbuf_delete(sb);
26289099Sfjoe	return (0);
26389099Sfjoe}
26489099Sfjoe
265147256Sbrooksstatic struct cdevsw led_cdevsw = {
266298075Spfg	.d_version =	D_VERSION,
26789099Sfjoe	.d_write =	led_write,
26889099Sfjoe	.d_name =	"LED",
26989099Sfjoe};
27089099Sfjoe
271298075Spfgstruct cdev *
27289099Sfjoeled_create(led_t *func, void *priv, char const *name)
27389099Sfjoe{
27489099Sfjoe
27589099Sfjoe	return (led_create_state(func, priv, name, 0));
27689099Sfjoe}
27789099Sfjoestruct cdev *
27889099Sfjoeled_create_state(led_t *func, void *priv, char const *name, int state)
279298649Spfg{
28089099Sfjoe	struct ledsc	*sc;
28189099Sfjoe
28289099Sfjoe	sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO);
28389099Sfjoe
28489099Sfjoe	sx_xlock(&led_sx);
28589099Sfjoe	sc->name = strdup(name, M_LED);
28689099Sfjoe	sc->unit = alloc_unr(led_unit);
287110106Sfjoe	sc->private = priv;
28889099Sfjoe	sc->func = func;
28989099Sfjoe	sc->dev = make_dev(&led_cdevsw, sc->unit,
29089099Sfjoe	    UID_ROOT, GID_WHEEL, 0600, "led/%s", name);
29189099Sfjoe	sx_xunlock(&led_sx);
29289099Sfjoe
29389099Sfjoe	mtx_lock(&led_mtx);
294243882Sglebius	sc->dev->si_drv1 = sc;
29589099Sfjoe	LIST_INSERT_HEAD(&led_list, sc, list);
29693750Sluigi	sc->func(sc->private, state != 0);
29789099Sfjoe	mtx_unlock(&led_mtx);
29889099Sfjoe
29989099Sfjoe	return (sc->dev);
300243882Sglebius}
301298075Spfg
30293750Sluigivoid
30389099Sfjoeled_destroy(struct cdev *dev)
30489099Sfjoe{
30589099Sfjoe	struct ledsc *sc;
30689099Sfjoe
30789099Sfjoe	mtx_lock(&led_mtx);
30889099Sfjoe	sc = dev->si_drv1;
30989099Sfjoe	dev->si_drv1 = NULL;
31089099Sfjoe	if (sc->ptr != NULL)
31189099Sfjoe		blinkers--;
31289099Sfjoe	LIST_REMOVE(sc, list);
31389099Sfjoe	if (LIST_EMPTY(&led_list))
31489099Sfjoe		callout_stop(&led_ch);
31589099Sfjoe	mtx_unlock(&led_mtx);
31689099Sfjoe
31789099Sfjoe	sx_xlock(&led_sx);
31889099Sfjoe	free_unr(led_unit, sc->unit);
319243882Sglebius	destroy_dev(dev);
320298075Spfg	if (sc->spec != NULL)
32189099Sfjoe		sbuf_delete(sc->spec);
32289099Sfjoe	free(sc->name, M_LED);
32389099Sfjoe	free(sc, M_LED);
32489099Sfjoe	sx_xunlock(&led_sx);
32589099Sfjoe}
32689099Sfjoe
32789099Sfjoestatic void
32889099Sfjoeled_drvinit(void *unused)
32989099Sfjoe{
33089099Sfjoe
33189099Sfjoe	led_unit = new_unrhdr(0, INT_MAX, NULL);
332243882Sglebius	mtx_init(&led_mtx, "LED mtx", NULL, MTX_DEF);
333298075Spfg	sx_init(&led_sx, "LED sx");
33489099Sfjoe	callout_init_mtx(&led_ch, &led_mtx, 0);
33589099Sfjoe}
33689099Sfjoe
33789099SfjoeSYSINIT(leddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, led_drvinit, NULL);
33889099Sfjoe