led.c revision 128678
1139823Simp/*- 2200838Sluigi * ---------------------------------------------------------------------------- 398943Sluigi * "THE BEER-WARE LICENSE" (Revision 42): 498943Sluigi * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you 598943Sluigi * can do whatever you want with this stuff. If we meet some day, and you think 698943Sluigi * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 798943Sluigi * ---------------------------------------------------------------------------- 898943Sluigi * 9116763Sluigi */ 1098943Sluigi 1198943Sluigi#include <sys/cdefs.h> 12105775Smaxim__FBSDID("$FreeBSD: head/sys/dev/led/led.c 128678 2004-04-27 13:09:21Z phk $"); 1398943Sluigi 1498943Sluigi#include <sys/param.h> 1598943Sluigi#include <sys/conf.h> 1698943Sluigi#include <sys/kernel.h> 1798943Sluigi#include <sys/systm.h> 1898943Sluigi#include <sys/malloc.h> 1998943Sluigi#include <sys/ctype.h> 2098943Sluigi#include <sys/sbuf.h> 2198943Sluigi#include <sys/queue.h> 2298943Sluigi#include <dev/led/led.h> 2398943Sluigi#include <sys/uio.h> 2498943Sluigi 2598943Sluigistruct ledsc { 26172467Ssilby LIST_ENTRY(ledsc) list; 27172467Ssilby void *private; 28172467Ssilby led_t *func; 2998943Sluigi dev_t dev; 30200601Sluigi struct sbuf *spec; 3198943Sluigi char *str; 3298943Sluigi char *ptr; 33134346Sru int count; 3498943Sluigi}; 35166479Salc 3698943Sluigistatic unsigned next_minor; 3798943Sluigistatic struct mtx led_mtx; 3898943Sluigistatic LIST_HEAD(, ledsc) led_list = LIST_HEAD_INITIALIZER(&led_list); 3998943Sluigi 4098943SluigiMALLOC_DEFINE(M_LED, "LED", "LED driver"); 41134346Sru 42152928Sumestatic void 43152928Sumeled_timeout(void *p) 4498943Sluigi{ 4598943Sluigi struct ledsc *sc; 4698943Sluigi 47138642Scsjp mtx_lock(&led_mtx); 48165648Spiso LIST_FOREACH(sc, &led_list, list) { 4998943Sluigi if (sc->ptr == NULL) 5098943Sluigi continue; 5198943Sluigi if (sc->count > 0) { 52155201Scsjp sc->count--; 53133600Scsjp continue; 54129876Sphk } 55164033Srwatson if (*sc->ptr == '.') { 5698943Sluigi sc->ptr = NULL; 57155201Scsjp continue; 5898943Sluigi } else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') { 5998943Sluigi sc->func(sc->private, 0); 6098943Sluigi } else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') { 6198943Sluigi sc->func(sc->private, 1); 6298943Sluigi } 63188676Sluigi sc->count = *sc->ptr & 0xf; 6498943Sluigi sc->ptr++; 6598943Sluigi if (*sc->ptr == '\0') 66171173Smlaier sc->ptr = sc->str; 67185571Sbz } 68175659Srwatson mtx_unlock(&led_mtx); 6998943Sluigi timeout(led_timeout, p, hz / 10); 7098943Sluigi return; 7198943Sluigi} 7298943Sluigi 7398943Sluigistatic int 7498943Sluigiled_write(dev_t dev, struct uio *uio, int ioflag) 7598943Sluigi{ 76200580Sluigi int error; 77163069Sbz char *s, *s2; 78161767Sjhay struct ledsc *sc; 7998943Sluigi struct sbuf *sb; 8098943Sluigi struct sbuf *sb2; 8198943Sluigi int i; 82164258Sbz 83185571Sbz sc = dev->si_drv1; 84145246Sbrooks 85145246Sbrooks if (uio->uio_resid > 512) 86148414Sume return (EINVAL); 87223073Sae s2 = s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK); 88148414Sume if (s == NULL) 89200027Sume return (ENOMEM); 90148414Sume s[uio->uio_resid] = '\0'; 91145246Sbrooks error = uiomove(s, uio->uio_resid, uio); 9299475Sluigi if (error) { 9399475Sluigi free(s2, M_DEVBUF); 94188580Sluigi return (error); 95163606Srwatson } 96188580Sluigi 97163606Srwatson /* 98101628Sluigi * Handle "on" and "off" immediately so people can flash really 99200601Sluigi * fast from userland if they want to 100200601Sluigi */ 101101628Sluigi if (*s == '0' || *s == '1') { 10298943Sluigi mtx_lock(&led_mtx); 103200601Sluigi sb2 = sc->spec; 104215701Sdim sc->spec = NULL; 105200601Sluigi sc->str = NULL; 106195699Srwatson sc->ptr = NULL; 107215701Sdim sc->count = 0; 108200601Sluigi sc->func(sc->private, *s & 1); 109200601Sluigi mtx_unlock(&led_mtx); 110191932Sjhb if (sb2 != NULL) 111191932Sjhb sbuf_delete(sb2); 112191932Sjhb free(s2, M_DEVBUF); 113191932Sjhb return(0); 114191932Sjhb } 11598943Sluigi 116200601Sluigi sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 117220878Sbz if (sb == NULL) { 118193859Soleg free(s2, M_DEVBUF); 119130363Scsjp return (ENOMEM); 120200601Sluigi } 121200601Sluigi 122200601Sluigi switch(s[0]) { 123200601Sluigi /* 124200601Sluigi * Flash, default is 100msec/100msec. 125200601Sluigi * 'f2' sets 200msec/200msec etc. 126200601Sluigi */ 12798943Sluigi case 'f': 128200601Sluigi if (s[1] >= '1' && s[1] <= '9') 129200601Sluigi i = s[1] - '1'; 130200601Sluigi else 131200601Sluigi i = 0; 132200601Sluigi sbuf_printf(sb, "%c%c", 'A' + i, 'a' + i); 133200601Sluigi break; 134200601Sluigi /* 135200601Sluigi * Digits, flashes out numbers. 136200601Sluigi * 'd12' becomes -__________-_-______________________________ 137195699Srwatson */ 13898943Sluigi case 'd': 139176669Spiso for(s++; *s; s++) { 140200580Sluigi if (!isdigit(*s)) 141176669Spiso continue; 142176669Spiso i = *s - '0'; 143176669Spiso if (i == 0) 144176669Spiso i = 10; 14598943Sluigi for (; i > 1; i--) 14698943Sluigi sbuf_cat(sb, "Aa"); 147204591Sluigi sbuf_cat(sb, "Aj"); 148204591Sluigi } 149204591Sluigi sbuf_cat(sb, "jj"); 150204591Sluigi break; 151204591Sluigi /* 15298943Sluigi * String, roll your own. 153200601Sluigi * 'a-j' gives "off" for n/10 sec. 154200601Sluigi * 'A-J' gives "on" for n/10 sec. 155200601Sluigi * no delay before repeat 156195699Srwatson * 'sAaAbBa' becomes _-_--__- 157195862Sjulian */ 158195862Sjulian case 's': 159195699Srwatson for(s++; *s; s++) { 160195862Sjulian if ((*s >= 'a' && *s <= 'j') || 161195862Sjulian (*s >= 'A' && *s <= 'J') || 162195699Srwatson *s == '.') 163195699Srwatson sbuf_bcat(sb, s, 1); 164186048Sbz } 165182818Srik break; 166204591Sluigi /* 167195862Sjulian * Morse. 168183015Srik * '.' becomes _- 169204591Sluigi * '-' becomes _--- 170195862Sjulian * ' ' becomes __ 171191932Sjhb * '\n' becomes ____ 172195862Sjulian * 1sec pause between repeats 173195862Sjulian * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________ 174191932Sjhb */ 175200838Sluigi case 'm': 176200838Sluigi for(s++; *s; s++) { 177200838Sluigi if (*s == '.') 178200040Sluigi sbuf_cat(sb, "aA"); 179195862Sjulian else if (*s == '-') 180195862Sjulian sbuf_cat(sb, "aC"); 181195862Sjulian else if (*s == ' ') 182195862Sjulian sbuf_cat(sb, "b"); 183195862Sjulian else if (*s == '\n') 184195862Sjulian sbuf_cat(sb, "d"); 185200040Sluigi } 18698943Sluigi sbuf_cat(sb, "j"); 187204591Sluigi break; 188204591Sluigi default: 189187821Sluigi sbuf_delete(sb); 19098943Sluigi free(s2, M_DEVBUF); 191200580Sluigi return (EINVAL); 192149020Sbz } 193200601Sluigi sbuf_finish(sb); 194145093Sbrooks free(s2, M_DEVBUF); 195145093Sbrooks if (sbuf_overflowed(sb)) { 19698943Sluigi sbuf_delete(sb); 197145093Sbrooks return (ENOMEM); 198145093Sbrooks } 199164258Sbz if (sbuf_len(sb) == 0) { 200145093Sbrooks sbuf_delete(sb); 201145565Sbrooks return (0); 202145246Sbrooks } 20398943Sluigi 20499622Sluigi mtx_lock(&led_mtx); 205145565Sbrooks sb2 = sc->spec; 20698943Sluigi sc->spec = sb; 207145093Sbrooks sc->str = sbuf_data(sb); 20898943Sluigi sc->ptr = sc->str; 20998943Sluigi sc->count = 0; 21098943Sluigi mtx_unlock(&led_mtx); 21198943Sluigi if (sb2 != NULL) 21298943Sluigi sbuf_delete(sb2); 21398943Sluigi return(0); 21498943Sluigi} 21598943Sluigi 216145565Sbrooksstatic struct cdevsw led_cdevsw = { 21798943Sluigi .d_version = D_VERSION, 218145093Sbrooks .d_flags = D_NEEDGIANT, 219145093Sbrooks .d_write = led_write, 22098943Sluigi .d_name = "LED", 22198943Sluigi}; 22298943Sluigi 22398943Sluigidev_t 22498943Sluigiled_create(led_t *func, void *priv, char const *name) 22598943Sluigi{ 22698943Sluigi struct ledsc *sc; 22798943Sluigi struct sbuf *sb; 22898943Sluigi 22998943Sluigi if (next_minor == 0) { 23098943Sluigi mtx_init(&led_mtx, "LED mtx", NULL, MTX_DEF); 23198943Sluigi timeout(led_timeout, NULL, hz / 10); 23298943Sluigi } 23398943Sluigi 23498943Sluigi sb = sbuf_new(NULL, NULL, SPECNAMELEN, SBUF_FIXEDLEN); 23598943Sluigi if (sb == NULL) 23698943Sluigi return (NODEV); 23798943Sluigi sbuf_cpy(sb, "led/"); 23898943Sluigi sbuf_cat(sb, name); 23998943Sluigi sbuf_finish(sb); 24098943Sluigi if (sbuf_overflowed(sb)) { 24198943Sluigi sbuf_delete(sb); 24298943Sluigi return (NODEV); 24398943Sluigi } 24498943Sluigi 24598943Sluigi sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO); 24698943Sluigi sc->private = priv; 24798943Sluigi sc->func = func; 24898943Sluigi sc->dev = make_dev(&led_cdevsw, unit2minor(next_minor), 24998943Sluigi UID_ROOT, GID_WHEEL, 0600, sbuf_data(sb)); 25098943Sluigi sc->dev->si_drv1 = sc; 25198943Sluigi next_minor++; 25298943Sluigi sbuf_delete(sb); 25398943Sluigi mtx_lock(&led_mtx); 25498943Sluigi LIST_INSERT_HEAD(&led_list, sc, list); 25598943Sluigi sc->func(sc->private, 0); 25698943Sluigi mtx_unlock(&led_mtx); 25798943Sluigi return (sc->dev); 25898943Sluigi} 25998943Sluigi 26098943Sluigivoid 26198943Sluigiled_destroy(dev_t dev) 26298943Sluigi{ 26398943Sluigi struct ledsc *sc; 26498943Sluigi 26598943Sluigi sc = dev->si_drv1; 26698943Sluigi mtx_lock(&led_mtx); 26798943Sluigi LIST_REMOVE(sc, list); 26898943Sluigi mtx_unlock(&led_mtx); 26998943Sluigi if (sc->spec != NULL) 27098943Sluigi sbuf_delete(sc->spec); 27198943Sluigi destroy_dev(dev); 27298943Sluigi free(sc, M_LED); 27398943Sluigi} 27498943Sluigi