kbd: merge linker set drivers into standard kbd driver list

This leads to the revert of r355806; this reduces duplication in keyboard
registration and driver switch lookup and leaves us with one authoritative
source for currently registered drivers. The reduced duplication later is
nice as we have more procedure involved in keyboard setup.

keyboard_driver->flags is used to more quickly detect bogus adds/removes.
From KPI consumers' perspective, nothing changes- kbd_add_driver of an
already-registered driver will succeed, and a single kbd_delete_driver will
later remove it as expected. In contrast to historical behavior,
kbd_delete_driver on a driver registered via linker set will now actually
de-register the driver so that it may not be used -- e.g. if kbdmux's
MOD_LOAD handler fails somewhere.

Detection for already-registered drivers in kbd_add_driver has improved, as
the previous SLIST_NEXT(driver) != NULL check would not have caught a driver
that's at the tail end.

kbdinit is now called from cninit() rather than via SYSINIT so that keyboard
drivers are available as early as console drivers. This is particularly
important as cnprobe will, in both syscons and vt, attempt to do any early
configuration of keyboard drivers built-in (see: kbd_configure).

Reviewed by:	imp (earlier version, pre-cninit change)
Differential Revision:	https://reviews.freebsd.org/D22835
This commit is contained in:
kevans 2019-12-26 15:21:34 +00:00
parent afdbc510f4
commit 35193248cd
3 changed files with 51 additions and 45 deletions

View File

@ -70,7 +70,7 @@ static void genkbd_diag(keyboard_t *kbd, int level);
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 */
@ -163,12 +163,18 @@ kbd_set_maps(keyboard_t *kbd, keymap_t *keymap, accentmap_t *accmap,
int
kbd_add_driver(keyboard_driver_t *driver)
{
if (SLIST_NEXT(driver, link))
return (EINVAL);
if ((driver->flags & KBDF_REGISTERED) != 0)
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 |= KBDF_REGISTERED;
SLIST_INSERT_HEAD(&keyboard_drivers, driver, link);
return (0);
}
@ -176,6 +182,11 @@ kbd_add_driver(keyboard_driver_t *driver)
int
kbd_delete_driver(keyboard_driver_t *driver)
{
if ((driver->flags & KBDF_REGISTERED) == 0)
return (EINVAL);
driver->flags &= ~KBDF_REGISTERED;
SLIST_REMOVE(&keyboard_drivers, driver, keyboard_driver, link);
SLIST_NEXT(driver, link) = NULL;
return (0);
@ -185,7 +196,6 @@ kbd_delete_driver(keyboard_driver_t *driver)
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 +236,6 @@ kbd_register(keyboard_t *kbd)
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 +275,12 @@ kbd_unregister(keyboard_t *kbd)
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 +422,12 @@ kbd_get_keyboard(int index)
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);
}
@ -1507,19 +1488,27 @@ kbd_ev_event(keyboard_t *kbd, uint16_t type, uint16_t code, int32_t value)
}
}
static void
kbd_drv_init(void)
void
kbdinit(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;
SYSINIT(kbd_drv_init, SI_SUB_DRIVERS, SI_ORDER_FIRST, kbd_drv_init, NULL);
/*
* The following printfs will almost universally get dropped,
* with exception to kernel configs with EARLY_PRINTF and
* special setups where msgbufinit() is called early with a
* static buffer to capture output occurring before the dynamic
* message buffer is mapped.
*/
if (kbd_add_driver(drv) != 0)
printf("kbd: failed to register driver '%s'\n",
drv->name);
else if (bootverbose)
printf("kbd: registered driver '%s'\n",
drv->name);
}
}

View File

@ -107,8 +107,11 @@ typedef struct keyboard_driver {
keyboard_switch_t * const kbdsw;
/* backdoor for the console driver */
int (* const configure)(int);
int flags;
} keyboard_driver_t;
#define KBDF_REGISTERED 0x0001
/* keyboard */
struct keyboard {
/* the following fields are managed by kbdio */
@ -397,6 +400,9 @@ int kbd_detach(keyboard_t *kbd);
#define LED_MASK (LED_CAP | LED_NUM | LED_SCR)
*/
/* Initialization for the kbd layer, performed by cninit. */
void kbdinit(void);
int genkbd_commonioctl(keyboard_t *kbd, u_long cmd, caddr_t arg);
int genkbd_keyaction(keyboard_t *kbd, int keycode, int up,
int *shiftstate, int *accents);

View File

@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/cons.h>
#include <sys/fcntl.h>
#include <sys/kbio.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
@ -69,6 +70,8 @@ __FBSDID("$FreeBSD$");
#include <ddb/ddb.h>
#include <dev/kbd/kbdreg.h>
#include <machine/cpu.h>
#include <machine/clock.h>
@ -123,6 +126,14 @@ cninit(void)
|RB_VERBOSE
|RB_ASKNAME)) == RB_MUTE);
/*
* Bring up the kbd layer just in time for cnprobe. Console drivers
* have a dependency on kbd being ready, so this fits nicely between the
* machdep callers of cninit() and MI probing/initialization of consoles
* here.
*/
kbdinit();
/*
* Find the first console with the highest priority.
*/