diff options
author | Harald Welte <laforge@gnumonks.org> | 2011-07-07 17:39:32 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2011-07-07 17:39:32 +0200 |
commit | fe88b83e80df8be0351ff38ee6a77b855b0cd0a9 (patch) | |
tree | 93f6d6c0bd11d9f3a92a5daef69db1df6eb4ca44 /firmware | |
parent | 30cb576ca5ed5281e997333fd522440faa595565 (diff) |
dfu: fix EP0 IN trasnfers that are even multiple of 8
We have to send a ZLP at the end of such transfers, otherwise the host
will time out at the end of the transfer. This resulted in multi-second
delays for recognizing the SIMtrace hardware by the USB host.
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/src/dfu/dfu.c | 18 |
1 files changed, 14 insertions, 4 deletions
diff --git a/firmware/src/dfu/dfu.c b/firmware/src/dfu/dfu.c index f740800..8425c28 100644 --- a/firmware/src/dfu/dfu.c +++ b/firmware/src/dfu/dfu.c @@ -86,18 +86,20 @@ static void __dfufunc udp_init(void) AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPCD_PIO_UDP_PUPv4); } +static void __dfufunc udp_ep0_send_zlp(void); + /* Send Data through the control endpoint */ static void __dfufunc udp_ep0_send_data(const char *pData, u_int32_t length) { AT91PS_UDP pUdp = AT91C_BASE_UDP; - u_int32_t cpt = 0; + u_int32_t cpt = 0, len_remain = length; AT91_REG csr; DEBUGE("send_data: %u bytes ", length); do { - cpt = MIN(length, 8); - length -= cpt; + cpt = MIN(len_remain, 8); + len_remain -= cpt; while (cpt--) pUdp->UDP_FDR[0] = *pData++; @@ -119,12 +121,20 @@ static void __dfufunc udp_ep0_send_data(const char *pData, u_int32_t length) } } while (!(csr & AT91C_UDP_TXCOMP)); - } while (length); + } while (len_remain); if (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) { pUdp->UDP_CSR[0] &= ~(AT91C_UDP_TXCOMP); while (pUdp->UDP_CSR[0] & AT91C_UDP_TXCOMP) ; } + + if ((length % 8) == 0) { + /* if the length is a multiple of the EP size, we need + * to send another ZLP (zero-length packet) to tell the + * host the transfer has completed. */ + DEBUGE("set_txpktrdy_zlp "); + udp_ep0_send_zlp(); + } } static void udp_ep0_recv_clean(void) |