Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 29 Jan 2015 21:00:04 +0100
From:      Hans Petter Selasky <hps@selasky.org>
To:        Chagin Dmitry <dchagin@freebsd.org>
Cc:        freebsd-usb@freebsd.org
Subject:   Re: [Bug?] Control Transfers in xHCI
Message-ID:  <54CA9144.4030601@selasky.org>
In-Reply-To: <20150129195714.GA3683@dchagin.static.corbina.net>
References:  <20150129.212550.434561541001871867.okuno.kohji@jp.panasonic.com> <54CA4BE3.2090706@selasky.org> <20150129195714.GA3683@dchagin.static.corbina.net>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------020607040003040609030404
Content-Type: text/plain; charset=windows-1252; format=flowed
Content-Transfer-Encoding: 7bit

On 01/29/15 20:57, Chagin Dmitry wrote:
> On Thu, Jan 29, 2015 at 04:04:03PM +0100, Hans Petter Selasky wrote:
>> On 01/29/15 13:25, Kohji Okuno wrote:
>>> Hi HPS,
>>>
>>> I found a bug in xHCI device driver.
>>>
>>> Acording to extensible-host-controler-interface-usb-xhci.pdf:"3.2.9
>>> Control Transfers"...
>>>
>>> A Data Stage TD consists of a Data Stage TRB followed by zero or more
>>> Normal TRBs. If the data is not physically contiguous, Normal TRBs may
>>> be chained to the Data Stage TRB.
>>>
>>>
>>> But, in the current imprementation, when two or more TRBs are needed,
>>> the device driver set XHCI_TRB_TYPE_DATA_STAGE to all TRBs.
>>> This is the violation of the spec.
>>>
>>> In my minor xHCI, I encountered strange bubble error in a control
>>> transfer. After I changed as the following, I succeeded its control
>>> transfer.
>>>
>>> Would you check the following (****)?
>>>
>>
>> Hi Kohji,
>>
>> You are correct there is a bug, but your patch is not correct.
>>
>> In FreeBSD we allow SETUP and DATA stages to be done as separate jobs.
>> That means at the entry of creating a new DATA chain, we need to check
>> if it is there first DATA packet or not.
>>
>> Can you test the attached patch and see if it works for you?
>>
> patch is lost somewhere, Hans.
>

Trying again.

I think Kohji got it.

--HPS

--------------020607040003040609030404
Content-Type: text/x-patch;
 name="xhci.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="xhci.patch"

Index: sys/dev/usb/controller/xhci.c
===================================================================
--- sys/dev/usb/controller/xhci.c	(revision 277724)
+++ sys/dev/usb/controller/xhci.c	(working copy)
@@ -1866,6 +1866,15 @@
 				    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 @@
 		mult = 1;
 		temp.isoc_delta = 0;
 		temp.isoc_frame = 0;
-		temp.trb_type = XHCI_TRB_TYPE_DATA_STAGE;
+		temp.trb_type = usbd_control_transfer_did_data(xfer) ?
+		    XHCI_TRB_TYPE_NORMAL : XHCI_TRB_TYPE_DATA_STAGE;
 	} else {
 		x = 0;
 		mult = 1;
Index: sys/dev/usb/usb_transfer.c
===================================================================
--- sys/dev/usb/usb_transfer.c	(revision 277724)
+++ sys/dev/usb/usb_transfer.c	(working copy)
@@ -1409,6 +1409,31 @@
 }
 
 /*------------------------------------------------------------------------*
+ *	usbd_control_transfer_did_data
+ *
+ * This function returns non-zero in USB host mode if a control
+ * endpoint has done the first DATA packet after the SETUP packet.
+ * Else it return zero.
+ *------------------------------------------------------------------------*/
+uint8_t
+usbd_control_transfer_did_data(struct usb_xfer *xfer)
+{
+	struct usb_device_request req;
+
+	if (xfer->flags_int.usb_mode == USB_MODE_DEVICE ||
+	    xfer->flags_int.control_xfr == 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
Index: sys/dev/usb/usbdi.h
===================================================================
--- sys/dev/usb/usbdi.h	(revision 277724)
+++ sys/dev/usb/usbdi.h	(working copy)
@@ -529,6 +529,7 @@
 void	usbd_transfer_stop(struct usb_xfer *xfer);
 void	usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup);
 void	usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max);
+uint8_t	usbd_control_transfer_did_data(struct usb_xfer *xfer);
 void	usbd_set_parent_iface(struct usb_device *udev, uint8_t iface_index,
 	    uint8_t parent_index);
 uint8_t	usbd_get_bus_index(struct usb_device *udev);

--------------020607040003040609030404--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?54CA9144.4030601>