Index: sys/dev/kbd/kbd.c =================================================================== --- sys/dev/kbd/kbd.c +++ sys/dev/kbd/kbd.c @@ -70,7 +70,7 @@ static SLIST_HEAD(, keyboard_driver) keyboard_drivers = SLIST_HEAD_INITIALIZER(keyboard_drivers); -SET_DECLARE(kbddriver_set, const keyboard_driver_t); +SET_DECLARE(kbddriver_set, keyboard_driver_t); /* local arrays */ @@ -159,23 +159,70 @@ kbd->kb_fkeytab_size = fkeymap_size; } -/* declare a new keyboard driver */ -int -kbd_add_driver(keyboard_driver_t *driver) +/* + * kbd_add_driver_internal: declare a new keyboard driver. This may be called + * up to twice for a given keyboard driver: once if it's built into the kernel, + * and again if it's also been declared as a module and it's built into the + * kernel. The first call will always add the driver to the driver list, and + * the second call (post-linker set registration) will just mark the driver as + * officially registered so we reject any later calls. + */ +static int +kbd_add_driver_internal(keyboard_driver_t *driver, bool linker_set) { - if (SLIST_NEXT(driver, link)) + + KASSERT(!linker_set || (driver->flags & KBDF_SET_REGISTERED) == 0, + ("%s: double registration from linker set for '%s'", + __func__, driver->name)); + if ((driver->flags & KBDF_REGISTERED) != 0) return (EINVAL); + + /* + * No further action required if the driver has already been registered + * as a linker set driver. Just mark it as fully registered and return, + * it has already been added to the driver list in the first pass. + */ + if (!linker_set && (driver->flags & KBDF_SET_REGISTERED) != 0) { + driver->flags |= KBDF_REGISTERED; + return (0); + } + + KASSERT(SLIST_NEXT(driver, link) == NULL, + ("%s: keyboard driver list garbage detected", __func__)); if (driver->kbdsw->get_fkeystr == NULL) driver->kbdsw->get_fkeystr = genkbd_get_fkeystr; if (driver->kbdsw->diag == NULL) driver->kbdsw->diag = genkbd_diag; + + driver->flags |= (linker_set ? KBDF_SET_REGISTERED : KBDF_REGISTERED); SLIST_INSERT_HEAD(&keyboard_drivers, driver, link); return (0); } +int +kbd_add_driver(keyboard_driver_t *driver) +{ + + return (kbd_add_driver_internal(driver, false)); +} + int kbd_delete_driver(keyboard_driver_t *driver) { + + /* + * Imbalanced kbd_add_driver/kbd_delete_driver. Technically one can + * call kbd_delete_driver without having callded kbd_add_driver if + * there's been some sort of error and the driver will not be + * functional. Most likely these will already have a matching + * kbd_add_driver because they're processing module events and doing + * setup along those lines. We don't discriminate against in-kernel + * only drivers that wish to unregister, though. + */ + if ((driver->flags & KBDF_REGISTRATION_MASK) == 0) + return (EINVAL); + + driver->flags &= ~KBDF_REGISTRATION_MASK; SLIST_REMOVE(&keyboard_drivers, driver, keyboard_driver, link); SLIST_NEXT(driver, link) = NULL; return (0); @@ -185,7 +232,6 @@ int kbd_register(keyboard_t *kbd) { - const keyboard_driver_t **list; const keyboard_driver_t *p; keyboard_t *mux; keyboard_info_t ki; @@ -226,23 +272,6 @@ return (index); } } - SET_FOREACH(list, kbddriver_set) { - p = *list; - if (strcmp(p->name, kbd->kb_name) == 0) { - kbd->kb_drv = p; - keyboard[index] = kbd; - - if (mux != NULL) { - bzero(&ki, sizeof(ki)); - strcpy(ki.kb_name, kbd->kb_name); - ki.kb_unit = kbd->kb_unit; - - (void)kbdd_ioctl(mux, KBADDKBD, (caddr_t) &ki); - } - - return (index); - } - } return (-1); } @@ -282,18 +311,12 @@ keyboard_switch_t * kbd_get_switch(char *driver) { - const keyboard_driver_t **list; const keyboard_driver_t *p; SLIST_FOREACH(p, &keyboard_drivers, link) { if (strcmp(p->name, driver) == 0) return (p->kbdsw); } - SET_FOREACH(list, kbddriver_set) { - p = *list; - if (strcmp(p->name, driver) == 0) - return (p->kbdsw); - } return (NULL); } @@ -435,18 +458,12 @@ int kbd_configure(int flags) { - const keyboard_driver_t **list; const keyboard_driver_t *p; SLIST_FOREACH(p, &keyboard_drivers, link) { if (p->configure != NULL) (*p->configure)(flags); } - SET_FOREACH(list, kbddriver_set) { - p = *list; - if (p->configure != NULL) - (*p->configure)(flags); - } return (0); } @@ -1510,16 +1527,19 @@ static void kbd_drv_init(void) { - const keyboard_driver_t **list; - const keyboard_driver_t *p; + keyboard_driver_t *drv, **list; SET_FOREACH(list, kbddriver_set) { - p = *list; - if (p->kbdsw->get_fkeystr == NULL) - p->kbdsw->get_fkeystr = genkbd_get_fkeystr; - if (p->kbdsw->diag == NULL) - p->kbdsw->diag = genkbd_diag; + drv = *list; + + if (kbd_add_driver_internal(drv, true) != 0) + printf("kbd: failed to register driver '%s'\n", + drv->name); + else if (bootverbose) + printf("kbd: registered driver '%s'\n", + drv->name); } + } SYSINIT(kbd_drv_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, kbd_drv_init, NULL); Index: sys/dev/kbd/kbdreg.h =================================================================== --- sys/dev/kbd/kbdreg.h +++ sys/dev/kbd/kbdreg.h @@ -107,8 +107,14 @@ keyboard_switch_t * const kbdsw; /* backdoor for the console driver */ int (* const configure)(int); + int flags; } keyboard_driver_t; +#define KBDF_SET_REGISTERED 0x0001 +#define KBDF_REGISTERED 0x0002 + +#define KBDF_REGISTRATION_MASK (KBDF_SET_REGISTERED | KBDF_REGISTERED) + /* keyboard */ struct keyboard { /* the following fields are managed by kbdio */