Sick of not being able to use DDB on a legacy-free PC which doesn't have a PS/2 keyboard or serial port? I know I am! On machines with pckbd and pckbc, the attach of pckbd to the console always succeeds. This is so that we can plug a keyboard into the machine later on and have it work, and also means that a legacy emulated keyboard will work in "boot -c". But it also means that USB keyboards don't get a chance to become the console keyboard (i.e. one that can interact with DDB). This patch checks the result of an "enable" command to the keyboard early in the boot and then before and after we attach each of the USB host controllers. If we see the keyboard stop responding after we probed a USB controller, then it's likely that the keyboard controller was really a SMM emulation using the host controller we just probed. Having detected this, we can then work some magic and let the first USB keyboard to attach become the console keyboard. If we got it wrong and the keyboard wasn't actually USB (say, someone pulled the keyboard out during boot), a real PC keyboard plugged in will still work in DDB so long as there isn't a USB keyboard attached. I'm interested to hear whether you have any legacy free machines where this doesn't say "Xhci: was performing legacy keyboard emulation". If so, we'll need another way to detect that the keyboard was emulated. Index: dev/pci/ehci_pci.c =================================================================== RCS file: /cvs/src/sys/dev/pci/ehci_pci.c,v retrieving revision 1.10 diff -u -p -r1.10 ehci_pci.c --- dev/pci/ehci_pci.c 25 Aug 2006 04:17:00 -0000 1.10 +++ dev/pci/ehci_pci.c 11 Mar 2007 02:50:45 -0000 @@ -57,6 +57,15 @@ #include #include +#include "pckbc.h" +#include "pckbd.h" +#include "ukbd.h" +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 +#include +#include +#include +#endif + #ifdef EHCI_DEBUG #define DPRINTF(x) if (ehcidebug) printf x extern int ehcidebug; @@ -110,6 +119,11 @@ ehci_pci_attach(struct device *parent, s char *devname = sc->sc.sc_bus.bdev.dv_xname; usbd_status r; int s; +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 + int pckbd_chk; + + pckbd_chk = pckbd_cncheck(); +#endif /* Map I/O registers */ if (pci_mapreg_map(pa, PCI_CBMEM, PCI_MAPREG_TYPE_MEM, 0, @@ -181,6 +195,15 @@ ehci_pci_attach(struct device *parent, s sc->sc.sc_shutdownhook = shutdownhook_establish(ehci_pci_shutdown, sc); splx(s); + +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 + /* Check if the PC keyboard stopped responding when we attached. */ + if (pckbd_chk == PCKBD_CNCHECK_NO_CHANGE && + pckbd_cncheck() == PCKBD_CNCHECK_CONSOLE_CHANGED) { + /* It did? Let ukbd attach a keyboard to the console. */ + ukbd_cnsteal_ok(devname); + } +#endif /* Attach usb device. */ sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus, Index: dev/pci/ohci_pci.c =================================================================== RCS file: /cvs/src/sys/dev/pci/ohci_pci.c,v retrieving revision 1.29 diff -u -p -r1.29 ohci_pci.c --- dev/pci/ohci_pci.c 22 May 2006 16:09:21 -0000 1.29 +++ dev/pci/ohci_pci.c 11 Mar 2007 02:50:45 -0000 @@ -64,6 +64,15 @@ #include #include +#include "pckbc.h" +#include "pckbd.h" +#include "ukbd.h" +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 +#include +#include +#include +#endif + int ohci_pci_match(struct device *, void *, void *); void ohci_pci_attach(struct device *, struct device *, void *); int ohci_pci_detach(struct device *, int); @@ -104,6 +113,11 @@ ohci_pci_attach(struct device *parent, s int s; const char *vendor; char *devname = sc->sc.sc_bus.bdev.dv_xname; +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 + int pckbd_chk; + + pckbd_chk = pckbd_cncheck(); +#endif /* Map I/O registers */ if (pci_mapreg_map(pa, PCI_CBMEM, PCI_MAPREG_TYPE_MEM, 0, @@ -174,6 +188,15 @@ ohci_pci_attach(struct device *parent, s sc->sc.sc_bus.bdev.dv_xname); splx(s); + +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 + /* Check if the PC keyboard stopped responding when we attached. */ + if (pckbd_chk == PCKBD_CNCHECK_NO_CHANGE && + pckbd_cncheck() == PCKBD_CNCHECK_CONSOLE_CHANGED) { + /* It did? Let ukbd attach a keyboard to the console. */ + ukbd_cnsteal_ok(devname); + } +#endif /* Attach usb device. */ sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus, Index: dev/pci/uhci_pci.c =================================================================== RCS file: /cvs/src/sys/dev/pci/uhci_pci.c,v retrieving revision 1.22 diff -u -p -r1.22 uhci_pci.c --- dev/pci/uhci_pci.c 25 Aug 2006 04:17:00 -0000 1.22 +++ dev/pci/uhci_pci.c 11 Mar 2007 02:50:45 -0000 @@ -57,6 +57,15 @@ #include #include +#include "pckbc.h" +#include "pckbd.h" +#include "ukbd.h" +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 +#include +#include +#include +#endif + int uhci_pci_match(struct device *, void *, void *); void uhci_pci_attach(struct device *, struct device *, void *); int uhci_pci_detach(struct device *, int); @@ -99,6 +108,9 @@ uhci_pci_attach(struct device *parent, s char *devname = sc->sc.sc_bus.bdev.dv_xname; usbd_status r; int s; +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 + int pckbd_chk; +#endif #if defined(__NetBSD__) char devinfo[256]; @@ -106,6 +118,9 @@ uhci_pci_attach(struct device *parent, s pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo); printf(": %s (rev. 0x%02x)", devinfo, PCI_REVISION(pa->pa_class)); #endif +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 + pckbd_chk = pckbd_cncheck(); +#endif /* Map I/O registers */ if (pci_mapreg_map(pa, PCI_CBIO, PCI_MAPREG_TYPE_IO, 0, @@ -179,6 +194,15 @@ uhci_pci_attach(struct device *parent, s goto unmap_ret; } splx(s); + +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 + /* Check if the PC keyboard stopped responding when we attached. */ + if (pckbd_chk == PCKBD_CNCHECK_NO_CHANGE && + pckbd_cncheck() == PCKBD_CNCHECK_CONSOLE_CHANGED) { + /* It did? Let ukbd attach a keyboard to the console. */ + ukbd_cnsteal_ok(devname); + } +#endif /* Attach usb device. */ sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus, Index: dev/pckbc/pckbd.c =================================================================== RCS file: /cvs/src/sys/dev/pckbc/pckbd.c,v retrieving revision 1.9 diff -u -p -r1.9 pckbd.c --- dev/pckbc/pckbd.c 30 Jan 2007 20:45:05 -0000 1.9 +++ dev/pckbc/pckbd.c 11 Mar 2007 02:50:45 -0000 @@ -102,6 +102,13 @@ #include /* XXX for hz */ #endif +#include "pckbc.h" +#include "ukbd.h" +#if NPCKBC > 0 && NUKBD > 0 +#define PCKBD_BOOT_CNCHECK_NOT_DONE 0x5a5a5a5a +int pckbd_boot_cncheck_result = PCKBD_BOOT_CNCHECK_NOT_DONE; +#endif + struct pckbd_internal { int t_isconsole; pckbc_tag_t t_kbctag; @@ -648,6 +655,40 @@ pckbd_hookup_bell(fn, arg) } } +#if NPCKBC > 0 && NUKBD > 0 +/* + * Check the response to a keyboard enable command and compare it to the + * response to the same command performed early in the boot process. If the + * response has changed, it is likely that a USB controller was performing + * legacy emulation. + */ +int +pckbd_cncheck(void) +{ + static int already_changed = 0; + char cmd[1]; + int res; + + if (pckbd_boot_cncheck_result == PCKBD_BOOT_CNCHECK_NOT_DONE + || !pckbd_consdata.t_isconsole) + return PCKBD_CNCHECK_NOT_CONSOLE; + + if (already_changed) + return PCKBD_CNCHECK_CONSOLE_CHANGED; + + cmd[0] = KBC_ENABLE; + res = pckbc_poll_cmd(pckbd_consdata.t_kbctag, + pckbd_consdata.t_kbcslot, cmd, 1, 0, 0, 0); + + if (res == pckbd_boot_cncheck_result) + return PCKBD_CNCHECK_NO_CHANGE; + + already_changed = 1; + + return PCKBD_CNCHECK_CONSOLE_CHANGED; +} +#endif + int pckbd_cnattach(kbctag, kbcslot) pckbc_tag_t kbctag; @@ -668,6 +709,11 @@ pckbd_cnattach(kbctag, kbcslot) #if 0 if (res) return (res); +#endif + +#if NPCKBC > 0 && NUKBD > 0 + /* Store the initial response to the enable command. */ + pckbd_boot_cncheck_result = res; #endif wskbd_cnattach(&pckbd_consops, &pckbd_consdata, &pckbd_keymapdata); Index: dev/pckbc/pckbdvar.h =================================================================== RCS file: /cvs/src/sys/dev/pckbc/pckbdvar.h,v retrieving revision 1.3 diff -u -p -r1.3 pckbdvar.h --- dev/pckbc/pckbdvar.h 14 Mar 2002 03:16:07 -0000 1.3 +++ dev/pckbc/pckbdvar.h 11 Mar 2007 02:50:45 -0000 @@ -4,3 +4,9 @@ int pckbd_cnattach(pckbc_tag_t, int); void pckbd_hookup_bell(void (*fn)(void *, u_int, u_int, u_int, int), void *); + +/* Hooks so that USB can steal the console after legacy handover */ +#define PCKBD_CNCHECK_NOT_CONSOLE (-1) +#define PCKBD_CNCHECK_CONSOLE_CHANGED (0) +#define PCKBD_CNCHECK_NO_CHANGE (1) +int pckbd_cncheck(void); Index: dev/usb/ukbd.c =================================================================== RCS file: /cvs/src/sys/dev/usb/ukbd.c,v retrieving revision 1.27 diff -u -p -r1.27 ukbd.c --- dev/usb/ukbd.c 11 Feb 2007 20:29:22 -0000 1.27 +++ dev/usb/ukbd.c 11 Mar 2007 02:50:47 -0000 @@ -75,6 +75,14 @@ #include #include +#include "pckbc.h" +#include "pckbd.h" +#if NPCKBC > 0 && NPCKBD > 0 +#include +#include +#include +#endif + #ifdef UKBD_DEBUG #define DPRINTF(x) do { if (ukbddebug) logprintf x; } while (0) #define DPRINTFN(n,x) do { if (ukbddebug>(n)) logprintf x; } while (0) @@ -426,6 +434,14 @@ USB_ATTACH(ukbd) printf("\n"); +#if NPCKBC > 0 && NPCKBD > 0 + if (sc->sc_console_keyboard == 2) { + printf("%s: ", USBDEVNAME(sc->sc_hdev.sc_dev)); + wskbd_cnsteal(); + printf("\n"); + } +#endif + if (sc->sc_console_keyboard) { DPRINTF(("ukbd_attach: console keyboard sc=%p\n", sc)); wskbd_cnattach(&ukbd_consops, sc, &ukbd_keymapdata); @@ -522,10 +538,17 @@ USB_DETACH(ukbd) * XXX Should notify some other keyboard that it can be * XXX console, if there are any other keyboards. */ - printf("%s: was console keyboard\n", + printf("%s: was console keyboard", USBDEVNAME(sc->sc_hdev.sc_dev)); wskbd_cndetach(); - ukbd_is_console = 1; + ukbd_is_console = sc->sc_console_keyboard; +#if NPCKBC > 0 && NPCKBD > 0 + if (sc->sc_console_keyboard == 2) { + printf(", "); + wskbd_cnreturn(); + } +#endif + printf("\n"); #endif } /* No need to do reference counting of ukbd, wskbd has all the goo. */ @@ -873,6 +896,18 @@ ukbd_cnattach(void) ukbd_is_console = 1; return (0); } + +#if NPCKBC > 0 && NPCKBD > 0 +void +ukbd_cnsteal_ok(char *hc_name) +{ + if (ukbd_is_console != 0) + return; + + printf("%s: was performing legacy keyboard emulation\n", hc_name); + ukbd_is_console = 2; +} +#endif const char * ukbd_parse_desc(struct ukbd_softc *sc) Index: dev/usb/ukbdvar.h =================================================================== RCS file: /cvs/src/sys/dev/usb/ukbdvar.h,v retrieving revision 1.2 diff -u -p -r1.2 ukbdvar.h --- dev/usb/ukbdvar.h 8 Nov 2000 18:10:38 -0000 1.2 +++ dev/usb/ukbdvar.h 11 Mar 2007 02:50:47 -0000 @@ -42,5 +42,6 @@ #define _DEV_USB_UKBDVAR_H_ int ukbd_cnattach(void); +void ukbd_cnsteal_ok(char *); #endif /* _DEV_USB_UKBDVAR_H_ */ Index: dev/wscons/wskbd.c =================================================================== RCS file: /cvs/src/sys/dev/wscons/wskbd.c,v retrieving revision 1.53 diff -u -p -r1.53 wskbd.c --- dev/wscons/wskbd.c 14 Aug 2006 17:41:08 -0000 1.53 +++ dev/wscons/wskbd.c 11 Mar 2007 02:50:47 -0000 @@ -106,6 +106,9 @@ #include #include "audio.h" /* NAUDIO (mixer tuning) */ +#include "pckbc.h" +#include "pckbd.h" +#include "ukbd.h" #include "wsdisplay.h" #include "wskbd.h" #include "wsmux.h" @@ -295,6 +298,9 @@ void wskbd_repeat(void *v); static int wskbd_console_initted; static struct wskbd_softc *wskbd_console_device; +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 +static struct wskbd_softc *stolen_wskbd_console_device; +#endif static struct wskbd_internal wskbd_console_data; void wskbd_update_layout(struct wskbd_internal *, kbd_t); @@ -477,7 +483,71 @@ wskbd_cnattach(const struct wskbd_consop wskbd_console_initted = 1; } -void +#if NPCKBC > 0 && NPCKBD > 0 && NUKBD > 0 +void +wskbd_cnsteal(void) +{ + struct wskbd_internal *newid; + int s; + + KASSERT(wskbd_console_initted); + KASSERT(wskbd_console_device != NULL); + KASSERT(wskbd_console_device == wskbd_console_data.t_sc); + KASSERT(stolen_wskbd_console_device == NULL); + + printf("stealing console keyboard role from %s", + wskbd_console_device->sc_base.me_dv.dv_xname); + + newid = malloc(sizeof(struct wskbd_internal), M_DEVBUF, M_WAITOK); + s = spltty(); + memcpy(newid, &wskbd_console_data, sizeof(struct wskbd_internal)); + wskbd_console_device->id = newid; + wskbd_console_device->sc_isconsole = 0; + stolen_wskbd_console_device = wskbd_console_device; + wskbd_console_device = NULL; + splx(s); + +#if NWSDISPLAY > 0 + wsdisplay_unset_cons_kbd(); +#endif + + wskbd_console_initted = 0; +} + +void +wskbd_cnreturn(void) +{ + struct wskbd_internal *oldid; + int s; + + KASSERT(!wskbd_console_initted); + KASSERT(stolen_wskbd_console_device != NULL); + KASSERT(stolen_wskbd_console_device != wskbd_console_data.t_sc); + + printf("returning console keyboard role to %s", + stolen_wskbd_console_device->sc_base.me_dv.dv_xname); + + /* Restore keyboard state */ + s = spltty(); + memcpy(&wskbd_console_data, stolen_wskbd_console_device->id, + sizeof(struct wskbd_internal)); + oldid = stolen_wskbd_console_device->id; + stolen_wskbd_console_device->id = &wskbd_console_data; + stolen_wskbd_console_device->sc_isconsole = 1; + wskbd_console_device = stolen_wskbd_console_device; + stolen_wskbd_console_device = NULL; + splx(s); + free(oldid, M_DEVBUF); + +#if NWSDISPLAY > 0 + wsdisplay_set_cons_kbd(wskbd_cngetc, wskbd_cnpollc, wskbd_cnbell); +#endif + + wskbd_console_initted = 1; +} +#endif + +void wskbd_cndetach() { KASSERT(wskbd_console_initted); @@ -568,7 +638,7 @@ wskbd_detach(struct device *self, int f } #endif - if (sc->sc_isconsole) { + if (sc->sc_isconsole == 1) { KASSERT(wskbd_console_device == sc); wskbd_console_device = NULL; } @@ -597,6 +667,10 @@ wskbd_detach(struct device *self, int f /* Nuke the vnodes for any open instances. */ mn = self->dv_unit; vdevgone(maj, mn, mn, VCHR); + + /* Free our internal data */ + if (!sc->sc_isconsole) + free(sc->id, M_DEVBUF); return (0); } Index: dev/wscons/wskbdvar.h =================================================================== RCS file: /cvs/src/sys/dev/wscons/wskbdvar.h,v retrieving revision 1.2 diff -u -p -r1.2 wskbdvar.h --- dev/wscons/wskbdvar.h 14 Mar 2002 01:27:03 -0000 1.2 +++ dev/wscons/wskbdvar.h 11 Mar 2007 02:50:47 -0000 @@ -100,3 +100,9 @@ void wskbd_rawinput(struct device *, u_c int wskbd_cngetc(dev_t dev); void wskbd_cnpollc(dev_t dev, int poll); void wskbd_cnbell(dev_t, u_int, u_int, u_int); + +/* + * Hooks to permit USB keyboards to become the console keyboard. + */ +void wskbd_cnsteal(void); +void wskbd_cnreturn(void);