led.c revision 130585
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 130585 2004-06-16 09:47:26Z 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; 29130585Sphk struct cdev *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 } 55128678Sphk if (*sc->ptr == '.') { 56128678Sphk sc->ptr = NULL; 57128678Sphk continue; 58128678Sphk } else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') { 59123494Sphk sc->func(sc->private, 0); 60128678Sphk } else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') { 61123494Sphk sc->func(sc->private, 1); 62128678Sphk } 63123494Sphk sc->count = *sc->ptr & 0xf; 64128867Sphk sc->count--; 65123494Sphk sc->ptr++; 66123494Sphk if (*sc->ptr == '\0') 67121941Sphk sc->ptr = sc->str; 68121941Sphk } 69121941Sphk mtx_unlock(&led_mtx); 70121941Sphk timeout(led_timeout, p, hz / 10); 71121941Sphk return; 72121941Sphk} 73121941Sphk 74121941Sphkstatic int 75130585Sphkled_write(struct cdev *dev, struct uio *uio, int ioflag) 76121941Sphk{ 77121941Sphk int error; 78123494Sphk char *s, *s2; 79121941Sphk struct ledsc *sc; 80121941Sphk struct sbuf *sb; 81121941Sphk struct sbuf *sb2; 82121941Sphk int i; 83121941Sphk 84121941Sphk sc = dev->si_drv1; 85121941Sphk 86121941Sphk if (uio->uio_resid > 512) 87121941Sphk return (EINVAL); 88123494Sphk s2 = s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK); 89121941Sphk if (s == NULL) 90121941Sphk return (ENOMEM); 91121941Sphk s[uio->uio_resid] = '\0'; 92121941Sphk error = uiomove(s, uio->uio_resid, uio); 93121941Sphk if (error) { 94123494Sphk free(s2, M_DEVBUF); 95121941Sphk return (error); 96121941Sphk } 97121941Sphk 98121941Sphk /* 99121941Sphk * Handle "on" and "off" immediately so people can flash really 100121941Sphk * fast from userland if they want to 101121941Sphk */ 102121941Sphk if (*s == '0' || *s == '1') { 103121941Sphk mtx_lock(&led_mtx); 104121941Sphk sb2 = sc->spec; 105121941Sphk sc->spec = NULL; 106121941Sphk sc->str = NULL; 107121941Sphk sc->ptr = NULL; 108121941Sphk sc->count = 0; 109121941Sphk sc->func(sc->private, *s & 1); 110121941Sphk mtx_unlock(&led_mtx); 111121941Sphk if (sb2 != NULL) 112121941Sphk sbuf_delete(sb2); 113123494Sphk free(s2, M_DEVBUF); 114121941Sphk return(0); 115121941Sphk } 116121941Sphk 117121941Sphk sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 118121941Sphk if (sb == NULL) { 119123494Sphk free(s2, M_DEVBUF); 120121941Sphk return (ENOMEM); 121121941Sphk } 122121941Sphk 123121941Sphk switch(s[0]) { 124121941Sphk /* 125121941Sphk * Flash, default is 100msec/100msec. 126121941Sphk * 'f2' sets 200msec/200msec etc. 127121941Sphk */ 128121941Sphk case 'f': 129121941Sphk if (s[1] >= '1' && s[1] <= '9') 130121941Sphk i = s[1] - '1'; 131121941Sphk else 132121941Sphk i = 0; 133123014Sphk sbuf_printf(sb, "%c%c", 'A' + i, 'a' + i); 134121941Sphk break; 135121941Sphk /* 136121941Sphk * Digits, flashes out numbers. 137121941Sphk * 'd12' becomes -__________-_-______________________________ 138121941Sphk */ 139121941Sphk case 'd': 140121941Sphk for(s++; *s; s++) { 141121941Sphk if (!isdigit(*s)) 142121941Sphk continue; 143121941Sphk i = *s - '0'; 144121941Sphk if (i == 0) 145121941Sphk i = 10; 146121941Sphk for (; i > 1; i--) 147123014Sphk sbuf_cat(sb, "Aa"); 148123014Sphk sbuf_cat(sb, "Aj"); 149121941Sphk } 150123014Sphk sbuf_cat(sb, "jj"); 151121941Sphk break; 152121941Sphk /* 153121941Sphk * String, roll your own. 154123014Sphk * 'a-j' gives "off" for n/10 sec. 155123014Sphk * 'A-J' gives "on" for n/10 sec. 156121941Sphk * no delay before repeat 157121941Sphk * 'sAaAbBa' becomes _-_--__- 158121941Sphk */ 159121941Sphk case 's': 160121941Sphk for(s++; *s; s++) { 161128678Sphk if ((*s >= 'a' && *s <= 'j') || 162128678Sphk (*s >= 'A' && *s <= 'J') || 163128678Sphk *s == '.') 164128678Sphk sbuf_bcat(sb, s, 1); 165121941Sphk } 166121941Sphk break; 167121941Sphk /* 168121941Sphk * Morse. 169121941Sphk * '.' becomes _- 170121941Sphk * '-' becomes _--- 171121941Sphk * ' ' becomes __ 172121941Sphk * '\n' becomes ____ 173121941Sphk * 1sec pause between repeats 174121941Sphk * '... --- ...' -> _-_-_-___---_---_---___-_-_-__________ 175121941Sphk */ 176121941Sphk case 'm': 177121941Sphk for(s++; *s; s++) { 178121941Sphk if (*s == '.') 179123014Sphk sbuf_cat(sb, "aA"); 180121941Sphk else if (*s == '-') 181123014Sphk sbuf_cat(sb, "aC"); 182121941Sphk else if (*s == ' ') 183123014Sphk sbuf_cat(sb, "b"); 184121956Sphk else if (*s == '\n') 185123014Sphk sbuf_cat(sb, "d"); 186121941Sphk } 187123014Sphk sbuf_cat(sb, "j"); 188121941Sphk break; 189121941Sphk default: 190123494Sphk sbuf_delete(sb); 191123494Sphk free(s2, M_DEVBUF); 192123494Sphk return (EINVAL); 193121941Sphk } 194121941Sphk sbuf_finish(sb); 195123494Sphk free(s2, M_DEVBUF); 196121941Sphk if (sbuf_overflowed(sb)) { 197121941Sphk sbuf_delete(sb); 198121941Sphk return (ENOMEM); 199121941Sphk } 200121941Sphk if (sbuf_len(sb) == 0) { 201121941Sphk sbuf_delete(sb); 202121941Sphk return (0); 203121941Sphk } 204121941Sphk 205121941Sphk mtx_lock(&led_mtx); 206121941Sphk sb2 = sc->spec; 207121941Sphk sc->spec = sb; 208121941Sphk sc->str = sbuf_data(sb); 209121941Sphk sc->ptr = sc->str; 210121941Sphk sc->count = 0; 211121941Sphk mtx_unlock(&led_mtx); 212121941Sphk if (sb2 != NULL) 213121941Sphk sbuf_delete(sb2); 214121941Sphk return(0); 215121941Sphk} 216121941Sphk 217121941Sphkstatic struct cdevsw led_cdevsw = { 218126080Sphk .d_version = D_VERSION, 219126080Sphk .d_flags = D_NEEDGIANT, 220125810Sphk .d_write = led_write, 221125810Sphk .d_name = "LED", 222121941Sphk}; 223121941Sphk 224130585Sphkstruct cdev * 225121941Sphkled_create(led_t *func, void *priv, char const *name) 226121941Sphk{ 227121941Sphk struct ledsc *sc; 228121941Sphk struct sbuf *sb; 229121941Sphk 230121941Sphk if (next_minor == 0) { 231123272Struckman mtx_init(&led_mtx, "LED mtx", NULL, MTX_DEF); 232121941Sphk timeout(led_timeout, NULL, hz / 10); 233121941Sphk } 234121941Sphk 235121941Sphk sb = sbuf_new(NULL, NULL, SPECNAMELEN, SBUF_FIXEDLEN); 236121941Sphk if (sb == NULL) 237121941Sphk return (NODEV); 238121941Sphk sbuf_cpy(sb, "led/"); 239121941Sphk sbuf_cat(sb, name); 240121941Sphk sbuf_finish(sb); 241121941Sphk if (sbuf_overflowed(sb)) { 242121941Sphk sbuf_delete(sb); 243121941Sphk return (NODEV); 244121941Sphk } 245121941Sphk 246121941Sphk sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO); 247121941Sphk sc->private = priv; 248121941Sphk sc->func = func; 249121941Sphk sc->dev = make_dev(&led_cdevsw, unit2minor(next_minor), 250121941Sphk UID_ROOT, GID_WHEEL, 0600, sbuf_data(sb)); 251121941Sphk sc->dev->si_drv1 = sc; 252121941Sphk next_minor++; 253121941Sphk sbuf_delete(sb); 254121941Sphk mtx_lock(&led_mtx); 255121941Sphk LIST_INSERT_HEAD(&led_list, sc, list); 256128678Sphk sc->func(sc->private, 0); 257121941Sphk mtx_unlock(&led_mtx); 258121941Sphk return (sc->dev); 259121941Sphk} 260121941Sphk 261121941Sphkvoid 262130585Sphkled_destroy(struct cdev *dev) 263121941Sphk{ 264121941Sphk struct ledsc *sc; 265121941Sphk 266121941Sphk sc = dev->si_drv1; 267121941Sphk mtx_lock(&led_mtx); 268121941Sphk LIST_REMOVE(sc, list); 269121941Sphk mtx_unlock(&led_mtx); 270122963Sphk if (sc->spec != NULL) 271122963Sphk sbuf_delete(sc->spec); 272121941Sphk destroy_dev(dev); 273121941Sphk free(sc, M_LED); 274121941Sphk} 275