Add quirk for XHCI(4) controllers to support USB control transfers

above 1Kbyte.  It might look like some XHCI(4) controllers do not
support when the USB control transfer is split using a link TRB. The
next NORMAL TRB after the link TRB is simply failing with XHCI error
code 4. The quirk ensures we allocate a 64Kbyte buffer so that the
data stage TRB is not broken with a link TRB.

Found at:	EuroBSDcon 2019
MFC after:	1 week
Sponsored by:	Mellanox Technologies
This commit is contained in:
Hans Petter Selasky 2019-09-20 11:28:45 +00:00
parent 4631d7f717
commit 7fca0e69f6
3 changed files with 35 additions and 2 deletions

View File

@ -601,6 +601,9 @@ xhci_init(struct xhci_softc *sc, device_t self, uint8_t dma32)
device_printf(self, "%d bytes context size, %d-bit DMA\n",
sc->sc_ctx_is_64_byte ? 64 : 32, (int)sc->sc_bus.dma_bits);
/* enable 64Kbyte control endpoint quirk */
sc->sc_bus.control_ep_quirk = 1;
temp = XREAD4(sc, capa, XHCI_HCSPARAMS1);
/* get number of device slots */

View File

@ -131,6 +131,7 @@ struct usb_bus {
uint8_t do_probe; /* set if USB should be re-probed */
uint8_t no_explore; /* don't explore USB ports */
uint8_t dma_bits; /* number of DMA address lines */
uint8_t control_ep_quirk; /* need 64kByte buffer for data stage */
};
#endif /* _USB_BUS_H_ */

View File

@ -106,6 +106,33 @@ static const struct usb_config usb_control_ep_cfg[USB_CTRL_XFER_MAX] = {
},
};
static const struct usb_config usb_control_ep_quirk_cfg[USB_CTRL_XFER_MAX] = {
/* This transfer is used for generic control endpoint transfers */
[0] = {
.type = UE_CONTROL,
.endpoint = 0x00, /* Control endpoint */
.direction = UE_DIR_ANY,
.bufsize = 65535, /* bytes */
.callback = &usb_request_callback,
.usb_mode = USB_MODE_DUAL, /* both modes */
},
/* This transfer is used for generic clear stall only */
[1] = {
.type = UE_CONTROL,
.endpoint = 0x00, /* Control pipe */
.direction = UE_DIR_ANY,
.bufsize = sizeof(struct usb_device_request),
.callback = &usb_do_clear_stall_callback,
.timeout = 1000, /* 1 second */
.interval = 50, /* 50ms */
.usb_mode = USB_MODE_HOST,
},
};
/* function prototypes */
static void usbd_update_max_frame_size(struct usb_xfer *);
@ -1021,7 +1048,8 @@ usbd_transfer_setup(struct usb_device *udev,
* context, else there is a chance of
* deadlock!
*/
if (setup_start == usb_control_ep_cfg)
if (setup_start == usb_control_ep_cfg ||
setup_start == usb_control_ep_quirk_cfg)
info->done_p =
USB_BUS_CONTROL_XFER_PROC(udev->bus);
else if (xfer_mtx == &Giant)
@ -3149,7 +3177,8 @@ usbd_ctrl_transfer_setup(struct usb_device *udev)
*/
iface_index = 0;
if (usbd_transfer_setup(udev, &iface_index,
udev->ctrl_xfer, usb_control_ep_cfg, USB_CTRL_XFER_MAX, NULL,
udev->ctrl_xfer, udev->bus->control_ep_quirk ?
usb_control_ep_quirk_cfg : usb_control_ep_cfg, USB_CTRL_XFER_MAX, NULL,
&udev->device_mtx)) {
DPRINTFN(0, "could not setup default "
"USB transfer\n");