diff mbox

fastboot: Fix OUT transaction length alignment

Message ID 1460548862-27625-1-git-send-email-semen.protsenko@linaro.org
State New
Headers show

Commit Message

Sam Protsenko April 13, 2016, 12:01 p.m. UTC
From: Sam Protsenko <semen.protsenko@linaro.org>

Some UDC controllers may require buffer size to be aligned to
wMaxPacketSize. It's indicated by gadget->quirk_ep_out_aligned_size
field being set to "true" (in UDC driver code). In that case
rx_bytes_expected must be aligned to wMaxPacket size, otherwise stuck on
transaction will happen. For example, it's required by DWC3 controller
data manual:

    section 8.2.3.3 Buffer Size Rules and Zero-Length Packets:

    For OUT endpoints, the following rules apply:
    - The BUFSIZ field must be ≥ 1 byte.
    - The total size of a Buffer Descriptor must be a multiple of
      MaxPacketSize
    - A received zero-length packet still requires a MaxPacketSize buffer.
      Therefore, if the expected amount of data to be received is a multiple
      of MaxPacketSize, software should add MaxPacketSize bytes to the buffer
      to sink a possible zero-length packet at the end of the transfer.

But other UDC controllers don't need such alignment, so mentioned field
is set to "false". If buffer size is aligned to wMaxPacketSize, those
controllers may stuck on transaction. The example is DWC2.

This patch checks gadget->quirk_ep_out_aligned_size field and aligns
rx_bytes_expected to wMaxPacketSize only when it's needed.

Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org>
---
 drivers/usb/gadget/f_fastboot.c | 9 +++++++++
 1 file changed, 9 insertions(+)

Comments

