From e04ffbb8b67e9f6a83ea4fee6093b81757856740 Mon Sep 17 00:00:00 2001 From: alfred Date: Thu, 30 Jul 2009 00:14:34 +0000 Subject: [PATCH] USB CORE: - Add minimum polling support to drive UMASS and UKBD in case of panic. - Add extra check to ukbd probe to fix problem about mouse devices attaching like keyboards. - P4 ID: 166148 Submitted by: hps Approved by: re --- sys/dev/usb/controller/at91dci.c | 1 + sys/dev/usb/controller/atmegadci.c | 1 + sys/dev/usb/controller/avr32dci.c | 1 + sys/dev/usb/controller/ehci.c | 1 + sys/dev/usb/controller/musb_otg.c | 1 + sys/dev/usb/controller/ohci.c | 1 + sys/dev/usb/controller/uhci.c | 1 + sys/dev/usb/controller/uss820dci.c | 1 + sys/dev/usb/input/ukbd.c | 157 ++++++++++++++++++++++------- sys/dev/usb/usb_controller.h | 3 + sys/dev/usb/usb_transfer.c | 96 ++++++++++++++++-- 11 files changed, 222 insertions(+), 42 deletions(-) diff --git a/sys/dev/usb/controller/at91dci.c b/sys/dev/usb/controller/at91dci.c index 210f0e7af36d..d9f8e4066c31 100644 --- a/sys/dev/usb/controller/at91dci.c +++ b/sys/dev/usb/controller/at91dci.c @@ -2326,4 +2326,5 @@ struct usb_bus_methods at91dci_bus_methods = .set_stall = &at91dci_set_stall, .clear_stall = &at91dci_clear_stall, .roothub_exec = &at91dci_roothub_exec, + .xfer_poll = &at91dci_do_poll, }; diff --git a/sys/dev/usb/controller/atmegadci.c b/sys/dev/usb/controller/atmegadci.c index 47e27b5f3240..9b60ad7e17be 100644 --- a/sys/dev/usb/controller/atmegadci.c +++ b/sys/dev/usb/controller/atmegadci.c @@ -2143,4 +2143,5 @@ struct usb_bus_methods atmegadci_bus_methods = .set_stall = &atmegadci_set_stall, .clear_stall = &atmegadci_clear_stall, .roothub_exec = &atmegadci_roothub_exec, + .xfer_poll = &atmegadci_do_poll, }; diff --git a/sys/dev/usb/controller/avr32dci.c b/sys/dev/usb/controller/avr32dci.c index 156f2866c876..f251f9109ae8 100644 --- a/sys/dev/usb/controller/avr32dci.c +++ b/sys/dev/usb/controller/avr32dci.c @@ -2081,4 +2081,5 @@ struct usb_bus_methods avr32dci_bus_methods = .set_stall = &avr32dci_set_stall, .clear_stall = &avr32dci_clear_stall, .roothub_exec = &avr32dci_roothub_exec, + .xfer_poll = &avr32dci_do_poll, }; diff --git a/sys/dev/usb/controller/ehci.c b/sys/dev/usb/controller/ehci.c index a326a73f7abd..30c9bb967abb 100644 --- a/sys/dev/usb/controller/ehci.c +++ b/sys/dev/usb/controller/ehci.c @@ -3828,4 +3828,5 @@ struct usb_bus_methods ehci_bus_methods = .device_suspend = ehci_device_suspend, .set_hw_power = ehci_set_hw_power, .roothub_exec = ehci_roothub_exec, + .xfer_poll = ehci_do_poll, }; diff --git a/sys/dev/usb/controller/musb_otg.c b/sys/dev/usb/controller/musb_otg.c index 81332d757b6f..60d29dd690b6 100644 --- a/sys/dev/usb/controller/musb_otg.c +++ b/sys/dev/usb/controller/musb_otg.c @@ -2736,4 +2736,5 @@ struct usb_bus_methods musbotg_bus_methods = .set_stall = &musbotg_set_stall, .clear_stall = &musbotg_clear_stall, .roothub_exec = &musbotg_roothub_exec, + .xfer_poll = &musbotg_do_poll, }; diff --git a/sys/dev/usb/controller/ohci.c b/sys/dev/usb/controller/ohci.c index 1e4c317a584d..30592c14751b 100644 --- a/sys/dev/usb/controller/ohci.c +++ b/sys/dev/usb/controller/ohci.c @@ -2756,4 +2756,5 @@ struct usb_bus_methods ohci_bus_methods = .device_suspend = ohci_device_suspend, .set_hw_power = ohci_set_hw_power, .roothub_exec = ohci_roothub_exec, + .xfer_poll = ohci_do_poll, }; diff --git a/sys/dev/usb/controller/uhci.c b/sys/dev/usb/controller/uhci.c index 77e95d3ad8be..2a2ff1cf2ceb 100644 --- a/sys/dev/usb/controller/uhci.c +++ b/sys/dev/usb/controller/uhci.c @@ -3251,4 +3251,5 @@ struct usb_bus_methods uhci_bus_methods = .device_suspend = uhci_device_suspend, .set_hw_power = uhci_set_hw_power, .roothub_exec = uhci_roothub_exec, + .xfer_poll = uhci_do_poll, }; diff --git a/sys/dev/usb/controller/uss820dci.c b/sys/dev/usb/controller/uss820dci.c index 1016ed6447e4..62fa8df4b619 100644 --- a/sys/dev/usb/controller/uss820dci.c +++ b/sys/dev/usb/controller/uss820dci.c @@ -2360,4 +2360,5 @@ struct usb_bus_methods uss820dci_bus_methods = .set_stall = &uss820dci_set_stall, .clear_stall = &uss820dci_clear_stall, .roothub_exec = &uss820dci_roothub_exec, + .xfer_poll = &uss820dci_do_poll, }; diff --git a/sys/dev/usb/input/ukbd.c b/sys/dev/usb/input/ukbd.c index 29546a5c2a84..0d09ad43783a 100644 --- a/sys/dev/usb/input/ukbd.c +++ b/sys/dev/usb/input/ukbd.c @@ -299,6 +299,28 @@ ukbd_put_key(struct ukbd_softc *sc, uint32_t key) } } +static void +ukbd_do_poll(struct ukbd_softc *sc, uint8_t wait) +{ + DPRINTFN(2, "polling\n"); + + while (sc->sc_inputs == 0) { + + usbd_transfer_poll(sc->sc_xfer, UKBD_N_TRANSFER); + + DELAY(1000); /* delay 1 ms */ + + sc->sc_time_ms++; + + /* support repetition of keys: */ + + ukbd_interrupt(sc); + + if (!wait) + break; + } +} + static int32_t ukbd_get_key(struct ukbd_softc *sc, uint8_t wait) { @@ -311,24 +333,7 @@ ukbd_get_key(struct ukbd_softc *sc, uint8_t wait) usbd_transfer_start(sc->sc_xfer[UKBD_INTR_DT]); } if (sc->sc_flags & UKBD_FLAG_POLLING) { - DPRINTFN(2, "polling\n"); - - while (sc->sc_inputs == 0) { - - usbd_transfer_poll(sc->sc_xfer, UKBD_N_TRANSFER); - - DELAY(1000); /* delay 1 ms */ - - sc->sc_time_ms++; - - /* support repetition of keys: */ - - ukbd_interrupt(sc); - - if (!wait) { - break; - } - } + ukbd_do_poll(sc, wait); } if (sc->sc_inputs == 0) { c = -1; @@ -706,7 +711,15 @@ ukbd_probe(device_t dev) if (error) return (ENXIO); + /* + * NOTE: we currently don't support USB mouse and USB keyboard + * on the same USB endpoint. + */ if (hid_is_collection(d_ptr, d_len, + HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE))) { + /* most likely a mouse */ + error = ENXIO; + } else if (hid_is_collection(d_ptr, d_len, HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) { if (usb_test_quirk(uaa, UQ_KBD_IGNORE)) error = ENXIO; @@ -983,6 +996,14 @@ ukbd_lock(keyboard_t *kbd, int lock) static int ukbd_enable(keyboard_t *kbd) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_enable(kbd); + mtx_unlock(&Giant); + return (retval); + } mtx_assert(&Giant, MA_OWNED); KBD_ACTIVATE(kbd); return (0); @@ -992,6 +1013,14 @@ ukbd_enable(keyboard_t *kbd) static int ukbd_disable(keyboard_t *kbd) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_disable(kbd); + mtx_unlock(&Giant); + return (retval); + } mtx_assert(&Giant, MA_OWNED); KBD_DEACTIVATE(kbd); return (0); @@ -1003,14 +1032,26 @@ ukbd_check(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; - if (!mtx_owned(&Giant)) { - return (0); /* XXX */ + if (!KBD_IS_ACTIVE(kbd)) + return (0); + + if (sc->sc_flags & UKBD_FLAG_POLLING) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_check(kbd); + mtx_unlock(&Giant); + return (retval); + } + ukbd_do_poll(sc, 0); + } else { + /* XXX the keyboard layer requires Giant */ + if (!mtx_owned(&Giant)) + return (0); } mtx_assert(&Giant, MA_OWNED); - if (!KBD_IS_ACTIVE(kbd)) { - return (0); - } #ifdef UKBD_EMULATE_ATSCANCODE if (sc->sc_buffered_char[0]) { return (1); @@ -1028,14 +1069,25 @@ ukbd_check_char(keyboard_t *kbd) { struct ukbd_softc *sc = kbd->kb_data; - if (!mtx_owned(&Giant)) { - return (0); /* XXX */ + if (!KBD_IS_ACTIVE(kbd)) + return (0); + + if (sc->sc_flags & UKBD_FLAG_POLLING) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_check_char(kbd); + mtx_unlock(&Giant); + return (retval); + } + } else { + /* XXX the keyboard layer requires Giant */ + if (!mtx_owned(&Giant)) + return (0); } mtx_assert(&Giant, MA_OWNED); - if (!KBD_IS_ACTIVE(kbd)) { - return (0); - } if ((sc->sc_composed_char > 0) && (!(sc->sc_flags & UKBD_FLAG_COMPOSE))) { return (1); @@ -1056,9 +1108,22 @@ ukbd_read(keyboard_t *kbd, int wait) uint32_t scancode; #endif + if (!KBD_IS_ACTIVE(kbd)) + return (-1); - if (!mtx_owned(&Giant)) { - return -1; /* XXX */ + if (sc->sc_flags & UKBD_FLAG_POLLING) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_read(kbd, wait); + mtx_unlock(&Giant); + return (retval); + } + } else { + /* XXX the keyboard layer requires Giant */ + if (!mtx_owned(&Giant)) + return (-1); } mtx_assert(&Giant, MA_OWNED); @@ -1077,9 +1142,9 @@ ukbd_read(keyboard_t *kbd, int wait) /* XXX */ usbcode = ukbd_get_key(sc, (wait == FALSE) ? 0 : 1); - if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1)) { - return -1; - } + if (!KBD_IS_ACTIVE(kbd) || (usbcode == -1)) + return (-1); + ++(kbd->kb_count); #ifdef UKBD_EMULATE_ATSCANCODE @@ -1107,8 +1172,23 @@ ukbd_read_char(keyboard_t *kbd, int wait) uint32_t scancode; #endif - if (!mtx_owned(&Giant)) { - return (NOKEY); /* XXX */ + + if (!KBD_IS_ACTIVE(kbd)) + return (NOKEY); + + if (sc->sc_flags & UKBD_FLAG_POLLING) { + if (!mtx_owned(&Giant)) { + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_read_char(kbd, wait); + mtx_unlock(&Giant); + return (retval); + } + } else { + /* XXX the keyboard layer requires Giant */ + if (!mtx_owned(&Giant)) + return (NOKEY); } mtx_assert(&Giant, MA_OWNED); @@ -1485,7 +1565,12 @@ ukbd_poll(keyboard_t *kbd, int on) struct ukbd_softc *sc = kbd->kb_data; if (!mtx_owned(&Giant)) { - return (0); /* XXX */ + /* XXX cludge */ + int retval; + mtx_lock(&Giant); + retval = ukbd_poll(kbd, on); + mtx_unlock(&Giant); + return (retval); } mtx_assert(&Giant, MA_OWNED); diff --git a/sys/dev/usb/usb_controller.h b/sys/dev/usb/usb_controller.h index fbdfe314a955..6e355420e6e6 100644 --- a/sys/dev/usb/usb_controller.h +++ b/sys/dev/usb/usb_controller.h @@ -99,6 +99,9 @@ struct usb_bus_methods { void (*set_stall) (struct usb_device *udev, struct usb_xfer *xfer, struct usb_endpoint *ep, uint8_t *did_stall); void (*clear_stall) (struct usb_device *udev, struct usb_endpoint *ep); + /* Optional transfer polling support */ + + void (*xfer_poll) (struct usb_bus *); }; /* diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c index 04b462e95fea..3c466fb72d33 100644 --- a/sys/dev/usb/usb_transfer.c +++ b/sys/dev/usb/usb_transfer.c @@ -2853,15 +2853,99 @@ usbd_clear_stall_callback(struct usb_xfer *xfer1, return (1); /* Clear Stall Finished */ } +/*------------------------------------------------------------------------* + * usbd_transfer_poll + * + * The following function gets called from the USB keyboard driver and + * UMASS when the system has paniced. + * + * NOTE: It is currently not possible to resume normal operation on + * the USB controller which has been polled, due to clearing of the + * "up_dsleep" and "up_msleep" flags. + *------------------------------------------------------------------------*/ void usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max) { - static uint8_t once = 0; - /* polling is currently not supported */ - if (!once) { - once = 1; - printf("usbd_transfer_poll: USB polling is " - "not supported!\n"); + struct usb_xfer *xfer; + struct usb_xfer_root *xroot; + struct usb_device *udev; + struct usb_proc_msg *pm; + uint16_t n; + uint16_t drop_bus; + uint16_t drop_xfer; + + for (n = 0; n != max; n++) { + /* Extra checks to avoid panic */ + xfer = ppxfer[n]; + if (xfer == NULL) + continue; /* no USB transfer */ + xroot = xfer->xroot; + if (xroot == NULL) + continue; /* no USB root */ + udev = xroot->udev; + if (udev == NULL) + continue; /* no USB device */ + if (udev->bus == NULL) + continue; /* no BUS structure */ + if (udev->bus->methods == NULL) + continue; /* no BUS methods */ + if (udev->bus->methods->xfer_poll == NULL) + continue; /* no poll method */ + + /* make sure that the BUS mutex is not locked */ + drop_bus = 0; + while (mtx_owned(&xroot->udev->bus->bus_mtx)) { + mtx_unlock(&xroot->udev->bus->bus_mtx); + drop_bus++; + } + + /* make sure that the transfer mutex is not locked */ + drop_xfer = 0; + while (mtx_owned(xroot->xfer_mtx)) { + mtx_unlock(xroot->xfer_mtx); + drop_xfer++; + } + + /* Make sure cv_signal() and cv_broadcast() is not called */ + udev->bus->control_xfer_proc.up_dsleep = 0; + udev->bus->control_xfer_proc.up_msleep = 0; + udev->bus->explore_proc.up_dsleep = 0; + udev->bus->explore_proc.up_msleep = 0; + udev->bus->giant_callback_proc.up_dsleep = 0; + udev->bus->giant_callback_proc.up_msleep = 0; + udev->bus->non_giant_callback_proc.up_dsleep = 0; + udev->bus->non_giant_callback_proc.up_msleep = 0; + + /* poll USB hardware */ + (udev->bus->methods->xfer_poll) (udev->bus); + + USB_BUS_LOCK(xroot->bus); + + /* check for clear stall */ + if (udev->default_xfer[1] != NULL) { + + /* poll clear stall start */ + pm = &udev->cs_msg[0].hdr; + (pm->pm_callback) (pm); + /* poll clear stall done thread */ + pm = &udev->default_xfer[1]-> + xroot->done_m[0].hdr; + (pm->pm_callback) (pm); + } + + /* poll done thread */ + pm = &xroot->done_m[0].hdr; + (pm->pm_callback) (pm); + + USB_BUS_UNLOCK(xroot->bus); + + /* restore transfer mutex */ + while (drop_xfer--) + mtx_lock(xroot->xfer_mtx); + + /* restore BUS mutex */ + while (drop_bus--) + mtx_lock(&xroot->udev->bus->bus_mtx); } }