usbhid(4): Add second set of USB transfers to work in polled mode.
The second set of USB transfer is requested by hkbd(4) and should improve HID keyboard handling in kdb and panic contexts. MFC after: 1 week Reviewed by: hselasky Differential revision: https://reviews.freebsd.org/D30486
This commit is contained in:
parent
e889a462d8
commit
9aa0e5af75
@ -44,8 +44,8 @@ INTERFACE hid;
|
||||
# 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 @@ METHOD int intr_stop {
|
||||
};
|
||||
|
||||
#
|
||||
# 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;
|
||||
|
@ -457,6 +457,9 @@ hidbus_write_ivar(device_t bus, device_t child, int which, uintptr_t value)
|
||||
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;
|
||||
|
@ -782,6 +782,9 @@ iichid_intr_setup(device_t dev, hid_intr_t intr, void *context,
|
||||
{
|
||||
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
|
||||
|
@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/usb/usbdi.h>
|
||||
#include <dev/usb/usbdi_util.h>
|
||||
#include <dev/usb/usbhid.h>
|
||||
#include <dev/usb/usb_core.h>
|
||||
|
||||
#define USB_DEBUG_VAR usbhid_debug
|
||||
#include <dev/usb/usb_debug.h>
|
||||
@ -85,6 +86,8 @@ SYSCTL_INT(_hw_usb_usbhid, OID_AUTO, debug, CTLFLAG_RWTUN,
|
||||
&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 usbhid_softc {
|
||||
|
||||
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 @@ usbhid_xfer_max_len(struct usb_xfer *xfer)
|
||||
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 @@ usbhid_intr_setup(device_t dev, hid_intr_t intr, void *context,
|
||||
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 @@ usbhid_intr_setup(device_t dev, hid_intr_t intr, void *context,
|
||||
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 @@ usbhid_intr_unsetup(device_t dev)
|
||||
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 @@ usbhid_intr_start(device_t dev)
|
||||
.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 @@ usbhid_intr_stop(device_t dev)
|
||||
|
||||
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 @@ usbhid_intr_poll(device_t dev)
|
||||
{
|
||||
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 @@ usbhid_sync_xfer(struct usbhid_softc* sc, int xfer_idx,
|
||||
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 @@ usbhid_sync_xfer(struct usbhid_softc* sc, int xfer_idx,
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user