Sam Protsenko April 13, 2016, 4:56 p.m. UTC | #1
On Wed, Apr 13, 2016 at 3:32 PM, Roger Quadros <rogerq@ti.com> wrote:
> Hi,
>
> On 13/04/16 15:01, Semen Protsenko wrote:
>> From: Sam Protsenko <semen.protsenko@linaro.org>
>>
>> Some UDC controllers may require buffer size to be aligned to
>> wMaxPacketSize. It's indicated by gadget->quirk_ep_out_aligned_size
>> field being set to "true" (in UDC driver code). In that case
>> rx_bytes_expected must be aligned to wMaxPacket size, otherwise stuck on
>> transaction will happen. For example, it's required by DWC3 controller
>> data manual:
>>
>>     section 8.2.3.3 Buffer Size Rules and Zero-Length Packets:
>>
>>     For OUT endpoints, the following rules apply:
>>     - The BUFSIZ field must be ≥ 1 byte.
>>     - The total size of a Buffer Descriptor must be a multiple of
>>       MaxPacketSize
>>     - A received zero-length packet still requires a MaxPacketSize buffer.
>>       Therefore, if the expected amount of data to be received is a multiple
>>       of MaxPacketSize, software should add MaxPacketSize bytes to the buffer
>>       to sink a possible zero-length packet at the end of the transfer.
>>
>> But other UDC controllers don't need such alignment, so mentioned field
>> is set to "false". If buffer size is aligned to wMaxPacketSize, those
>> controllers may stuck on transaction. The example is DWC2.
>>
>> This patch checks gadget->quirk_ep_out_aligned_size field and aligns
>> rx_bytes_expected to wMaxPacketSize only when it's needed.
>>
>> Signed-off-by: Sam Protsenko <semen.protsenko@linaro.org>
>> ---
>>  drivers/usb/gadget/f_fastboot.c | 9 +++++++++
>>  1 file changed, 9 insertions(+)
>>
>> diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
>> index 2e87fee..54dcce0 100644
>> --- a/drivers/usb/gadget/f_fastboot.c
>> +++ b/drivers/usb/gadget/f_fastboot.c
>> @@ -58,6 +58,7 @@ static unsigned int fastboot_flash_session_id;
>>  static unsigned int download_size;
>>  static unsigned int download_bytes;
>>  static bool is_high_speed;
>> +static bool quirk_ep_out_aligned_size;
>>
>>  static struct usb_endpoint_descriptor fs_ep_in = {
>>       .bLength            = USB_DT_ENDPOINT_SIZE,
>> @@ -240,6 +241,8 @@ static int fastboot_set_alt(struct usb_function *f,
>>       debug("%s: func: %s intf: %d alt: %d\n",
>>             __func__, f->name, interface, alt);
>>
>> +     quirk_ep_out_aligned_size = gadget->quirk_ep_out_aligned_size;
>> +
>>       /* make sure we don't enable the ep twice */
>>       if (gadget->speed == USB_SPEED_HIGH) {
>>               ret = usb_ep_enable(f_fb->out_ep, &hs_ep_out);
>> @@ -435,12 +438,18 @@ static unsigned int rx_bytes_expected(unsigned int maxpacket)
>>               return 0;
>>       if (rx_remain > EP_BUFFER_SIZE)
>>               return EP_BUFFER_SIZE;
>> +
>> +     if (!quirk_ep_out_aligned_size)
>> +             goto out;
>> +
>>       if (rx_remain < maxpacket) {
>>               rx_remain = maxpacket;
>>       } else if (rx_remain % maxpacket != 0) {
>>               rem = rx_remain % maxpacket;
>>               rx_remain = rx_remain + (maxpacket - rem);
>>       }
>> +
>> +out:
>>       return rx_remain;
>>  }
>>
>>
>
> Why do we need a special flag for this driver if other drivers e.g. mass storage
> are doing perfectly fine without it?
>

I don't know how it works in other gadgets, but please see this patch
in kernel: [1]. That patch is doing just the same as I did (and also
in gadget code), using usb_ep_align_maybe() function for alignment.

> This patch is just covering up the real problem, by bypassing the faulty code
> with a flag.
>
> The buffer size is EP_BUFFER_SIZE and is already aligned to wMaxPacketSize so
> the problem shouldn't have happened in the first place. But it is happening
> indicating something else is wrong.
>

There is what I'm observing on platform with DWC3 controller:
 - when doing "fastboot flash xloader MLO":
 - the whole size of data to send is 70964 bytes
 - the size of all packets (except of last packet) is 4096 bytes
 - so those are being sent just fine (in req->complete, which is
rx_handler_dl_image() function)
 - but last packet has size of 1332 bytes (because 70964 % 4096 = 1332)
 - when its req->length is aligned to wMaxPacketSize (so it's 1536
bytes): after we send it using usb_ep_queue(), the req->complete
callback is called one last time and we see that transmission is
finished (download_bytes >= download_size)
 - but when its req->length is not aligned to wMaxPacketSize (so it's
1332 bytes): req->complete callback is not called last time, so
transaction is not finished and we are stuck in "fastboot flash"

So I guess the issue is real and related to DWC3 quirk. If you have
any thoughts regarding other possible causes of this problem -- please
share. I can't predict all possible causes as I'm not USB expert.

> Why is the code using wMaxPacketSize for the check. why can't it just use usb_ep->maxpacket?
>

I just reused already existed code. Sure we can use usb_ep->maxpacket
instead, it's also 512 in my case (I believe it's assigned in DWC3
code).

[1] https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=219580e64f035bb9018dbb08d340f90b0ac50f8c


> cheers,
> -roger
diff mbox

Patch

diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index 2e87fee..54dcce0 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -58,6 +58,7 @@  static unsigned int fastboot_flash_session_id;
 static unsigned int download_size;
 static unsigned int download_bytes;
 static bool is_high_speed;
+static bool quirk_ep_out_aligned_size;
 
 static struct usb_endpoint_descriptor fs_ep_in = {
 	.bLength            = USB_DT_ENDPOINT_SIZE,
@@ -240,6 +241,8 @@  static int fastboot_set_alt(struct usb_function *f,
 	debug("%s: func: %s intf: %d alt: %d\n",
 	      __func__, f->name, interface, alt);
 
+	quirk_ep_out_aligned_size = gadget->quirk_ep_out_aligned_size;
+
 	/* make sure we don't enable the ep twice */
 	if (gadget->speed == USB_SPEED_HIGH) {
 		ret = usb_ep_enable(f_fb->out_ep, &hs_ep_out);
@@ -435,12 +438,18 @@  static unsigned int rx_bytes_expected(unsigned int maxpacket)
 		return 0;
 	if (rx_remain > EP_BUFFER_SIZE)
 		return EP_BUFFER_SIZE;
+
+	if (!quirk_ep_out_aligned_size)
+		goto out;
+
 	if (rx_remain < maxpacket) {
 		rx_remain = maxpacket;
 	} else if (rx_remain % maxpacket != 0) {
 		rem = rx_remain % maxpacket;
 		rx_remain = rx_remain + (maxpacket - rem);
 	}
+
+out:
 	return rx_remain;
 }