From eab81d3c8ac88c983c3ee58f18adbe1675f0d242 Mon Sep 17 00:00:00 2001 From: hselasky Date: Tue, 10 Feb 2015 13:16:53 +0000 Subject: [PATCH] MFC r278071: Section 3.2.9 in the XHCI specification about control transfers says that we should use a normal-TRB if there are more TRBs extending the data-stage TRB. Add a dedicated state bit to the internal USB transfer flags to handle this case. git-svn-id: svn://svn.freebsd.org/base/stable/10@278507 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- sys/dev/usb/controller/xhci.c | 12 +++++++++++- sys/dev/usb/usb_core.h | 1 + sys/dev/usb/usb_transfer.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/sys/dev/usb/controller/xhci.c b/sys/dev/usb/controller/xhci.c index 66e69572b..da7f7f66c 100644 --- a/sys/dev/usb/controller/xhci.c +++ b/sys/dev/usb/controller/xhci.c @@ -1870,6 +1870,15 @@ xhci_setup_generic_chain_sub(struct xhci_std_temp *temp) XHCI_TRB_3_TYPE_SET(XHCI_TRB_TYPE_DATA_STAGE); if (temp->direction == UE_DIR_IN) dword |= XHCI_TRB_3_DIR_IN | XHCI_TRB_3_ISP_BIT; + /* + * Section 3.2.9 in the XHCI + * specification about control + * transfers says that we should use a + * normal-TRB if there are more TRBs + * extending the data-stage + * TRB. Update the "trb_type". + */ + temp->trb_type = XHCI_TRB_TYPE_NORMAL; break; case XHCI_TRB_TYPE_STATUS_STAGE: dword = XHCI_TRB_3_CHAIN_BIT | XHCI_TRB_3_CYCLE_BIT | @@ -2110,7 +2119,8 @@ xhci_setup_generic_chain(struct usb_xfer *xfer) mult = 1; temp.isoc_delta = 0; temp.isoc_frame = 0; - temp.trb_type = XHCI_TRB_TYPE_DATA_STAGE; + temp.trb_type = xfer->flags_int.control_did_data ? + XHCI_TRB_TYPE_NORMAL : XHCI_TRB_TYPE_DATA_STAGE; } else { x = 0; mult = 1; diff --git a/sys/dev/usb/usb_core.h b/sys/dev/usb/usb_core.h index dc514646f..739a00391 100644 --- a/sys/dev/usb/usb_core.h +++ b/sys/dev/usb/usb_core.h @@ -101,6 +101,7 @@ struct usb_xfer_flags_int { * sent */ uint8_t control_act:1; /* set if control transfer is active */ uint8_t control_stall:1; /* set if control transfer should be stalled */ + uint8_t control_did_data:1; /* set if control DATA has been transferred */ uint8_t short_frames_ok:1; /* filtered version */ uint8_t short_xfer_ok:1; /* filtered version */ diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c index ba73687b2..464e12816 100644 --- a/sys/dev/usb/usb_transfer.c +++ b/sys/dev/usb/usb_transfer.c @@ -1408,6 +1408,29 @@ usbd_control_transfer_init(struct usb_xfer *xfer) (req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT; } +/*------------------------------------------------------------------------* + * usbd_control_transfer_did_data + * + * This function returns non-zero if a control endpoint has + * transferred the first DATA packet after the SETUP packet. + * Else it returns zero. + *------------------------------------------------------------------------*/ +static uint8_t +usbd_control_transfer_did_data(struct usb_xfer *xfer) +{ + struct usb_device_request req; + + /* SETUP packet is not yet sent */ + if (xfer->flags_int.control_hdr != 0) + return (0); + + /* copy out the USB request header */ + usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); + + /* compare remainder to the initial value */ + return (xfer->flags_int.control_rem != UGETW(req.wLength)); +} + /*------------------------------------------------------------------------* * usbd_setup_ctrl_transfer * @@ -1513,6 +1536,11 @@ usbd_setup_ctrl_transfer(struct usb_xfer *xfer) len = (xfer->sumlen - sizeof(struct usb_device_request)); } + /* update did data flag */ + + xfer->flags_int.control_did_data = + usbd_control_transfer_did_data(xfer); + /* check if there is a length mismatch */ if (len > xfer->flags_int.control_rem) { -- 2.45.0