Author: hselasky
Date: Mon Feb  2 11:06:41 2015
New Revision: 278071
URL: https://svnweb.freebsd.org/changeset/base/278071

Log:
  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.
  
  Reported by:  Kohji Okuno <okuno.ko...@jp.panasonic.com>
  MFC after:    1 week

Modified:
  head/sys/dev/usb/controller/xhci.c
  head/sys/dev/usb/usb_core.h
  head/sys/dev/usb/usb_transfer.c

Modified: head/sys/dev/usb/controller/xhci.c
==============================================================================
--- head/sys/dev/usb/controller/xhci.c  Mon Feb  2 10:31:35 2015        
(r278070)
+++ head/sys/dev/usb/controller/xhci.c  Mon Feb  2 11:06:41 2015        
(r278071)
@@ -1866,6 +1866,15 @@ restart:
                                    
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 |
@@ -2106,7 +2115,8 @@ xhci_setup_generic_chain(struct usb_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;

Modified: head/sys/dev/usb/usb_core.h
==============================================================================
--- head/sys/dev/usb/usb_core.h Mon Feb  2 10:31:35 2015        (r278070)
+++ head/sys/dev/usb/usb_core.h Mon Feb  2 11:06:41 2015        (r278071)
@@ -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 */

Modified: head/sys/dev/usb/usb_transfer.c
==============================================================================
--- head/sys/dev/usb/usb_transfer.c     Mon Feb  2 10:31:35 2015        
(r278070)
+++ head/sys/dev/usb/usb_transfer.c     Mon Feb  2 11:06:41 2015        
(r278071)
@@ -1409,6 +1409,29 @@ usbd_control_transfer_init(struct usb_xf
 }
 
 /*------------------------------------------------------------------------*
+ *     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
  *
  * This function handles initialisation of control transfers. Control
@@ -1513,6 +1536,11 @@ usbd_setup_ctrl_transfer(struct usb_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) {
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to