diff --git a/sys/dev/hid/hid_if.m b/sys/dev/hid/hid_if.m --- a/sys/dev/hid/hid_if.m +++ b/sys/dev/hid/hid_if.m @@ -44,8 +44,8 @@ # rdesc is pointer to structire containing requested maximal sizes of input, # output and feature reports. It is used by hardware transport drivers # to determine sizes of internal buffers. -# This function returns zero upon success. A non-zero return value indicates -# failure. +# This function can be subsequently called with intr parameter set to NULL +# to request intr_poll method support for transport driver. # METHOD void intr_setup { device_t dev; @@ -76,8 +76,9 @@ }; # -# The following function gets called from the HID keyboard driver -# when the system has paniced. +# The following function gets called from the HID keyboard driver when +# the system has paniced. intr_setup method with NULL passed as intr parameter +# must be called once before to let transport driver to be prepared. # METHOD void intr_poll { device_t dev; diff --git a/sys/dev/hid/hidbus.c b/sys/dev/hid/hidbus.c --- a/sys/dev/hid/hidbus.c +++ b/sys/dev/hid/hidbus.c @@ -457,6 +457,9 @@ break; case HIDBUS_IVAR_FLAGS: tlc->flags = value; + if ((value & HIDBUS_FLAG_CAN_POLL) != 0) + HID_INTR_SETUP( + device_get_parent(bus), NULL, NULL, NULL); break; case HIDBUS_IVAR_DRIVER_INFO: tlc->driver_info = value; diff --git a/sys/dev/iicbus/iichid.c b/sys/dev/iicbus/iichid.c --- a/sys/dev/iicbus/iichid.c +++ b/sys/dev/iicbus/iichid.c @@ -782,6 +782,9 @@ { struct iichid_softc *sc; + if (intr == NULL) + return; + sc = device_get_softc(dev); /* * Do not rely on wMaxInputLength, as some devices may set it to diff --git a/sys/dev/usb/input/usbhid.c b/sys/dev/usb/input/usbhid.c --- a/sys/dev/usb/input/usbhid.c +++ b/sys/dev/usb/input/usbhid.c @@ -67,6 +67,7 @@ #include #include #include +#include #define USB_DEBUG_VAR usbhid_debug #include @@ -85,6 +86,8 @@ &usbhid_debug, 0, "Debug level"); #endif +/* Second set of USB transfers for polling mode */ +#define POLL_XFER(xfer) ((xfer) + USBHID_N_TRANSFER) enum { USBHID_INTR_OUT_DT, USBHID_INTR_IN_DT, @@ -123,8 +126,9 @@ struct mtx sc_mtx; struct usb_config sc_config[USBHID_N_TRANSFER]; - struct usb_xfer *sc_xfer[USBHID_N_TRANSFER]; - struct usbhid_xfer_ctx sc_xfer_ctx[USBHID_N_TRANSFER]; + struct usb_xfer *sc_xfer[POLL_XFER(USBHID_N_TRANSFER)]; + struct usbhid_xfer_ctx sc_xfer_ctx[POLL_XFER(USBHID_N_TRANSFER)]; + bool sc_can_poll; struct usb_device *sc_udev; uint8_t sc_iface_no; @@ -317,6 +321,8 @@ static inline int usbhid_xfer_check_len(struct usbhid_softc* sc, int xfer_idx, hid_size_t len) { + if (USB_IN_POLLING_MODE_FUNC()) + xfer_idx = POLL_XFER(xfer_idx); if (sc->sc_xfer[xfer_idx] == NULL) return (ENODEV); if (len > usbd_xfer_max_len(sc->sc_xfer[xfer_idx])) @@ -333,6 +339,39 @@ bool nowrite; int error; + nowrite = hid_test_quirk(&sc->sc_hw, HQ_NOWRITE); + + /* + * Setup the USB transfers one by one, so they are memory independent + * which allows for handling panics triggered by the HID drivers + * itself, typically by hkbd via CTRL+ALT+ESC sequences. Or if the HID + * keyboard driver was processing a key at the moment of panic. + */ + if (intr == NULL) { + if (sc->sc_can_poll) + return; + for (n = 0; n != USBHID_N_TRANSFER; n++) { + if (nowrite && n == USBHID_INTR_OUT_DT) + continue; + error = usbd_transfer_setup(sc->sc_udev, + &sc->sc_iface_index, sc->sc_xfer + POLL_XFER(n), + sc->sc_config + n, 1, + (void *)(sc->sc_xfer_ctx + POLL_XFER(n)), + &sc->sc_mtx); + if (error) + DPRINTF("xfer %d setup error=%s\n", n, + usbd_errstr(error)); + } + mtx_lock(&sc->sc_mtx); + if (sc->sc_xfer[USBHID_INTR_IN_DT] != NULL && + sc->sc_xfer[USBHID_INTR_IN_DT]->flags_int.started) + usbd_transfer_start( + sc->sc_xfer[POLL_XFER(USBHID_INTR_IN_DT)]); + mtx_unlock(&sc->sc_mtx); + sc->sc_can_poll = true; + return; + } + sc->sc_intr_handler = intr; sc->sc_intr_ctx = context; bcopy(usbhid_config, sc->sc_config, sizeof(usbhid_config)); @@ -344,14 +383,6 @@ sc->sc_config[USBHID_CTRL_DT].bufsize = MAX(rdesc->isize, MAX(rdesc->osize, rdesc->fsize)); - nowrite = hid_test_quirk(&sc->sc_hw, HQ_NOWRITE); - - /* - * Setup the USB transfers one by one, so they are memory independent - * which allows for handling panics triggered by the HID drivers - * itself, typically by hkbd via CTRL+ALT+ESC sequences. Or if the HID - * keyboard driver was processing a key at the moment of panic. - */ for (n = 0; n != USBHID_N_TRANSFER; n++) { if (nowrite && n == USBHID_INTR_OUT_DT) continue; @@ -378,6 +409,10 @@ struct usbhid_softc* sc = device_get_softc(dev); usbd_transfer_unsetup(sc->sc_xfer, USBHID_N_TRANSFER); + if (sc->sc_can_poll) + usbd_transfer_unsetup( + sc->sc_xfer, POLL_XFER(USBHID_N_TRANSFER)); + sc->sc_can_poll = false; free(sc->sc_intr_buf, M_USBDEV); } @@ -397,7 +432,16 @@ .cb_ctx = sc, .buf = sc->sc_intr_buf, }; + sc->sc_xfer_ctx[POLL_XFER(USBHID_INTR_IN_DT)] = (struct usbhid_xfer_ctx) { + .req.intr.maxlen = + usbd_xfer_max_len(sc->sc_xfer[USBHID_INTR_IN_DT]), + .cb = usbhid_intr_handler_cb, + .cb_ctx = sc, + .buf = sc->sc_intr_buf, + }; usbd_transfer_start(sc->sc_xfer[USBHID_INTR_IN_DT]); + if (sc->sc_can_poll) + usbd_transfer_start(sc->sc_xfer[POLL_XFER(USBHID_INTR_IN_DT)]); mtx_unlock(&sc->sc_mtx); return (0); @@ -410,6 +454,8 @@ usbd_transfer_drain(sc->sc_xfer[USBHID_INTR_IN_DT]); usbd_transfer_drain(sc->sc_xfer[USBHID_INTR_OUT_DT]); + if (sc->sc_can_poll) + usbd_transfer_drain(sc->sc_xfer[POLL_XFER(USBHID_INTR_IN_DT)]); return (0); } @@ -419,7 +465,9 @@ { struct usbhid_softc* sc = device_get_softc(dev); + MPASS(sc->sc_can_poll); usbd_transfer_poll(sc->sc_xfer + USBHID_INTR_IN_DT, 1); + usbd_transfer_poll(sc->sc_xfer + POLL_XFER(USBHID_INTR_IN_DT), 1); } /* @@ -430,12 +478,13 @@ union usbhid_device_request *req, void *buf) { int error, timeout; - struct usbhid_xfer_ctx *xfer_ctx, save; + struct usbhid_xfer_ctx *xfer_ctx; xfer_ctx = sc->sc_xfer_ctx + xfer_idx; if (USB_IN_POLLING_MODE_FUNC()) { - save = *xfer_ctx; + xfer_ctx = POLL_XFER(xfer_ctx); + xfer_idx = POLL_XFER(xfer_idx); } else { mtx_lock(&sc->sc_mtx); ++xfer_ctx->waiters; @@ -473,9 +522,7 @@ if (error == 0) *req = xfer_ctx->req; - if (USB_IN_POLLING_MODE_FUNC()) { - *xfer_ctx = save; - } else { + if (!USB_IN_POLLING_MODE_FUNC()) { xfer_ctx->influx = false; if (xfer_ctx->waiters != 0) wakeup_one(&xfer_ctx->waiters);