summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMin Xu <min.xu@min-info.net>2014-10-25 20:39:28 +0200
committerHarald Welte <laforge@gnumonks.org>2014-11-11 22:30:54 +0100
commite39637e1b816d841e94f2a32f9bf18e0ee89269e (patch)
tree169c220b7a5c2e84a029c3f40bf8f3d1c0ef8cf6
parent7fcc62940d35ae31bdf5e5ebdd5ca55c8f4429c2 (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.
-rw-r--r--firmware/src/os/pcd_enumerate.c53
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) {
personal git repositories of Harald Welte. Your mileage may vary