The ukbd driver has some questionable "magic" to for a default keyboard

which is ukbd0.  Specifically, the keyboard driver structures for ukbd0
are not allocated/freed but are statically allocated via a persistent
global variable.  There is some additional magic for the ukbd0 such that
if the keyboard is marked as probed in this global variable, then we
don't check to see if the device_t we are probing has an interface.

This causes a problem if an attach of ukbd0 fails without fulling clearing
the state in the global variable.  Specifically, if the keyboard fails to
initialize in init_keyboard() or kbd_register(), then the keyboard will
still be marked as probed.  The USB layer will then try to offer the
"generic" version of the USB keyboard device (as opposed to the
per-interface sub-devices) and the ukbd(4) driver will see that the
keyboard is marked probe and will skip the "is this a per-interface device"
check.  Later in ukbd_attach() it panics because it tries to dereference
the interface pointer which is NULL.

The fix is to clear the flags in the persistent keyboard data for ukbd0
when init_keyboard() or kbd_register() fail.

MFC after:	1 week
Reviewed by:	imp
This commit is contained in:
John Baldwin 2007-10-12 19:47:42 +00:00
parent faf529dce5
commit 6b66deaeff
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=172604

View file

@ -598,14 +598,20 @@ ukbd_init(int unit, keyboard_t **kbdp, void *arg, int flags)
if (!KBD_IS_INITIALIZED(kbd) && !(flags & KB_CONF_PROBE_ONLY)) {
if (KBD_HAS_DEVICE(kbd)
&& init_keyboard((ukbd_state_t *)kbd->kb_data,
&kbd->kb_type, kbd->kb_flags))
&kbd->kb_type, kbd->kb_flags)) {
kbd->kb_flags = 0;
/* XXX: Missing free()'s */
return ENXIO;
}
ukbd_ioctl(kbd, KDSETLED, (caddr_t)&(state->ks_state));
KBD_INIT_DONE(kbd);
}
if (!KBD_IS_CONFIGURED(kbd)) {
if (kbd_register(kbd) < 0)
if (kbd_register(kbd) < 0) {
kbd->kb_flags = 0;
/* XXX: Missing free()'s */
return ENXIO;
}
if (ukbd_enable_intr(kbd, TRUE, (usbd_intr_t *)data[1]) == 0)
ukbd_timeout((void *)kbd);
KBD_CONFIG_DONE(kbd);