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