summaryrefslogtreecommitdiff
path: root/firmware/src/os
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 /firmware/src/os
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.
Diffstat (limited to 'firmware/src/os')
-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