summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author(no author) <(no author)@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>2006-07-30 10:17:48 +0000
committer(no author) <(no author)@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>2006-07-30 10:17:48 +0000
commitfa5c8c7c635a99453c8b6f7e0ff7e970285d83f5 (patch)
tree85c0d2c0bb2bbbde065728e7f75ebec6f19263f1
parent628c6776c9a4736fdb434a597ccccf602ef90c39 (diff)
implement new request context and interrupt handling
git-svn-id: https://svn.openpcd.org:2342/trunk@61 6dc7ffe9-61d6-0310-9af1-9938baff3ed1
-rw-r--r--openpcd/firmware/src/pcd_enumerate.c111
-rw-r--r--openpcd/firmware/src/pcd_enumerate.h15
2 files changed, 115 insertions, 11 deletions
diff --git a/openpcd/firmware/src/pcd_enumerate.c b/openpcd/firmware/src/pcd_enumerate.c
index 80926bc..dd0ba48 100644
--- a/openpcd/firmware/src/pcd_enumerate.c
+++ b/openpcd/firmware/src/pcd_enumerate.c
@@ -18,6 +18,7 @@
// 12. Apr. 2006: added modification found in the mikrocontroller.net gcc-Forum
// additional line marked with /* +++ */
+#include <errno.h>
#include <sys/types.h>
#include <usb_ch9.h>
#include <lib_AT91SAM7.h>
@@ -129,6 +130,44 @@ const struct _desc cfgDescriptor = {
static void udp_ep0_handler(void);
+void udp_unthrottle(void)
+{
+ AT91PS_UDP pUDP = pCDC.pUdp;
+ pUDP->UDP_IER = AT91C_UDP_EPINT1;
+}
+
+int udp_refill_ep(int ep, struct req_ctx *rctx)
+{
+ u_int16_t i;
+ AT91PS_UDP pUDP = pCDC.pUdp;
+
+ if (rctx->tx.tot_len > 64) {
+ DEBUGPCRF("TOO LARGE!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
+ return -EINVAL;
+ }
+
+ DEBUGP("refilling EP%u ", ep);
+
+ /* FIXME: state machine to handle two banks of FIFO memory, etc */
+ if (atomic_read(&pCDC.ep[ep].pkts_in_transit) >= 2)
+ return -EBUSY;
+
+ /* fill FIFO/DPR */
+ for (i = 0; i < rctx->tx.tot_len; i++)
+ pUDP->UDP_FDR[ep] = rctx->tx.data[i];
+
+ if (atomic_read(&pCDC.ep[ep].pkts_in_transit) == 0) {
+ /* start transmit */
+ pUDP->UDP_CSR[ep] |= AT91C_UDP_TXPKTRDY;
+ atomic_inc(&pCDC.ep[ep].pkts_in_transit);
+ }
+
+ /* return rctx to pool of free contexts */
+ req_ctx_put(rctx);
+
+ return 0;
+}
+
static void udp_irq(void)
{
u_int32_t csr;
@@ -174,7 +213,8 @@ static void udp_irq(void)
if (csr & cur_rcv_bank) {
u_int16_t pkt_recv = 0;
u_int16_t pkt_size = csr >> 16;
- struct req_ctx *rctx = req_ctx_find_get();
+ struct req_ctx *rctx = req_ctx_find_get(RCTX_STATE_FREE,
+ RCTX_STATE_UDP_RCV_BUSY);
if (rctx) {
rctx->rx.tot_len = pkt_size;
@@ -190,32 +230,61 @@ static void udp_irq(void)
DEBUGP(rctx->rx.data);
#endif
pCDC.currentRcvBank = cur_rcv_bank;
- } else
- DEBUGP("NO RCTX AVAIL! ");
+ req_ctx_set_state(rctx, RCTX_STATE_UDP_RCV_DONE);
+ DEBUGP("RCTX=%u ", req_ctx_num(rctx));
+ } else {
+ /* disable interrupts for now */
+ pUDP->UDP_IDR = AT91C_UDP_EPINT1;
+ DEBUGP("NO_RCTX_AVAIL! ");
+ }
}
}
if (isr & AT91C_UDP_EPINT2) {
csr = pUDP->UDP_CSR[2];
- //pUDP->UDP_ICR = AT91C_UDP_EPINT2;
- DEBUGP("EP2INT(In) ");
+ DEBUGP("EP2INT(In, CSR=0x%08x) ", csr);
+ if (csr & AT91C_UDP_TXCOMP) {
+ struct req_ctx *rctx;
+
+ DEBUGP("ACK_TX_COMP ");
+ /* acknowledge TX completion */
+ pUDP->UDP_CSR[2] &= ~AT91C_UDP_TXCOMP;
+ while (pUDP->UDP_CSR[2] & AT91C_UDP_TXCOMP) ;
+
+ /* if we already have another packet in DPR, send it */
+ if (atomic_dec_return(&pCDC.ep[2].pkts_in_transit) == 1)
+ pUDP->UDP_CSR[2] |= AT91C_UDP_TXPKTRDY;
+
+ /* try to re-fill from pending rcts for EP2 */
+ rctx = req_ctx_find_get(RCTX_STATE_UDP_EP2_PENDING,
+ RCTX_STATE_UDP_EP2_BUSY);
+ if (rctx)
+ udp_refill_ep(2, rctx);
+ else
+ DEBUGP("NO_RCTX_pending ");
+
+ /* FIXME: re-fill second packet into DPR */
+ }
}
if (isr & AT91C_UDP_EPINT3) {
csr = pUDP->UDP_CSR[3];
- //pUDP->UDP_ICR = AT91C_UDP_EPINT3;
- DEBUGP("EP3INT(Interrupt) ");
+ DEBUGP("EP3INT(Interrupt, CSR=0x%08x) ", csr);
+ /* Transmit has completed, re-fill from pending rcts for EP3 */
}
if (isr & AT91C_UDP_RXSUSP) {
pUDP->UDP_ICR = AT91C_UDP_RXSUSP;
DEBUGP("RXSUSP ");
+ /* FIXME: implement suspend/resume */
}
if (isr & AT91C_UDP_RXRSM) {
pUDP->UDP_ICR = AT91C_UDP_RXRSM;
DEBUGP("RXRSM ");
+ /* FIXME: implement suspend/resume */
}
if (isr & AT91C_UDP_EXTRSM) {
pUDP->UDP_ICR = AT91C_UDP_EXTRSM;
DEBUGP("EXTRSM ");
+ /* FIXME: implement suspend/resume */
}
if (isr & AT91C_UDP_SOFINT) {
pUDP->UDP_ICR = AT91C_UDP_SOFINT;
@@ -224,6 +293,7 @@ static void udp_irq(void)
if (isr & AT91C_UDP_WAKEUP) {
pUDP->UDP_ICR = AT91C_UDP_WAKEUP;
DEBUGP("WAKEUP ");
+ /* FIXME: implement suspend/resume */
}
DEBUGP("END\r\n");
@@ -276,6 +346,7 @@ u_int8_t udp_is_configured(void)
return pCdc->currentConfiguration;
}
+
/* Send data through endpoint 2/3 */
u_int32_t AT91F_UDP_Write(u_int8_t irq, const unsigned char *pData, u_int32_t length)
{
@@ -394,8 +465,21 @@ static void udp_ep0_handler(void)
AT91PS_UDP pUDP = pCdc->pUdp;
u_int8_t bmRequestType, bRequest;
u_int16_t wValue, wIndex, wLength, wStatus;
+ u_int32_t csr = pUDP->UDP_CSR[0];
+
+ DEBUGP("CSR=0x%04x ", csr);
- if (!(pUDP->UDP_CSR[0] & AT91C_UDP_RXSETUP)) {
+ if (csr & AT91C_UDP_STALLSENT) {
+ DEBUGP("ACK_STALLSENT ");
+ pUDP->UDP_CSR[0] = ~AT91C_UDP_STALLSENT;
+ }
+
+ if (csr & AT91C_UDP_RX_DATA_BK0) {
+ DEBUGP("ACK_BANK0 ");
+ pUDP->UDP_CSR[0] = ~AT91C_UDP_RX_DATA_BK0;
+ }
+
+ if (!(csr & AT91C_UDP_RXSETUP)) {
DEBUGP("no setup packet ");
return;
}
@@ -451,7 +535,7 @@ static void udp_ep0_handler(void)
pUDP->UDP_CSR[3] =
(wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN) : 0;
pUDP->UDP_IER = (AT91C_UDP_EPINT0|AT91C_UDP_EPINT1|
- 0);//AT91C_UDP_EPINT2|AT91C_UDP_EPINT3);
+ AT91C_UDP_EPINT2|AT91C_UDP_EPINT3);
break;
case STD_GET_CONFIGURATION:
DEBUGP("GET_CONFIG ");
@@ -516,6 +600,7 @@ static void udp_ep0_handler(void)
DEBUGP("CLEAR_FEATURE_ENDPOINT(EPidx=%u) ", wIndex & 0x0f);
wIndex &= 0x0F;
if ((wValue == 0) && wIndex && (wIndex <= 3)) {
+ struct req_ctx *rctx;
if (wIndex == 1) {
pUDP->UDP_CSR[1] =
(AT91C_UDP_EPEDS |
@@ -529,6 +614,10 @@ static void udp_ep0_handler(void)
AT91C_UDP_EPTYPE_BULK_IN);
pUDP->UDP_RSTEP |= AT91C_UDP_EP2;
pUDP->UDP_RSTEP &= ~AT91C_UDP_EP2;
+
+ /* free all currently transmitting contexts */
+ while (rctx = req_ctx_find_get(RCTX_STATE_UDP_EP2_BUSY,
+ RCTX_STATE_FREE)) {}
}
else if (wIndex == 3) {
pUDP->UDP_CSR[3] =
@@ -536,6 +625,10 @@ static void udp_ep0_handler(void)
AT91C_UDP_EPTYPE_INT_IN);
pUDP->UDP_RSTEP |= AT91C_UDP_EP3;
pUDP->UDP_RSTEP &= ~AT91C_UDP_EP3;
+
+ /* free all currently transmitting contexts */
+ while (rctx = req_ctx_find_get(RCTX_STATE_UDP_EP3_BUSY,
+ RCTX_STATE_FREE)) {}
}
udp_ep0_send_zlp(pUDP);
} else
diff --git a/openpcd/firmware/src/pcd_enumerate.h b/openpcd/firmware/src/pcd_enumerate.h
index 31d8f28..d08ba65 100644
--- a/openpcd/firmware/src/pcd_enumerate.h
+++ b/openpcd/firmware/src/pcd_enumerate.h
@@ -17,6 +17,8 @@
#include <sys/types.h>
#include <AT91SAM7.h>
+#include <asm/atomic.h>
+#include "src/openpcd.h"
#define AT91C_EP_OUT 1
#define AT91C_EP_OUT_SIZE 0x40
@@ -24,6 +26,11 @@
#define AT91C_EP_IN_SIZE 0x40
#define AT91C_EP_INT 3
+struct ep_ctx {
+ atomic_t pkts_in_transit;
+ void *ctx;
+};
+
typedef struct _AT91S_CDC
{
@@ -31,14 +38,18 @@ typedef struct _AT91S_CDC
unsigned char currentConfiguration;
unsigned char currentConnection;
unsigned int currentRcvBank;
- void *ep_ctx[4];
+ struct ep_ctx ep[4];
} AT91S_CDC, *AT91PS_CDC;
//* external function description
extern void udp_init(void);
u_int8_t AT91F_UDP_IsConfigured(void);
-u_int32_t AT91F_UDP_Write(u_int8_t irq, const unsigned char *pData, u_int32_t length);
+
+//u_int32_t AT91F_UDP_Write(u_int8_t irq, const unsigned char *pData, u_int32_t length);
+
+extern int udp_refill_ep(int ep, struct req_ctx *rctx);
+extern void udp_unthrottle(void);
#endif // CDC_ENUMERATE_H
personal git repositories of Harald Welte. Your mileage may vary