diff options
author | Min Xu <min.xu@min-info.net> | 2014-10-25 20:39:28 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2014-11-11 22:30:54 +0100 |
commit | e39637e1b816d841e94f2a32f9bf18e0ee89269e (patch) | |
tree | 169c220b7a5c2e84a029c3f40bf8f3d1c0ef8cf6 /firmware/src/os | |
parent | 7fcc62940d35ae31bdf5e5ebdd5ca55c8f4429c2 (diff) |
pcd_enumerate: Avoid disabling interrupt during endpoint refill
The old implementation has a big local_irq_save() / restore() around the
endpoint re-fill routine. This disables interrupts for a long time,
psossibly causing lost interrupts.
The new implementation works around this by disabling the USB (UDP)
interrupt only, rather than disabling all interrupts on the system.
Diffstat (limited to 'firmware/src/os')
-rw-r--r-- | firmware/src/os/pcd_enumerate.c | 53 |
1 files changed, 28 insertions, 25 deletions
diff --git a/firmware/src/os/pcd_enumerate.c b/firmware/src/os/pcd_enumerate.c index 65ae9d9..62f5809 100644 --- a/firmware/src/os/pcd_enumerate.c +++ b/firmware/src/os/pcd_enumerate.c @@ -139,7 +139,7 @@ void udp_unthrottle(void) pUDP->UDP_IER = AT91C_UDP_EPINT1; } -static int __udp_refill_ep(int ep) +int udp_refill_ep(int ep) { u_int16_t i; AT91PS_UDP pUDP = upcd.pUdp; @@ -154,9 +154,11 @@ static int __udp_refill_ep(int ep) /* If there are already two packets in transit, the DPR of * the SAM7 UDC doesn't have space for more data */ - if (atomic_read(&upcd.ep[ep].pkts_in_transit) == 2) { + if (atomic_read(&upcd.ep[ep].pkts_in_transit) == 2) return -EBUSY; - } + + /* disable endpoint interrup */ + pUDP->UDP_IDR |= 1 << ep; /* If we have an incompletely-transmitted req_ctx (>EP size), * we need to transmit the rest and finish the transaction */ @@ -167,8 +169,23 @@ static int __udp_refill_ep(int ep) /* get pending rctx and start transmitting from zero */ rctx = req_ctx_find_get(0, epstate[ep].state_pending, epstate[ep].state_busy); - if (!rctx) + if (!rctx) { + /* re-enable endpoint interrupt */ + pUDP->UDP_IER |= 1 << ep; + return 0; + } + if (rctx->tot_len == 0) { + /* re-enable endpoint interrupt */ + pUDP->UDP_IER |= 1 << ep; + req_ctx_put(rctx); return 0; + } + DEBUGPCR("USBTx %08X, Len = %04u, Head 4/Tail 4 bytes: %02X %02X %02X %02X / %02X %02X %02X %02X", + rctx->data, rctx->tot_len, + rctx->data[4], rctx->data[5], rctx->data[6], rctx->data[7], + rctx->data[rctx->tot_len - 4], rctx->data[rctx->tot_len - 3], + rctx->data[rctx->tot_len - 2], rctx->data[rctx->tot_len - 1]); + start = 0; upcd.ep[ep].incomplete.bytes_sent = 0; @@ -180,8 +197,6 @@ static int __udp_refill_ep(int ep) end = start + AT91C_EP_IN_SIZE; /* fill FIFO/DPR */ - DEBUGII("RCTX_tx(ep=%u,ctx=%u):%u ", ep, req_ctx_num(rctx), - end - start); for (i = start; i < end; i++) pUDP->UDP_FDR[ep] = rctx->data[i]; @@ -190,15 +205,13 @@ static int __udp_refill_ep(int ep) pUDP->UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY; } - if ((end - start < AT91C_EP_OUT_SIZE) || - (((end - start) == 0) && end && (rctx->tot_len % AT91C_EP_OUT_SIZE) == 0)) { + if (end == rctx->tot_len) { /* CASE 1: return context to pool, if * - packet transfer < AT91C_EP_OUT_SIZE * - after ZLP of transfer == AT91C_EP_OUT_SIZE * - after ZLP of transfer % AT91C_EP_OUT_SIZE == 0 * - after last packet of transfer % AT91C_EP_OUT_SIZE != 0 */ - DEBUGII("RCTX(ep=%u,ctx=%u)_tx_done ", ep, req_ctx_num(rctx)); upcd.ep[ep].incomplete.rctx = NULL; req_ctx_put(rctx); } else { @@ -206,25 +219,15 @@ static int __udp_refill_ep(int ep) * - after data of transfer == AT91C_EP_OUT_SIZE * - after data of transfer > AT91C_EP_OUT_SIZE * - after last packet of transfer % AT91C_EP_OUT_SIZE == 0 - */ + */ upcd.ep[ep].incomplete.rctx = rctx; upcd.ep[ep].incomplete.bytes_sent += end - start; - DEBUGII("RCTX(ep=%u)_tx_cont ", ep); } - return 1; -} + /* re-enable endpoint interrupt */ + pUDP->UDP_IER |= 1 << ep; -int udp_refill_ep(int ep) -{ - unsigned long flags; - int ret; - - local_irq_save(flags); - ret = __udp_refill_ep(ep); - local_irq_restore(flags); - - return ret; + return 1; } static void udp_irq(void) @@ -361,7 +364,7 @@ cont_ep2: if (atomic_dec_return(&upcd.ep[2].pkts_in_transit) == 1) pUDP->UDP_CSR[2] |= AT91C_UDP_TXPKTRDY; - __udp_refill_ep(2); + udp_refill_ep(2); } } if (isr & AT91C_UDP_EPINT3) { @@ -376,7 +379,7 @@ cont_ep2: if (atomic_dec_return(&upcd.ep[3].pkts_in_transit) == 1) pUDP->UDP_CSR[3] |= AT91C_UDP_TXPKTRDY; - __udp_refill_ep(3); + udp_refill_ep(3); } } if (isr & AT91C_UDP_RXSUSP) { |