From 3b7035dcd9564b4980b33e674d722f547a488308 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 14:45:47 +0200 Subject: req_ctx: Use consistent numeric range for REQ_CTX_STATE ... which will allow us to check for invalid states by a simple 'greater than' check later on. --- firmware/src/os/pcd_enumerate.c | 6 ++++-- firmware/src/os/req_ctx.h | 39 ++++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 21 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/os/pcd_enumerate.c b/firmware/src/os/pcd_enumerate.c index 3a7397f..65ae9d9 100644 --- a/firmware/src/os/pcd_enumerate.c +++ b/firmware/src/os/pcd_enumerate.c @@ -99,8 +99,10 @@ struct epstate { }; static const struct epstate epstate[] = { - [0] = { .state_busy = RCTX_STATE_INVALID }, - [1] = { .state_busy = RCTX_STATE_INVALID }, + [0] = { .state_busy = RCTX_STATE_UDP_EP0_BUSY, + .state_pending = RCTX_STATE_UDP_EP0_PENDING }, + [1] = { .state_busy = RCTX_STATE_UDP_EP1_BUSY, + .state_pending = RCTX_STATE_UDP_EP1_PENDING }, [2] = { .state_busy = RCTX_STATE_UDP_EP2_BUSY, .state_pending = RCTX_STATE_UDP_EP2_PENDING }, [3] = { .state_busy = RCTX_STATE_UDP_EP3_BUSY, diff --git a/firmware/src/os/req_ctx.h b/firmware/src/os/req_ctx.h index 94b5c5a..7401243 100644 --- a/firmware/src/os/req_ctx.h +++ b/firmware/src/os/req_ctx.h @@ -20,25 +20,26 @@ struct req_ctx { u_int8_t *data; }; -#define RCTX_STATE_FREE 0xfe -#define RCTX_STATE_UDP_RCV_BUSY 0x01 -#define RCTX_STATE_UDP_RCV_DONE 0x02 -#define RCTX_STATE_MAIN_PROCESSING 0x03 -#define RCTX_STATE_RC632IRQ_BUSY 0x04 - -#define RCTX_STATE_UDP_EP2_PENDING 0x10 -#define RCTX_STATE_UDP_EP2_BUSY 0x11 - -#define RCTX_STATE_UDP_EP3_PENDING 0x12 -#define RCTX_STATE_UDP_EP3_BUSY 0x13 - -#define RCTX_STATE_SSC_RX_BUSY 0x20 - -#define RCTX_STATE_LIBRFID_BUSY 0x30 - -#define RCTX_STATE_PIOIRQ_BUSY 0x80 - -#define RCTX_STATE_INVALID 0xff +#define RCTX_STATE_FREE 0 +#define RCTX_STATE_UDP_RCV_BUSY 1 +#define RCTX_STATE_UDP_RCV_DONE 2 +#define RCTX_STATE_MAIN_PROCESSING 3 +#define RCTX_STATE_RC632IRQ_BUSY 4 +#define RCTX_STATE_UDP_EP2_PENDING 5 +#define RCTX_STATE_UDP_EP2_BUSY 6 +#define RCTX_STATE_UDP_EP3_PENDING 7 +#define RCTX_STATE_UDP_EP3_BUSY 8 +#define RCTX_STATE_SSC_RX_BUSY 9 +#define RCTX_STATE_LIBRFID_BUSY 10 +#define RCTX_STATE_PIOIRQ_BUSY 11 +#define RCTX_STATE_INVALID 12 +// Nominally UNUSED states +#define RCTX_STATE_UDP_EP0_PENDING 13 +#define RCTX_STATE_UDP_EP0_BUSY 14 +#define RCTX_STATE_UDP_EP1_PENDING 15 +#define RCTX_STATE_UDP_EP1_BUSY 16 +// Count of the number of STATES +#define RCTX_STATE_COUNT 17 extern struct req_ctx __ramfunc *req_ctx_find_get(int large, unsigned long old_state, unsigned long new_state); extern struct req_ctx *req_ctx_find_busy(void); -- cgit v1.2.3 From 40def0ab1a03243e6a6f7116fca30d67fd9e664f Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 14:47:13 +0200 Subject: change req_ctx sizes large 2048->1000; small 128->270 --- firmware/src/os/req_ctx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/os/req_ctx.h b/firmware/src/os/req_ctx.h index 7401243..edc48a3 100644 --- a/firmware/src/os/req_ctx.h +++ b/firmware/src/os/req_ctx.h @@ -1,8 +1,8 @@ #ifndef _REQ_CTX_H #define _REQ_CTX_H -#define RCTX_SIZE_LARGE 2048 -#define RCTX_SIZE_SMALL 128 +#define RCTX_SIZE_LARGE 1000 +#define RCTX_SIZE_SMALL 270 #define MAX_HDRSIZE sizeof(struct openpcd_hdr) #define MAX_REQSIZE (64-MAX_HDRSIZE) -- cgit v1.2.3 From b6b2c7d4829aab75feb9f1114b825bbde0491444 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 14:54:05 +0200 Subject: req_ctx: avoid loop iterations by introducing per-state queues In order to speed up the lookup of req_ctx, we keep per-state queues rather than iterating over all the request contexts that exist again and again. --- firmware/src/os/req_ctx.c | 142 ++++++++++++++++++++++++++++++++++++++-------- firmware/src/os/req_ctx.h | 1 + 2 files changed, 120 insertions(+), 23 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/os/req_ctx.c b/firmware/src/os/req_ctx.c index cc8d57b..67d6c93 100644 --- a/firmware/src/os/req_ctx.c +++ b/firmware/src/os/req_ctx.c @@ -43,63 +43,159 @@ static u_int8_t rctx_data_large[NUM_RCTX_LARGE][RCTX_SIZE_LARGE]; static struct req_ctx req_ctx[NUM_REQ_CTX]; +/* queue of RCTX indexed by their current state */ +static struct req_ctx *req_ctx_queues[RCTX_STATE_COUNT], *req_ctx_tails[RCTX_STATE_COUNT]; + struct req_ctx __ramfunc *req_ctx_find_get(int large, unsigned long old_state, unsigned long new_state) { + struct req_ctx *toReturn; unsigned long flags; - u_int8_t i; - - if (large) - i = NUM_RCTX_SMALL; - else - i = 0; - for (; i < NUM_REQ_CTX; i++) { - local_irq_save(flags); - if (req_ctx[i].state == old_state) { - req_ctx[i].state = new_state; - local_irq_restore(flags); - return &req_ctx[i]; - } - local_irq_restore(flags); + if (old_state >= RCTX_STATE_COUNT || new_state >= RCTX_STATE_COUNT) { + DEBUGPCR("Invalid parameters for req_ctx_find_get"); + return NULL; } - - return NULL; + local_irq_save(flags); + toReturn = req_ctx_queues[old_state]; + if (toReturn) { + if ((req_ctx_queues[old_state] = toReturn->next)) + toReturn->next->prev = NULL; + else + req_ctx_tails[old_state] = NULL; + if ((toReturn->prev = req_ctx_tails[new_state])) + toReturn->prev->next = toReturn; + else + req_ctx_queues[new_state] = toReturn; + req_ctx_tails[new_state] = toReturn; + toReturn->state = new_state; + toReturn->next = NULL; + } + local_irq_restore(flags); + return toReturn; } u_int8_t req_ctx_num(struct req_ctx *ctx) { - return ((char *)ctx - (char *)&req_ctx[0])/sizeof(*ctx); + return ctx - req_ctx; } void req_ctx_set_state(struct req_ctx *ctx, unsigned long new_state) { unsigned long flags; + unsigned old_state; - /* FIXME: do we need this kind of locking, we're UP! */ + if (new_state >= RCTX_STATE_COUNT) { + DEBUGPCR("Invalid new_state for req_ctx_set_state"); + return; + } local_irq_save(flags); + old_state = ctx->state; + if (ctx->prev) + ctx->prev->next = ctx->next; + else + req_ctx_queues[old_state] = ctx->next; + if (ctx->next) + ctx->next->prev = ctx->prev; + else + req_ctx_tails[old_state] = ctx->prev; + + if ((ctx->prev = req_ctx_tails[new_state])) + ctx->prev->next = ctx; + else + req_ctx_queues[new_state] = ctx; + req_ctx_tails[new_state] = ctx; ctx->state = new_state; + ctx->next = NULL; local_irq_restore(flags); } +#ifdef DEBUG_REQCTX +void req_print(int state) { + int count = 0; + struct req_ctx *ctx, *last = NULL; + DEBUGP("State [%02i] start <==> ", state); + ctx = req_ctx_queues[state]; + while (ctx) { + if (last != ctx->prev) + DEBUGP("*INV_PREV* "); + DEBUGP("%08X => ", ctx); + last = ctx; + ctx = ctx->next; + count++; + if (count > NUM_REQ_CTX) { + DEBUGP("*WILD POINTER* => "); + break; + } + } + DEBUGPCR("NULL"); + if (!req_ctx_queues[state] && req_ctx_tails[state]) { + DEBUGPCR("NULL head, NON-NULL tail"); + } + if (last != req_ctx_tails[state]) { + DEBUGPCR("Tail does not match last element"); + } +} +#endif + void req_ctx_put(struct req_ctx *ctx) { - req_ctx_set_state(ctx, RCTX_STATE_FREE); + unsigned long intcFlags; + unsigned old_state; + + local_irq_save(intcFlags); + old_state = ctx->state; + if (ctx->prev) + ctx->prev->next = ctx->next; + else + req_ctx_queues[old_state] = ctx->next; + if (ctx->next) + ctx->next->prev = ctx->prev; + else + req_ctx_tails[old_state] = ctx->prev; + + if ((ctx->prev = req_ctx_tails[RCTX_STATE_FREE])) + ctx->prev->next = ctx; + else + req_ctx_queues[RCTX_STATE_FREE] = ctx; + req_ctx_tails[RCTX_STATE_FREE] = ctx; + ctx->state = RCTX_STATE_FREE; + ctx->next = NULL; + local_irq_restore(intcFlags); } void req_ctx_init(void) { int i; - for (i = 0; i < NUM_RCTX_SMALL; i++) { + req_ctx[i].prev = req_ctx + i - 1; + req_ctx[i].next = req_ctx + i + 1; req_ctx[i].size = RCTX_SIZE_SMALL; + req_ctx[i].tot_len = 0; req_ctx[i].data = rctx_data[i]; req_ctx[i].state = RCTX_STATE_FREE; + DEBUGPCR("SMALL req_ctx[%02i] initialized at %08X, Data: %08X => %08X", + i, req_ctx + i, req_ctx[i].data, req_ctx[i].data + RCTX_SIZE_SMALL); } - for (i = 0; i < NUM_RCTX_LARGE; i++) { - req_ctx[NUM_RCTX_SMALL+i].size = RCTX_SIZE_LARGE; - req_ctx[NUM_RCTX_SMALL+i].data = rctx_data_large[i]; + for (; i < NUM_REQ_CTX; i++) { + req_ctx[i].prev = req_ctx + i - 1; + req_ctx[i].next = req_ctx + i + 1; + req_ctx[i].size = RCTX_SIZE_LARGE; + req_ctx[i].tot_len = 0; + req_ctx[i].data = rctx_data_large[i]; + req_ctx[i].state = RCTX_STATE_FREE; + DEBUGPCR("LARGE req_ctx[%02i] initialized at %08X, Data: %08X => %08X", + i, req_ctx + i, req_ctx[i].data, req_ctx[i].data + RCTX_SIZE_LARGE); + } + req_ctx[0].prev = NULL; + req_ctx[NUM_REQ_CTX - 1].next = NULL; + + req_ctx_queues[RCTX_STATE_FREE] = req_ctx; + req_ctx_tails[RCTX_STATE_FREE] = req_ctx + NUM_REQ_CTX - 1; + + for (i = RCTX_STATE_FREE + 1; i < RCTX_STATE_COUNT; i++) { + req_ctx_queues[i] = req_ctx_tails[i] = NULL; } } diff --git a/firmware/src/os/req_ctx.h b/firmware/src/os/req_ctx.h index edc48a3..1e53fa4 100644 --- a/firmware/src/os/req_ctx.h +++ b/firmware/src/os/req_ctx.h @@ -15,6 +15,7 @@ struct req_ctx { volatile u_int32_t state; + volatile struct req_ctx *prev, *next; u_int16_t size; u_int16_t tot_len; u_int8_t *data; -- cgit v1.2.3 From ec975220d0a559b5c3df65c8d0dfc5745997e2d9 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 14:55:04 +0200 Subject: req_ctx: Change number of small / large req_ctx --- firmware/src/os/req_ctx.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'firmware/src') diff --git a/firmware/src/os/req_ctx.c b/firmware/src/os/req_ctx.c index 67d6c93..2411f62 100644 --- a/firmware/src/os/req_ctx.c +++ b/firmware/src/os/req_ctx.c @@ -27,7 +27,11 @@ #include "../openpcd.h" /* FIXME: locking, FIFO order processing */ +#define NUM_RCTX_SMALL 0 +#define NUM_RCTX_LARGE 19 +// 8 * 128 + 4 * 2048 => 18 * 270 + 2 * 2048 +/* #if defined(__AT91SAM7S64__) || defined(RUN_FROM_RAM) #define NUM_RCTX_SMALL 16 #define NUM_RCTX_LARGE 1 @@ -35,7 +39,7 @@ #define NUM_RCTX_SMALL 8 #define NUM_RCTX_LARGE 4 #endif - +*/ #define NUM_REQ_CTX (NUM_RCTX_SMALL+NUM_RCTX_LARGE) static u_int8_t rctx_data[NUM_RCTX_SMALL][RCTX_SIZE_SMALL]; -- cgit v1.2.3 From ca4e80551b2144cb08a72a58b6c13c2f88b4b2a0 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 20:06:32 +0200 Subject: dbgu: make debug routines re-entrant --- firmware/src/os/dbgu.c | 305 +++++++++++++++++++++++++++++++------------------ firmware/src/os/dbgu.h | 7 +- firmware/src/os/main.c | 1 - 3 files changed, 193 insertions(+), 120 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/os/dbgu.c b/firmware/src/os/dbgu.c index 113208e..d8bf8d1 100644 --- a/firmware/src/os/dbgu.c +++ b/firmware/src/os/dbgu.c @@ -45,18 +45,18 @@ #include #include -//#define DEBUG_UNBUFFERED +/* In case we find that while (); is non interruptible, we may need to + * uncommend this line: */ +// #define ALLOW_INTERRUPT_LOOP asm("nop;nop;nop;nop;nop;nop;nop;nop;") + +#define ALLOW_INTERRUPT_LOOP + +/* DEBUG_BUFFER_SIZE MUST BE A POWER OF 2 */ +#define DEBUG_BUFFER_SIZE (1 << 10) +#define DEBUG_BUFFER_MASK (DEBUG_BUFFER_SIZE - 1) #define USART_SYS_LEVEL 4 /*---------------------------- Global Variable ------------------------------*/ -//*--------------------------1-------------------------------------------------- -//* \fn AT91F_DBGU_Printk -//* \brief This function is used to send a string through the DBGU channel -//*---------------------------------------------------------------------------- -void AT91F_DBGU_Ready(void) -{ - while (!(AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXEMPTY)) ; -} //*---------------------------------------------------------------------------- //* Function Name : Send_reset @@ -69,7 +69,7 @@ static void Send_reset(void) // Acknoledge the interrupt // Mark the End of Interrupt on the AIC AT91C_BASE_AIC->AIC_EOICR = 0; - AT91F_DBGU_Ready(); + while (!(AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXEMPTY)) ; // Jump in reset pfct(); } @@ -92,26 +92,26 @@ static void DBGU_irq_handler(u_int32_t sr) break; case '1': //* info udp_pullup_off(); - AT91F_DBGU_Printk("Set Pull up\n\r"); + AT91F_DBGU_Frame("Set Pull up\n\r"); // Reset Application Send_reset(); break; case '2': - AT91F_DBGU_Printk("Toggling LED 1\n\r"); + AT91F_DBGU_Frame("Toggling LED 1\n\r"); led_toggle(1); break; case '3': - AT91F_DBGU_Printk("Toggling LED 2\n\r"); + AT91F_DBGU_Frame("Toggling LED 2\n\r"); led_toggle(2); break; case '9': - AT91F_DBGU_Printk("Resetting SAM7\n\r"); + AT91F_DBGU_Frame("Resetting SAM7\n\r"); AT91F_RSTSoftReset(AT91C_BASE_RSTC, AT91C_RSTC_PROCRST| AT91C_RSTC_PERRST|AT91C_RSTC_EXTRST); break; default: if (_main_dbgu(value) < 0) - AT91F_DBGU_Printk("\n\r"); + AT91F_DBGU_Frame("\n\r"); break; } // end switch } @@ -148,17 +148,17 @@ void AT91F_DBGU_Init(void) //* open interrupt sysirq_register(AT91SAM7_SYSIRQ_DBGU, &DBGU_irq_handler); - AT91F_DBGU_Printk("\n\r"); - AT91F_DBGU_Printk("(C) 2006-2011 by Harald Welte \n\r" + debugp("\n\r"); + debugp("(C) 2006-2011 by Harald Welte \n\r" "This software is FREE SOFTWARE licensed under GNU GPL\n\r"); - AT91F_DBGU_Printk("Version " COMPILE_SVNREV + debugp("Version " COMPILE_SVNREV " compiled " COMPILE_DATE " by " COMPILE_BY "\n\r\n\r"); - AT91F_DBGU_Printk("\n\rDEBUG Interface:\n\r" + debugp("\n\rDEBUG Interface:\n\r" "0) Set Pull-up 1) Clear Pull-up 2) Toggle LED1 3) " "Toggle LED2\r\n9) Reset\n\r"); - debugp("RSTC_SR=0x%08x\n", rst_status); + debugp("RSTC_SR=0x%08x\n\r", rst_status); } /* @@ -172,32 +172,37 @@ void AT91F_DBGU_Fini(void) // Maybe FIXME, do more? -- Henryk Plötz } -//*---------------------------------------------------------------------------- -//* \fn AT91F_DBGU_Printk -//* \brief This function is used to send a string through the DBGU channel (Very low level debugging) -//*---------------------------------------------------------------------------- -void AT91F_DBGU_Printk(char *buffer) -{ - while (*buffer != '\0') { - while (!AT91F_US_TxReady((AT91PS_USART) AT91C_BASE_DBGU)) ; - AT91F_US_PutChar((AT91PS_USART) AT91C_BASE_DBGU, *buffer++); - } -} - //*---------------------------------------------------------------------------- //* \fn AT91F_DBGU_Frame -//* \brief This function is used to send a string through the DBGU channel +//* \brief This function is used to send a string through the DBGU channel (Very low level debugging) //*---------------------------------------------------------------------------- void AT91F_DBGU_Frame(char *buffer) { - unsigned char len; - - for (len = 0; buffer[len] != '\0'; len++) { + unsigned long intcFlags; + unsigned int len = 0; + while (buffer[len++]) ALLOW_INTERRUPT_LOOP; + len--; + local_irq_save(intcFlags); + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 9; // Disable transfer + if (AT91C_BASE_DBGU->DBGU_TNCR) { + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 8; // Resume transfer + local_irq_restore(intcFlags); + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0) ALLOW_INTERRUPT_LOOP; + local_irq_save(intcFlags); + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 9; // Disable transfer + AT91C_BASE_DBGU->DBGU_TPR = (unsigned)buffer; + AT91C_BASE_DBGU->DBGU_TCR = len; + } else if (AT91C_BASE_DBGU->DBGU_TCR) { + AT91C_BASE_DBGU->DBGU_TNPR = (unsigned)buffer; + AT91C_BASE_DBGU->DBGU_TNCR = len; + } else { + AT91C_BASE_DBGU->DBGU_TPR = (unsigned)buffer; + AT91C_BASE_DBGU->DBGU_TCR = len; } - - AT91F_US_SendFrame((AT91PS_USART) AT91C_BASE_DBGU, - (unsigned char *)buffer, len, 0, 0); - + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 8; // Resume transfer + local_irq_restore(intcFlags); + /* Return ONLY after we complete Transfer */ + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0) ALLOW_INTERRUPT_LOOP; } //*---------------------------------------------------------------------------- @@ -239,105 +244,177 @@ hexdump(const void *data, unsigned int len) } struct dbgu { - char buf[4096]; - char *next_inbyte; - char *next_outbyte; + char buf[DEBUG_BUFFER_SIZE]; + /* Since debugp appears to require to be re-entrant, we need a + * bit more state variables + * in_head Position where incoming *append* characters have + * finished copy to buffer + * in_tail Position where NEW incoming *append* characters + * should COPY to + * out_head Position where the LAST *possibly incomplete* + * dbgu_rb_flush started from + * out_tail Position where the NEXT dbug_rb_flush should + * start from + * The position in the RING should be in this order: + * --> out_tail --> in_head --> in_tail --> out_head --> + */ + volatile unsigned int in_head, in_tail, out_head, out_tail; + + /* flush_stack is to indicate rb_flush is being executed, NOT to + * execute again */ + volatile unsigned int flush_stack; + + /* append_stack is to indicate the re-entrack stack order of the + * current dbgu_append call */ + volatile unsigned int append_stack; }; + static struct dbgu dbgu; void dbgu_rb_init(void) { - memset(dbgu.buf, 0, sizeof(dbgu.buf)); - dbgu.next_inbyte = &dbgu.buf[0]; - dbgu.next_outbyte = &dbgu.buf[0]; -} - -/* pull one char out of debug ring buffer */ -static int dbgu_rb_pull(char *ret) -{ - unsigned long flags; - - local_irq_save(flags); - - if (dbgu.next_outbyte == dbgu.next_inbyte) { - local_irq_restore(flags); - return -1; - } - - *ret = *dbgu.next_outbyte; - - dbgu.next_outbyte++; - if (dbgu.next_outbyte == &dbgu.buf[0]+sizeof(dbgu.buf)) { - //AT91F_DBGU_Printk("WRAP DURING PULL\r\n"); - dbgu.next_outbyte = &dbgu.buf[0]; - } else if (dbgu.next_outbyte > &dbgu.buf[0]+sizeof(dbgu.buf)) { - //AT91F_DBGU_Printk("OUTBYTE > END_OF_BUF!!\r\n"); - dbgu.next_outbyte -= sizeof(dbgu.buf); - } - - local_irq_restore(flags); - - return 0; -} - -static void __rb_flush(void) -{ - char ch; - while (dbgu_rb_pull(&ch) >= 0) { - while (!AT91F_US_TxReady((AT91PS_USART) AT91C_BASE_DBGU)) ; - AT91F_US_PutChar((AT91PS_USART) AT91C_BASE_DBGU, ch); - } + dbgu.in_head = dbgu.in_tail = dbgu.out_head = dbgu.out_tail = 0; + dbgu.flush_stack = dbgu.append_stack = 0; } /* flush pending data from debug ring buffer to serial port */ -void dbgu_rb_flush(void) -{ - __rb_flush(); -} -static void __dbgu_rb_append(char *data, int len) +static void dbgu_rb_flush(void) { - char *pos = dbgu.next_inbyte; - - dbgu.next_inbyte += len; - if (dbgu.next_inbyte >= &dbgu.buf[0]+sizeof(dbgu.buf)) { - AT91F_DBGU_Printk("WRAP DURING APPEND\r\n"); - dbgu.next_inbyte -= sizeof(dbgu.buf); + unsigned long intcFlags; + unsigned int flush_stack, start, len; + if (dbgu.in_head == dbgu.out_tail) + return; + + /* Transmit can ONLY be disabled when Interrupt is disabled. We + * don't want to be interrupted while Transmit is disabled. */ + local_irq_save(intcFlags); + flush_stack = dbgu.flush_stack; + dbgu.flush_stack = 1; + if (flush_stack) { + local_irq_restore(intcFlags); + return; } - - memcpy(pos, data, len); + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 9; // Disable transfer + start = (unsigned)dbgu.buf + dbgu.out_tail; + len = (dbgu.in_head - dbgu.out_tail) & DEBUG_BUFFER_MASK; + if (dbgu.in_head > dbgu.out_tail || dbgu.in_head == 0) { // Just 1 fragmentf + if (AT91C_BASE_DBGU->DBGU_TNCR) { + if (AT91C_BASE_DBGU->DBGU_TNPR + AT91C_BASE_DBGU->DBGU_TNCR == start) { + AT91C_BASE_DBGU->DBGU_TNCR += len; + } else { + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 8; // Resume transfer + local_irq_restore(intcFlags); + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0) ALLOW_INTERRUPT_LOOP; + dbgu.out_head = dbgu.out_tail; // in case we are interrupted, they may need more space + /* Since the ONLY place where Transmit is started is dbgu_rb_flush and AT91F_DBGU_Frame and: + * 1) AT91F_DBGU_Frame always leave the dbgu in a TX completed state + * 2) dbgu_rb is non-reentrant by safeguard at the beginning of this routing + * We can assume that after this INTERRUPTIBLE while loop, there are NO data to be transmitted + */ + local_irq_save(intcFlags); + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 9; // Disable transfer + AT91C_BASE_DBGU->DBGU_TPR = start; + AT91C_BASE_DBGU->DBGU_TCR = len; + } + } else if (AT91C_BASE_DBGU->DBGU_TCR) { + if (AT91C_BASE_DBGU->DBGU_TPR + AT91C_BASE_DBGU->DBGU_TCR == start) { + dbgu.out_head = AT91C_BASE_DBGU->DBGU_TPR - (unsigned)dbgu.buf; + AT91C_BASE_DBGU->DBGU_TCR += len; + } else { + AT91C_BASE_DBGU->DBGU_TNPR = start; + AT91C_BASE_DBGU->DBGU_TNCR = len; + } + } else { + AT91C_BASE_DBGU->DBGU_TPR = start; + AT91C_BASE_DBGU->DBGU_TCR = len; + dbgu.out_head = dbgu.out_tail; + } + } else { // 2 fragments + if ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0) { + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 8; // Resume transfer + local_irq_restore(intcFlags); + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0) ALLOW_INTERRUPT_LOOP; + dbgu.out_head = dbgu.out_tail; // in case we are interrupted, they may need more space + local_irq_save(intcFlags); + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 9; // Disable transfer + } + AT91C_BASE_DBGU->DBGU_TPR = start; + AT91C_BASE_DBGU->DBGU_TCR = DEBUG_BUFFER_SIZE - dbgu.out_tail; + AT91C_BASE_DBGU->DBGU_TNPR = (unsigned)dbgu.buf; + AT91C_BASE_DBGU->DBGU_TNCR = dbgu.in_head; + dbgu.out_head = dbgu.out_tail; + } + AT91C_BASE_DBGU->DBGU_PTCR = 1 << 8; // Resume transfer + dbgu.out_tail = dbgu.in_head; + dbgu.flush_stack = 0; + local_irq_restore(intcFlags); } void dbgu_rb_append(char *data, int len) { - unsigned long flags; - int bytes_left; - char *data_cur; + /* Rules: + * 1) ONLY the LOWEST order of dbgu_rb_append CAN update in_head; + * 2) WHEN updateing in_head, always set it to the current in_tail (since all higher order dbgu_rb_append have completed + * 3) ONLY the LOWEST order of dbgu_rb_append MAY call dbgu_rb_flush + */ + unsigned long intcFlags; + unsigned int append_stack, avail, local_head; + if (len <= 0) + return; - local_irq_save(flags); - - bytes_left = &dbgu.buf[0]+sizeof(dbgu.buf)-dbgu.next_inbyte; - data_cur = data; - - if (len > bytes_left) { - AT91F_DBGU_Printk("LEN > BYTES_LEFT\r\n"); - __rb_flush(); - __dbgu_rb_append(data_cur, bytes_left); - len -= bytes_left; - data_cur += bytes_left; + local_irq_save(intcFlags); + append_stack = dbgu.append_stack++; + if (AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) + dbgu.out_head = dbgu.out_tail; + avail = (dbgu.out_head - 1 - dbgu.in_tail) & DEBUG_BUFFER_MASK; + local_head = (unsigned)len; + if (local_head > avail) { + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0); + dbgu.out_head = dbgu.out_tail; + avail = (dbgu.out_head - 1 - dbgu.in_tail) & DEBUG_BUFFER_MASK; + if (local_head > avail) { + local_head -= avail; + AT91C_BASE_DBGU->DBGU_TPR = (unsigned)data; + AT91C_BASE_DBGU->DBGU_TCR = local_head; + data += local_head; + len = avail; + } } - __dbgu_rb_append(data_cur, len); - - local_irq_restore(flags); + local_head = dbgu.in_tail; + dbgu.in_tail += len; + dbgu.in_tail &= DEBUG_BUFFER_MASK; + local_irq_restore(intcFlags); + if (dbgu.out_head <= local_head) { + // We may have to wrap around: out_head won't change because NO call to flush will be made YET + avail = DEBUG_BUFFER_SIZE - local_head; + if (avail >= (unsigned)len) { + memcpy(dbgu.buf + local_head, data, (size_t)len); + } else { + memcpy(dbgu.buf + local_head, data, (size_t)avail); + memcpy(dbgu.buf, data + avail, (size_t)(len - avail)); + } + } else { + memcpy(dbgu.buf + local_head, data, len); + } + local_irq_save(intcFlags); + dbgu.append_stack--; + if (!append_stack) + dbgu.in_head = dbgu.in_tail; + local_irq_restore(intcFlags); + if (!append_stack) + dbgu_rb_flush(); } static char dbg_buf[256]; +static int line_num = 0; void debugp(const char *format, ...) { va_list ap; va_start(ap, format); - vsnprintf(dbg_buf, sizeof(dbg_buf)-1, format, ap); + sprintf(dbg_buf, "[%06X] ", line_num++); + vsnprintf(dbg_buf + 9, sizeof(dbg_buf)-10, format, ap); va_end(ap); dbg_buf[sizeof(dbg_buf)-1] = '\0'; diff --git a/firmware/src/os/dbgu.h b/firmware/src/os/dbgu.h index c36ac8c..2a5b59d 100644 --- a/firmware/src/os/dbgu.h +++ b/firmware/src/os/dbgu.h @@ -17,17 +17,14 @@ #define AT91C_DBGU_BAUD 115200 -//#define DEBUGP(x) AT91F_DBGU_Printk(x) - //* ----------------------- External Function Prototype ----------------------- extern const char *hexdump(const void *data, unsigned int len); void AT91F_DBGU_Init(void); void AT91F_DBGU_Fini(void); -void AT91F_DBGU_Printk( char *buffer); -void AT91F_DBGU_Frame( char *buffer); +void AT91F_DBGU_Frame(char *buffer); +#define AT91F_DBGU_Printk(x) AT91F_DBGU_Frame(x) int AT91F_DBGU_Get( char *val); -void dbgu_rb_flush(void); #ifndef __WinARM__ void AT91F_DBGU_scanf(char * type,unsigned int * val); #endif diff --git a/firmware/src/os/main.c b/firmware/src/os/main.c index 968f6ad..363befc 100644 --- a/firmware/src/os/main.c +++ b/firmware/src/os/main.c @@ -77,7 +77,6 @@ int main(void) while (1) { /* Call application specific main idle function */ _main_func(); - dbgu_rb_flush(); /* restart watchdog timer */ wdt_restart(); -- cgit v1.2.3 From 7fcc62940d35ae31bdf5e5ebdd5ca55c8f4429c2 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 20:13:23 +0200 Subject: simtrace: Add heartbeat message to debug console --- firmware/src/simtrace/iso7816_uart.c | 10 ++++++++++ firmware/src/simtrace/iso7816_uart.h | 1 + firmware/src/simtrace/main_simtrace.c | 9 +++++++++ 3 files changed, 20 insertions(+) (limited to 'firmware/src') diff --git a/firmware/src/simtrace/iso7816_uart.c b/firmware/src/simtrace/iso7816_uart.c index 52522c0..611ccfb 100644 --- a/firmware/src/simtrace/iso7816_uart.c +++ b/firmware/src/simtrace/iso7816_uart.c @@ -125,6 +125,14 @@ static const u_int8_t di_table[] = { 12, 20, 2, 4, 8, 16, 32, 64, }; +void iso_uart_report_overrun(void) +{ + static unsigned lastOverrun = 0; + if (isoh.stats.overrun != lastOverrun) { + DEBUGPCR("UART overrun: %u", lastOverrun = isoh.stats.overrun); + } +} + void iso_uart_stats_dump(void) { DEBUGPCRF("no_rctx: %u, rctx_sent: %u, rst: %u, pps: %u, bytes: %u, " @@ -675,6 +683,8 @@ void iso_uart_init(void) { DEBUGPCR("USART Initializing"); + memset(&isoh, 0, sizeof(isoh)); + refill_rctx(&isoh); /* make sure we get clock from the power management controller */ diff --git a/firmware/src/simtrace/iso7816_uart.h b/firmware/src/simtrace/iso7816_uart.h index beb0b23..070d92a 100644 --- a/firmware/src/simtrace/iso7816_uart.h +++ b/firmware/src/simtrace/iso7816_uart.h @@ -6,3 +6,4 @@ void iso_uart_rst(unsigned int state); void iso_uart_rx_mode(void); void iso_uart_clk_master(unsigned int master); void iso_uart_init(void); +void iso_uart_report_overrun(void); diff --git a/firmware/src/simtrace/main_simtrace.c b/firmware/src/simtrace/main_simtrace.c index 6f82d96..ca4e646 100644 --- a/firmware/src/simtrace/main_simtrace.c +++ b/firmware/src/simtrace/main_simtrace.c @@ -218,6 +218,8 @@ int _main_dbgu(char key) void _main_func(void) { + static unsigned loopLow = 0, loopHigh = 0; + /* first we try to get rid of pending to-be-sent stuff */ usb_out_process(); @@ -225,4 +227,11 @@ void _main_func(void) usb_in_process(); udp_unthrottle(); + + if ((loopLow & 0xFFFF) == 0) { + DEBUGPCR("Heart beat %08X", loopHigh++); + } + loopLow++; + + iso_uart_report_overrun(); } -- cgit v1.2.3 From e39637e1b816d841e94f2a32f9bf18e0ee89269e Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 20:39:28 +0200 Subject: 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. --- firmware/src/os/pcd_enumerate.c | 53 ++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 25 deletions(-) (limited to 'firmware/src') 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) { -- cgit v1.2.3 From 82cb631bf93e7bca818824d36cfd8213fe7f9836 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 20:41:40 +0200 Subject: watchdog: print watchdog expiration even in non-debug builds --- firmware/src/os/wdt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/os/wdt.c b/firmware/src/os/wdt.c index 99d86a1..d5c19a3 100644 --- a/firmware/src/os/wdt.c +++ b/firmware/src/os/wdt.c @@ -33,9 +33,9 @@ static void wdt_irq(u_int32_t sr) { if (sr & 1) - DEBUGPCRF("================> WATCHDOG EXPIRED !!!!!"); + AT91F_DBGU_Frame("================> WATCHDOG EXPIRED !!!!!\n\r"); if (sr & 2) - DEBUGPCRF("================> WATCHDOG ERROR !!!!!"); + AT91F_DBGU_Frame("================> WATCHDOG ERROR !!!!!\n\r"); } void wdt_restart(void) -- cgit v1.2.3 From 04403a7c8b73a5d30dfcc182159f81ceeed5f6ec Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 20:42:25 +0200 Subject: usbcmd_generic: Add missing \r to DEBUGP() --- firmware/src/os/usbcmd_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'firmware/src') diff --git a/firmware/src/os/usbcmd_generic.c b/firmware/src/os/usbcmd_generic.c index 77b992f..aff13eb 100644 --- a/firmware/src/os/usbcmd_generic.c +++ b/firmware/src/os/usbcmd_generic.c @@ -132,7 +132,7 @@ static int gen_usb_rx(struct req_ctx *rctx) void usbcmd_gen_init(void) { - DEBUGP("Inititalizing usbcmd_gen_init\n"); + DEBUGP("Inititalizing usbcmd_gen_init\n\r"); /* setup FLASH write support for environment storage */ flash_init(); -- cgit v1.2.3 From 9bd1b003a63d61e91db3827c2b66290abc1adf13 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 20:54:09 +0200 Subject: iso7816_uart.h: add missing #ifdef/endif to avoid multiple inclusion --- firmware/src/simtrace/iso7816_uart.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'firmware/src') diff --git a/firmware/src/simtrace/iso7816_uart.h b/firmware/src/simtrace/iso7816_uart.h index 070d92a..1a2b202 100644 --- a/firmware/src/simtrace/iso7816_uart.h +++ b/firmware/src/simtrace/iso7816_uart.h @@ -1,3 +1,5 @@ +#ifndef SIMTRACE_ISO7816_UART_H +#define SIMTRACE_ISO7816_UART_H struct simtrace_stats *iso_uart_stats_get(void); void iso_uart_stats_dump(void); @@ -7,3 +9,5 @@ void iso_uart_rx_mode(void); void iso_uart_clk_master(unsigned int master); void iso_uart_init(void); void iso_uart_report_overrun(void); + +#endif -- cgit v1.2.3 From ab325fc295a23d04f27cc7300b12d2564e4d83e6 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 20:55:01 +0200 Subject: Ensure to transmit current req_ctx on RESET/VCC events When any of the following events occur: * VCC_PHONE off * nRST * RST we tranmsit the current req_ctx, if there is data pending. This ensures that for any successive data, the ATR flag in the next req_ctx containing the data, not in the previous req_ctx. It also ensures that the ATR is aligned at 0 offset in the new req_ctx, which is an assumption the host software makes but the previous code didn't ensure. Furthermore, we introduce a periodic flushing of any pending but incomplete req_ctx. --- firmware/src/simtrace/iso7816_uart.c | 21 +++++++++++++++++++++ firmware/src/simtrace/iso7816_uart.h | 2 ++ firmware/src/simtrace/main_simtrace.c | 3 +++ firmware/src/simtrace/sim_switch.c | 7 +++++-- 4 files changed, 31 insertions(+), 2 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/simtrace/iso7816_uart.c b/firmware/src/simtrace/iso7816_uart.c index 611ccfb..780340f 100644 --- a/firmware/src/simtrace/iso7816_uart.c +++ b/firmware/src/simtrace/iso7816_uart.c @@ -575,6 +575,23 @@ void iso7816_wtime_expired(void) set_state(&isoh, ISO7816_S_WAIT_APDU); } +void iso_uart_flush(void) +{ + send_rctx(&isoh); +} + +void iso_uart_idleflush(void) +{ + static struct req_ctx *last_req = NULL; + static u_int16_t last_len = 0; + + if (last_req == isoh.rctx || last_len == isoh.rctx->tot_len) { + send_rctx(&isoh); + } + last_req = isoh.rctx; + last_len = isoh.rctx->tot_len; +} + static __ramfunc void usart_irq(void) { u_int32_t csr = usart->US_CSR; @@ -618,9 +635,13 @@ static __ramfunc void usart_irq(void) static void reset_pin_irq(u_int32_t pio) { if (!AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, pio)) { + /* make sure to flush pending req_ctx */ + iso_uart_flush(); DEBUGPCR("nRST"); set_state(&isoh, ISO7816_S_RESET); } else { + /* make sure to flush pending req_ctx */ + iso_uart_flush(); DEBUGPCR("RST"); set_state(&isoh, ISO7816_S_WAIT_ATR); isoh.stats.rst++; diff --git a/firmware/src/simtrace/iso7816_uart.h b/firmware/src/simtrace/iso7816_uart.h index 1a2b202..c5790c8 100644 --- a/firmware/src/simtrace/iso7816_uart.h +++ b/firmware/src/simtrace/iso7816_uart.h @@ -9,5 +9,7 @@ void iso_uart_rx_mode(void); void iso_uart_clk_master(unsigned int master); void iso_uart_init(void); void iso_uart_report_overrun(void); +void iso_uart_flush(void); +void iso_uart_idleflush(void); #endif diff --git a/firmware/src/simtrace/main_simtrace.c b/firmware/src/simtrace/main_simtrace.c index ca4e646..6bad290 100644 --- a/firmware/src/simtrace/main_simtrace.c +++ b/firmware/src/simtrace/main_simtrace.c @@ -231,6 +231,9 @@ void _main_func(void) if ((loopLow & 0xFFFF) == 0) { DEBUGPCR("Heart beat %08X", loopHigh++); } + if ((loopLow & 0x3FF) == 0) { + iso_uart_idleflush(); + } loopLow++; iso_uart_report_overrun(); diff --git a/firmware/src/simtrace/sim_switch.c b/firmware/src/simtrace/sim_switch.c index faf48f0..90067ac 100644 --- a/firmware/src/simtrace/sim_switch.c +++ b/firmware/src/simtrace/sim_switch.c @@ -59,9 +59,12 @@ static void sw_sim_irq(u_int32_t pio) static void vcc_phone_irq(u_int32_t pio) { - if (!AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, SIMTRACE_PIO_VCC_PHONE)) + if (!AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, SIMTRACE_PIO_VCC_PHONE)) { DEBUGPCR("VCC_PHONE off"); - else + /* flush any pending req_ctx to make sure the next ATR + * will be aligned to position 0 */ + iso_uart_flush(); + } else DEBUGPCR("VCC_PHONE on"); } -- cgit v1.2.3 From 3cd467a8ef49a2f1d047c24a50832b5c82c405c8 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 21:05:37 +0200 Subject: req_ctx: Keep statistic about number of req_ctx in given state ... and print this from a debug statement. --- firmware/src/os/pcd_enumerate.c | 4 ++-- firmware/src/os/req_ctx.c | 18 ++++++++++++++++-- firmware/src/os/req_ctx.h | 1 + 3 files changed, 19 insertions(+), 4 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/os/pcd_enumerate.c b/firmware/src/os/pcd_enumerate.c index 62f5809..a886377 100644 --- a/firmware/src/os/pcd_enumerate.c +++ b/firmware/src/os/pcd_enumerate.c @@ -180,8 +180,8 @@ int udp_refill_ep(int 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, + DEBUGPCR("USBT(D=%08X, L=%04u, P=$02u) H4/T4: %02X %02X %02X %02X / %02X %02X %02X %02X", + rctx->data, rctx->tot_len, req_ctx_count(epstate[ep].state_pending), 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]); diff --git a/firmware/src/os/req_ctx.c b/firmware/src/os/req_ctx.c index 2411f62..a1495d9 100644 --- a/firmware/src/os/req_ctx.c +++ b/firmware/src/os/req_ctx.c @@ -49,6 +49,7 @@ static struct req_ctx req_ctx[NUM_REQ_CTX]; /* queue of RCTX indexed by their current state */ static struct req_ctx *req_ctx_queues[RCTX_STATE_COUNT], *req_ctx_tails[RCTX_STATE_COUNT]; +static unsigned req_counts[RCTX_STATE_COUNT]; struct req_ctx __ramfunc *req_ctx_find_get(int large, unsigned long old_state, @@ -68,6 +69,7 @@ struct req_ctx __ramfunc *req_ctx_find_get(int large, toReturn->next->prev = NULL; else req_ctx_tails[old_state] = NULL; + req_counts[old_state]--; if ((toReturn->prev = req_ctx_tails[new_state])) toReturn->prev->next = toReturn; else @@ -75,6 +77,7 @@ struct req_ctx __ramfunc *req_ctx_find_get(int large, req_ctx_tails[new_state] = toReturn; toReturn->state = new_state; toReturn->next = NULL; + req_counts[new_state]++; } local_irq_restore(flags); return toReturn; @@ -104,7 +107,7 @@ void req_ctx_set_state(struct req_ctx *ctx, unsigned long new_state) ctx->next->prev = ctx->prev; else req_ctx_tails[old_state] = ctx->prev; - + req_counts[old_state]--; if ((ctx->prev = req_ctx_tails[new_state])) ctx->prev->next = ctx; else @@ -112,6 +115,7 @@ void req_ctx_set_state(struct req_ctx *ctx, unsigned long new_state) req_ctx_tails[new_state] = ctx; ctx->state = new_state; ctx->next = NULL; + req_counts[new_state]++; local_irq_restore(flags); } @@ -158,7 +162,7 @@ void req_ctx_put(struct req_ctx *ctx) ctx->next->prev = ctx->prev; else req_ctx_tails[old_state] = ctx->prev; - + req_counts[old_state]--; if ((ctx->prev = req_ctx_tails[RCTX_STATE_FREE])) ctx->prev->next = ctx; else @@ -166,9 +170,17 @@ void req_ctx_put(struct req_ctx *ctx) req_ctx_tails[RCTX_STATE_FREE] = ctx; ctx->state = RCTX_STATE_FREE; ctx->next = NULL; + req_counts[RCTX_STATE_FREE]++; local_irq_restore(intcFlags); } +unsigned int req_ctx_count(unsigned long state) +{ + if (state >= RCTX_STATE_COUNT) + return 0; + return req_counts[state]; +} + void req_ctx_init(void) { int i; @@ -198,8 +210,10 @@ void req_ctx_init(void) req_ctx_queues[RCTX_STATE_FREE] = req_ctx; req_ctx_tails[RCTX_STATE_FREE] = req_ctx + NUM_REQ_CTX - 1; + req_counts[RCTX_STATE_FREE] = NUM_REQ_CTX; for (i = RCTX_STATE_FREE + 1; i < RCTX_STATE_COUNT; i++) { req_ctx_queues[i] = req_ctx_tails[i] = NULL; + req_counts[i] = 0; } } diff --git a/firmware/src/os/req_ctx.h b/firmware/src/os/req_ctx.h index 1e53fa4..b576ffe 100644 --- a/firmware/src/os/req_ctx.h +++ b/firmware/src/os/req_ctx.h @@ -47,5 +47,6 @@ extern struct req_ctx *req_ctx_find_busy(void); extern void req_ctx_set_state(struct req_ctx *ctx, unsigned long new_state); extern void req_ctx_put(struct req_ctx *ctx); extern u_int8_t req_ctx_num(struct req_ctx *ctx); +unsigned int req_ctx_count(unsigned long state); #endif /* _REQ_CTX_H */ -- cgit v1.2.3 From 4b98ba5d1c13aa620749997f85f24b20acf9ae69 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 21:09:03 +0200 Subject: pcd_enumerate: More complete USB endpoint resetting --- firmware/src/os/pcd_enumerate.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/os/pcd_enumerate.c b/firmware/src/os/pcd_enumerate.c index a886377..403cef9 100644 --- a/firmware/src/os/pcd_enumerate.c +++ b/firmware/src/os/pcd_enumerate.c @@ -114,8 +114,6 @@ static void reset_ep(unsigned int ep) AT91PS_UDP pUDP = upcd.pUdp; struct req_ctx *rctx; - //pUDP->UDP_CSR[ep] = AT91C_UDP_EPEDS; - atomic_set(&upcd.ep[ep].pkts_in_transit, 0); /* free all currently transmitting contexts */ @@ -127,6 +125,7 @@ static void reset_ep(unsigned int ep) pUDP->UDP_RSTEP |= (1 << ep); pUDP->UDP_RSTEP &= ~(1 << ep); + pUDP->UDP_CSR[ep] = AT91C_UDP_EPEDS; upcd.ep[ep].incomplete.rctx = NULL; } @@ -243,19 +242,18 @@ static void udp_irq(void) DEBUGI("ENDBUSRES "); pUDP->UDP_ICR = AT91C_UDP_ENDBUSRES; pUDP->UDP_IER = AT91C_UDP_EPINT0; - /* reset all endpoints */ - pUDP->UDP_RSTEP = (unsigned int)-1; - pUDP->UDP_RSTEP = 0; + reset_ep(0); + reset_ep(1); + reset_ep(2); + reset_ep(3); + /* Enable the function */ pUDP->UDP_FADDR = AT91C_UDP_FEN; + /* Configure endpoint 0 */ pUDP->UDP_CSR[0] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL); upcd.cur_config = 0; upcd.state = USB_STATE_DEFAULT; - - reset_ep(1); - reset_ep(2); - reset_ep(3); #ifdef CONFIG_DFU if (*dfu->dfu_state == DFU_STATE_appDETACH) { -- cgit v1.2.3 From 9662f2830a076a089373f3a4e6ecd93515cd108f Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 21:09:19 +0200 Subject: change number + qty of small/large req_ctx --- firmware/src/os/req_ctx.c | 14 +++++--------- firmware/src/os/req_ctx.h | 5 ++--- 2 files changed, 7 insertions(+), 12 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/os/req_ctx.c b/firmware/src/os/req_ctx.c index a1495d9..722c099 100644 --- a/firmware/src/os/req_ctx.c +++ b/firmware/src/os/req_ctx.c @@ -27,19 +27,15 @@ #include "../openpcd.h" /* FIXME: locking, FIFO order processing */ -#define NUM_RCTX_SMALL 0 -#define NUM_RCTX_LARGE 19 -// 8 * 128 + 4 * 2048 => 18 * 270 + 2 * 2048 -/* #if defined(__AT91SAM7S64__) || defined(RUN_FROM_RAM) -#define NUM_RCTX_SMALL 16 -#define NUM_RCTX_LARGE 1 +#define NUM_RCTX_SMALL 0 +#define NUM_RCTX_LARGE 8 #else -#define NUM_RCTX_SMALL 8 -#define NUM_RCTX_LARGE 4 +#define NUM_RCTX_SMALL 0 +#define NUM_RCTX_LARGE 20 #endif -*/ + #define NUM_REQ_CTX (NUM_RCTX_SMALL+NUM_RCTX_LARGE) static u_int8_t rctx_data[NUM_RCTX_SMALL][RCTX_SIZE_SMALL]; diff --git a/firmware/src/os/req_ctx.h b/firmware/src/os/req_ctx.h index b576ffe..92c21a7 100644 --- a/firmware/src/os/req_ctx.h +++ b/firmware/src/os/req_ctx.h @@ -1,11 +1,10 @@ #ifndef _REQ_CTX_H #define _REQ_CTX_H -#define RCTX_SIZE_LARGE 1000 -#define RCTX_SIZE_SMALL 270 +#define RCTX_SIZE_LARGE 960 +#define RCTX_SIZE_SMALL 320 #define MAX_HDRSIZE sizeof(struct openpcd_hdr) -#define MAX_REQSIZE (64-MAX_HDRSIZE) #define req_buf_payload(x) (x->data[x->hdr_len]) #define req_buf_hdr(x) (x->data[0]) -- cgit v1.2.3 From 7e3c9959e1e140753738be9b6d4142f6c2068e3d Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 21:18:03 +0200 Subject: iso_uart: Print more errors, not just overruns --- firmware/src/simtrace/iso7816_uart.c | 10 ++++++++-- firmware/src/simtrace/iso7816_uart.h | 2 +- firmware/src/simtrace/main_simtrace.c | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/simtrace/iso7816_uart.c b/firmware/src/simtrace/iso7816_uart.c index 780340f..e37deba 100644 --- a/firmware/src/simtrace/iso7816_uart.c +++ b/firmware/src/simtrace/iso7816_uart.c @@ -125,12 +125,18 @@ static const u_int8_t di_table[] = { 12, 20, 2, 4, 8, 16, 32, 64, }; -void iso_uart_report_overrun(void) +void iso_uart_report_errors(void) { - static unsigned lastOverrun = 0; + static unsigned lastOverrun = 0, lastParity = 0, lastFrame = 0; if (isoh.stats.overrun != lastOverrun) { DEBUGPCR("UART overrun: %u", lastOverrun = isoh.stats.overrun); } + if (isoh.stats.frame_err != lastFrame) { + DEBUGPCR("UART frame error: %u", lastFrame = isoh.stats.frame_err); + } + if (isoh.stats.parity_err != lastParity) { + DEBUGPCR("UART parity error: %u", lastParity = isoh.stats.parity_err); + } } void iso_uart_stats_dump(void) diff --git a/firmware/src/simtrace/iso7816_uart.h b/firmware/src/simtrace/iso7816_uart.h index c5790c8..969e6f9 100644 --- a/firmware/src/simtrace/iso7816_uart.h +++ b/firmware/src/simtrace/iso7816_uart.h @@ -2,13 +2,13 @@ #define SIMTRACE_ISO7816_UART_H struct simtrace_stats *iso_uart_stats_get(void); +void iso_uart_report_errors(void); void iso_uart_stats_dump(void); void iso_uart_dump(void); void iso_uart_rst(unsigned int state); void iso_uart_rx_mode(void); void iso_uart_clk_master(unsigned int master); void iso_uart_init(void); -void iso_uart_report_overrun(void); void iso_uart_flush(void); void iso_uart_idleflush(void); diff --git a/firmware/src/simtrace/main_simtrace.c b/firmware/src/simtrace/main_simtrace.c index 6bad290..b3f2665 100644 --- a/firmware/src/simtrace/main_simtrace.c +++ b/firmware/src/simtrace/main_simtrace.c @@ -236,5 +236,5 @@ void _main_func(void) } loopLow++; - iso_uart_report_overrun(); + iso_uart_report_errors(); } -- cgit v1.2.3 From 6bafd0cfb40c2d201f8723a3678dd4f04b380842 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 21:25:54 +0200 Subject: Don't split req_ctx with ATR payload The codnition for flushing a req_ctx on UART idle is fixed to flush only if 1) There has been no new data since last check, and 2) There are no req_ctx currently pending to be transmitted to USB, and 3) We did NOT just got a reset and waiting for ATR I saw an ATR split in two different req_ctx prior, presumably flush decided to send req_ctx after ATR but got a new character just before sent). If we did get a RESET, let original logic decide to send the req_ctx -- presumably when ATR data is fully received. --- firmware/src/simtrace/iso7816_uart.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'firmware/src') diff --git a/firmware/src/simtrace/iso7816_uart.c b/firmware/src/simtrace/iso7816_uart.c index e37deba..9e8ace9 100644 --- a/firmware/src/simtrace/iso7816_uart.c +++ b/firmware/src/simtrace/iso7816_uart.c @@ -591,7 +591,10 @@ void iso_uart_idleflush(void) static struct req_ctx *last_req = NULL; static u_int16_t last_len = 0; - if (last_req == isoh.rctx || last_len == isoh.rctx->tot_len) { + if (last_req == isoh.rctx && + last_len == isoh.rctx->tot_len && + req_ctx_count(RCTX_STATE_UDP_EP2_PENDING) == 0 && + (isoh.sh.flags & SIMTRACE_FLAG_ATR) == 0) { send_rctx(&isoh); } last_req = isoh.rctx; -- cgit v1.2.3 From 8701086cacca088ae34580908cec45f77042b94e Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 21:30:23 +0200 Subject: Retrieve + print previous PC from stack to debug wdog/spurious IRQ Retrive previous PC from the stack before entering specific interrupt handler routines. Allow user to trace where interrupt occured: e.g. WatchDog and Spurious interrupt Prior to this change, spurious interrupt would occur so much (observed via gdb/remote debug) so it appears that the board stalled. Once a custom spurious interrupt installed, the code continues after the interrupt instead of re-entering the interrupt. --- firmware/src/os/system_irq.c | 18 +++++++++++++++--- firmware/src/simtrace/main_simtrace.c | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/os/system_irq.c b/firmware/src/os/system_irq.c index dc787eb..4c1da31 100644 --- a/firmware/src/os/system_irq.c +++ b/firmware/src/os/system_irq.c @@ -25,15 +25,15 @@ #include #include +#include #include "../openpcd.h" static sysirq_hdlr *sysirq_hdlrs[AT91SAM7_SYSIRQ_COUNT]; -static void sys_irq(void) +void sys_irq(u_int32_t previous_pc) { u_int32_t sr; - DEBUGP("sys_irq "); /* Somehow Atmel decided to do really stupid interrupt sharing * for commonly-used interrupts such as the timer irq */ @@ -139,6 +139,10 @@ static void sys_irq(void) if (*AT91C_WDTC_WDMR & AT91C_WDTC_WDFIEN) { sr = *AT91C_WDTC_WDSR; if (sr) { + char dbg_buf[100]; + sprintf(dbg_buf, "sys_irq [Old PC = %08X]\n\r", previous_pc); + AT91F_DBGU_Frame(dbg_buf); + DEBUGP("WDT("); if (sysirq_hdlrs[AT91SAM7_SYSIRQ_WDT]) { DEBUGP("handler "); @@ -154,6 +158,14 @@ static void sys_irq(void) DEBUGPCR("END"); } +static void sysirq_entry(void) +{ + /* DON'T MODIFY THIS SECTION AND Cstartup.S/IRQ_Handler_Entry */ + register unsigned *previous_pc asm("r0"); + asm("ADD R1, SP, #16; LDR R0, [R1]"); + sys_irq(previous_pc); +} + void sysirq_register(enum sysirqs irq, sysirq_hdlr *hdlr) { if (irq >= AT91SAM7_SYSIRQ_COUNT) @@ -167,6 +179,6 @@ void sysirq_init(void) AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_SYS, OPENPCD_IRQ_PRIO_SYS, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, - &sys_irq); + &sysirq_entry); AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_SYS); } diff --git a/firmware/src/simtrace/main_simtrace.c b/firmware/src/simtrace/main_simtrace.c index b3f2665..b84525f 100644 --- a/firmware/src/simtrace/main_simtrace.c +++ b/firmware/src/simtrace/main_simtrace.c @@ -136,8 +136,24 @@ static int simtrace_usb_in(struct req_ctx *rctx) } } +void custom_spurious_handler(unsigned previous_pc) +{ + char dbg_buf[100]; + sprintf(dbg_buf, "SPURRIOUS IRQ [Old PC = %08X]\n\r", previous_pc); + AT91F_DBGU_Frame(dbg_buf); +} + +void custom_spurious_entry(void) +{ + register unsigned *previous_pc asm("r0"); + asm("ADD R1, SP, #16; LDR R0, [R1]"); + custom_spurious_handler(previous_pc); +} + void _init_func(void) { + AT91C_BASE_AIC->AIC_SPU = (int)custom_spurious_entry; + /* low-level hardware initialization */ pio_irq_init(); iso_uart_init(); -- cgit v1.2.3 From 478c2332b74c3ee7dfd8d70873419cf0ef19619e Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 22:04:30 +0200 Subject: Allow some USB interrupts to be handled during dbgu_append --- firmware/src/os/dbgu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'firmware/src') diff --git a/firmware/src/os/dbgu.c b/firmware/src/os/dbgu.c index d8bf8d1..72eaa88 100644 --- a/firmware/src/os/dbgu.c +++ b/firmware/src/os/dbgu.c @@ -55,7 +55,6 @@ #define DEBUG_BUFFER_SIZE (1 << 10) #define DEBUG_BUFFER_MASK (DEBUG_BUFFER_SIZE - 1) -#define USART_SYS_LEVEL 4 /*---------------------------- Global Variable ------------------------------*/ //*---------------------------------------------------------------------------- @@ -370,6 +369,9 @@ void dbgu_rb_append(char *data, int len) avail = (dbgu.out_head - 1 - dbgu.in_tail) & DEBUG_BUFFER_MASK; local_head = (unsigned)len; if (local_head > avail) { + local_irq_restore(intcFlags); + while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0); + local_irq_save(intcFlags); while ((AT91C_BASE_DBGU->DBGU_CSR & AT91C_US_TXBUFE) == 0); dbgu.out_head = dbgu.out_tail; avail = (dbgu.out_head - 1 - dbgu.in_tail) & DEBUG_BUFFER_MASK; -- cgit v1.2.3 From b6caca4b64a73d8d3ab4ce6b51cea71b8f1a54b0 Mon Sep 17 00:00:00 2001 From: Min Xu Date: Sat, 25 Oct 2014 22:08:04 +0200 Subject: improve handling of spurious IRQs rather than printing directly from the interrupt handler, just increment the number and print it from the main loop function instead. --- firmware/src/simtrace/main_simtrace.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) (limited to 'firmware/src') diff --git a/firmware/src/simtrace/main_simtrace.c b/firmware/src/simtrace/main_simtrace.c index b84525f..8069349 100644 --- a/firmware/src/simtrace/main_simtrace.c +++ b/firmware/src/simtrace/main_simtrace.c @@ -134,16 +134,30 @@ static int simtrace_usb_in(struct req_ctx *rctx) req_ctx_set_state(rctx, RCTX_STATE_FREE); break; } + return 0; } -void custom_spurious_handler(unsigned previous_pc) +static volatile unsigned spuirq_pc, spuirq_count = 0; + +static void check_spurious_irq() +{ + static unsigned last_count = 0; + if (last_count != spuirq_count) { + DEBUGPCR("SPURRIOUS IRQ %i [Old PC = %08X]", spuirq_count, spuirq_pc); + last_count = spuirq_count; + } +} + +static void custom_spurious_handler(unsigned previous_pc) { - char dbg_buf[100]; - sprintf(dbg_buf, "SPURRIOUS IRQ [Old PC = %08X]\n\r", previous_pc); - AT91F_DBGU_Frame(dbg_buf); + unsigned flags; + local_irq_save(flags); + spuirq_pc = previous_pc; + spuirq_count++; + local_irq_restore(flags); } -void custom_spurious_entry(void) +static void custom_spurious_entry(void) { register unsigned *previous_pc asm("r0"); asm("ADD R1, SP, #16; LDR R0, [R1]"); @@ -247,10 +261,11 @@ void _main_func(void) if ((loopLow & 0xFFFF) == 0) { DEBUGPCR("Heart beat %08X", loopHigh++); } - if ((loopLow & 0x3FF) == 0) { + if ((loopLow & 0x3F) == 0) { iso_uart_idleflush(); } loopLow++; iso_uart_report_errors(); + check_spurious_irq(); } -- cgit v1.2.3