Index: led.c =================================================================== RCS file: /usr/repo/src/sys/dev/led/led.c,v retrieving revision 1.16 diff -u -p -r1.16 led.c --- led.c 7 Mar 2005 11:05:46 -0000 1.16 +++ led.c 30 Mar 2005 06:56:07 -0000 @@ -14,8 +14,10 @@ __FBSDID("$FreeBSD: src/sys/dev/led/led. #include #include #include +#include #include #include +#include #include #include #include @@ -33,6 +35,8 @@ struct ledsc { char *str; char *ptr; int count; +#define LED_DESTROY 0x01 + u_int flags; time_t last_second; }; @@ -45,40 +49,70 @@ static struct callout led_ch; static MALLOC_DEFINE(M_LED, "LED", "LED driver"); static void -led_timeout(void *p) +led_remove(struct ledsc *sc) +{ + + sx_xlock(&led_sx); + free_unr(led_unit, sc->unit); + destroy_dev(sc->dev); + if (sc->spec != NULL) + sbuf_delete(sc->spec); + free(sc, M_LED); + sx_xunlock(&led_sx); +} + +static void +led_thread(void *p) { struct ledsc *sc; mtx_lock(&led_mtx); - LIST_FOREACH(sc, &led_list, list) { - if (sc->ptr == NULL) - continue; - if (sc->count > 0) { - sc->count--; - continue; + for (;;) { + LIST_FOREACH(sc, &led_list, list) { + if ((sc->flags & LED_DESTROY) != 0) { + LIST_REMOVE(sc, list); + mtx_unlock(&led_mtx); + led_remove(sc); + mtx_lock(&led_mtx); + if (LIST_EMPTY(&led_list)) { + wakeup(&led_list); + mtx_unlock(&led_mtx); + kthread_exit(0); + } + wakeup(&led_list); + break; + } } - if (*sc->ptr == '.') { - sc->ptr = NULL; + if (sc != NULL) continue; - } else if (*sc->ptr == 'U' || *sc->ptr == 'u') { - if (sc->last_second == time_second) + LIST_FOREACH(sc, &led_list, list) { + if (sc->ptr == NULL) + continue; + if (sc->count > 0) { + sc->count--; continue; - sc->last_second = time_second; - sc->func(sc->private, *sc->ptr == 'U'); - } else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') { - sc->func(sc->private, 0); - sc->count = (*sc->ptr & 0xf) - 1; - } else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') { - sc->func(sc->private, 1); - sc->count = (*sc->ptr & 0xf) - 1; + } + if (*sc->ptr == '.') { + sc->ptr = NULL; + continue; + } else if (*sc->ptr == 'U' || *sc->ptr == 'u') { + if (sc->last_second == time_second) + continue; + sc->last_second = time_second; + sc->func(sc->private, *sc->ptr == 'U'); + } else if (*sc->ptr >= 'a' && *sc->ptr <= 'j') { + sc->func(sc->private, 0); + sc->count = (*sc->ptr & 0xf) - 1; + } else if (*sc->ptr >= 'A' && *sc->ptr <= 'J') { + sc->func(sc->private, 1); + sc->count = (*sc->ptr & 0xf) - 1; + } + sc->ptr++; + if (*sc->ptr == '\0') + sc->ptr = sc->str; } - sc->ptr++; - if (*sc->ptr == '\0') - sc->ptr = sc->str; + msleep(&sc, &led_mtx, PUSER, "led", hz / 10); } - mtx_unlock(&led_mtx); - callout_reset(&led_ch, hz / 10, led_timeout, p); - return; } static int @@ -123,7 +157,9 @@ led_write(struct cdev *dev, struct uio * if (uio->uio_resid > 512) return (EINVAL); - s2 = s = malloc(uio->uio_resid + 1, M_DEVBUF, M_WAITOK); + s2 = s = malloc(uio->uio_resid + 1, M_DEVBUF, M_NOWAIT); + if (s2 == NULL) + return (ENOMEM); s[uio->uio_resid] = '\0'; error = uiomove(s, uio->uio_resid, uio); if (error) { @@ -242,7 +278,8 @@ static struct cdevsw led_cdevsw = { struct cdev * led_create(led_t *func, void *priv, char const *name) { - struct ledsc *sc; + struct ledsc *sc; + int launch = 0; sc = malloc(sizeof *sc, M_LED, M_WAITOK | M_ZERO); @@ -250,6 +287,7 @@ led_create(led_t *func, void *priv, char sc->unit = alloc_unr(led_unit); sc->private = priv; sc->func = func; + sc->flags = 0; sc->dev = make_dev(&led_cdevsw, unit2minor(sc->unit), UID_ROOT, GID_WHEEL, 0600, "led/%s", name); sx_xunlock(&led_sx); @@ -257,10 +295,12 @@ led_create(led_t *func, void *priv, char mtx_lock(&led_mtx); sc->dev->si_drv1 = sc; if (LIST_EMPTY(&led_list)) - callout_reset(&led_ch, hz / 10, led_timeout, NULL); + launch = 1; LIST_INSERT_HEAD(&led_list, sc, list); sc->func(sc->private, 0); mtx_unlock(&led_mtx); + if (launch) + kthread_create(led_thread, NULL, NULL, 0, 0, "led"); return (sc->dev); } @@ -268,28 +308,29 @@ led_create(led_t *func, void *priv, char void led_destroy(struct cdev *dev) { - struct ledsc *sc; + struct ledsc *sc, *lsc; mtx_lock(&led_mtx); sc = dev->si_drv1; + KASSERT(sc != NULL, ("led already destroyed")); dev->si_drv1 = NULL; - - LIST_REMOVE(sc, list); - if (LIST_EMPTY(&led_list)) - callout_stop(&led_ch); + sc->flags |= LED_DESTROY; + for (;;) { + /* Is it still on the list? */ + LIST_FOREACH(lsc, &led_list, list) { + if (lsc == sc) + break; + } + if (lsc == NULL) + break; + else + msleep(&led_list, &led_mtx, PUSER, "led", 0); + } mtx_unlock(&led_mtx); - - sx_xlock(&led_sx); - free_unr(led_unit, sc->unit); - destroy_dev(dev); - if (sc->spec != NULL) - sbuf_delete(sc->spec); - free(sc, M_LED); - sx_xunlock(&led_sx); } static void -led_drvinit(void *unused) +led_drvinit(void) { led_unit = new_unrhdr(0, minor2unit(MAXMINOR), NULL); @@ -298,4 +339,43 @@ led_drvinit(void *unused) callout_init(&led_ch, CALLOUT_MPSAFE); } -SYSINIT(leddev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, led_drvinit, NULL); +static void +led_drvfini(void) +{ + + delete_unrhdr(led_unit); + sx_destroy(&led_sx); + mtx_destroy(&led_mtx); +} + +static int +led_modevent(module_t mod, int type, void *data) +{ + int error = 0; + + switch (type) { + case MOD_LOAD: + led_drvinit(); + break; + case MOD_UNLOAD: + mtx_lock(&led_mtx); + if (LIST_EMPTY(&led_list)) + led_drvfini(); + else { + mtx_unlock(&led_mtx); + error = EBUSY; + } + break; + default: + error = EOPNOTSUPP; + break; + } + return (error); +} +static moduledata_t led_module = { + "led", + led_modevent, + NULL +}; +DECLARE_MODULE(led, led_module, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); +MODULE_VERSION(led, 1);