| From 953c66469735aed8d2ada639a72b150f01dae605 Mon Sep 17 00:00:00 2001 |
| From: Abbas Raza <Abbas_Raza@mentor.com> |
| Date: Thu, 17 Jul 2014 19:34:31 +0800 |
| Subject: usb: chipidea: udc: Disable auto ZLP generation on ep0 |
| |
| From: Abbas Raza <Abbas_Raza@mentor.com> |
| |
| commit 953c66469735aed8d2ada639a72b150f01dae605 upstream. |
| |
| There are 2 methods for ZLP (zero-length packet) generation: |
| 1) In software |
| 2) Automatic generation by device controller |
| |
| 1) is implemented in UDC driver and it attaches ZLP to IN packet if |
| descriptor->size < wLength |
| 2) can be enabled/disabled by setting ZLT bit in the QH |
| |
| When gadget ffs is connected to ubuntu host, the host sends |
| get descriptor request and wLength in setup packet is 255 while the |
| size of descriptor which will be sent by gadget in IN packet is |
| 64 byte. So the composite driver sets req->zero = 1. |
| In UDC driver following code will be executed then |
| |
| if (hwreq->req.zero && hwreq->req.length |
| && (hwreq->req.length % hwep->ep.maxpacket == 0)) |
| add_td_to_list(hwep, hwreq, 0); |
| |
| Case-A: |
| So in case of ubuntu host, UDC driver will attach a ZLP to the IN packet. |
| ubuntu host will request 255 byte in IN request, gadget will send 64 byte |
| with ZLP and host will come to know that there is no more data. |
| But hold on, by default ZLT=0 for endpoint 0 so hardware also tries to |
| automatically generate the ZLP which blocks enumeration for ~6 seconds due |
| to endpoint 0 STALL, NAKs are sent to host for any requests (OUT/PING) |
| |
| Case-B: |
| In case when gadget ffs is connected to Apple device, Apple device sends |
| setup packet with wLength=64. So descriptor->size = 64 and wLength=64 |
| therefore req->zero = 0 and UDC driver will not attach any ZLP to the |
| IN packet. Apple device requests 64 bytes, gets 64 bytes and doesn't |
| further request for IN data. But ZLT=0 by default for endpoint 0 so |
| hardware tries to automatically generate the ZLP which blocks enumeration |
| for ~6 seconds due to endpoint 0 STALL, NAKs are sent to host for any |
| requests (OUT/PING) |
| |
| According to USB2.0 specs: |
| |
| 8.5.3.2 Variable-length Data Stage |
| A control pipe may have a variable-length data phase in which the |
| host requests more data than is contained in the specified data |
| structure. When all of the data structure is returned to the host, |
| the function should indicate that the Data stage is ended by |
| returning a packet that is shorter than the MaxPacketSize for the |
| pipe. If the data structure is an exact multiple of wMaxPacketSize |
| for the pipe, the function will return a zero-length packet to indicate |
| the end of the Data stage. |
| |
| In Case-A mentioned above: |
| If we disable software ZLP generation & ZLT=0 for endpoint 0 OR if software |
| ZLP generation is not disabled but we set ZLT=1 for endpoint 0 then |
| enumeration doesn't block for 6 seconds. |
| |
| In Case-B mentioned above: |
| If we disable software ZLP generation & ZLT=0 for endpoint then enumeration |
| still blocks due to ZLP automatically generated by hardware and host not needing |
| it. But if we keep software ZLP generation enabled but we set ZLT=1 for |
| endpoint 0 then enumeration doesn't block for 6 seconds. |
| |
| So the proper solution for this issue seems to disable automatic ZLP generation |
| by hardware (i.e by setting ZLT=1 for endpoint 0) and let software (UDC driver) |
| handle the ZLP generation based on req->zero field. |
| |
| Signed-off-by: Abbas Raza <Abbas_Raza@mentor.com> |
| Signed-off-by: Peter Chen <peter.chen@freescale.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/chipidea/udc.c | 4 ++-- |
| 1 file changed, 2 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/usb/chipidea/udc.c |
| +++ b/drivers/usb/chipidea/udc.c |
| @@ -1179,8 +1179,8 @@ static int ep_enable(struct usb_ep *ep, |
| |
| if (hwep->type == USB_ENDPOINT_XFER_CONTROL) |
| cap |= QH_IOS; |
| - if (hwep->num) |
| - cap |= QH_ZLT; |
| + |
| + cap |= QH_ZLT; |
| cap |= (hwep->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT; |
| /* |
| * For ISO-TX, we set mult at QH as the largest value, and use |