From ad5b96d584fc9962a9d8504067cd59697fb9a580 Mon Sep 17 00:00:00 2001 From: henryk Date: Fri, 29 Feb 2008 08:53:20 +0000 Subject: New cleaned-up (and then messed up again) SSC code Better layering separation git-svn-id: https://svn.openpcd.org:2342/trunk@434 6dc7ffe9-61d6-0310-9af1-9938baff3ed1 --- openpicc/Makefile | 4 +- openpicc/application/cmd.c | 98 +++-- openpicc/application/iso14443_layer2a.c | 96 ++--- openpicc/application/iso14443_layer2a.h | 5 +- openpicc/application/iso14443_layer3a.h | 88 +--- openpicc/application/iso14443_sniffer.c | 10 +- openpicc/application/iso14443a_miller.c | 2 +- openpicc/application/main.c | 153 ++++--- openpicc/application/openpicc.h | 1 + openpicc/application/ssc.c | 599 ++++++++++++++++++++++++++ openpicc/application/ssc.h | 53 +++ openpicc/application/ssc_buffer.h | 46 ++ openpicc/application/ssc_picc.c | 731 -------------------------------- openpicc/application/ssc_picc.h | 105 ----- openpicc/application/tc_fdt.c | 39 +- openpicc/application/usb_print.c | 2 +- openpicc/os/usb/USB-CDC.c | 8 +- openpicc/os/usb/USB-CDC.h | 1 + 18 files changed, 943 insertions(+), 1098 deletions(-) create mode 100644 openpicc/application/ssc.c create mode 100644 openpicc/application/ssc.h create mode 100644 openpicc/application/ssc_buffer.h delete mode 100644 openpicc/application/ssc_picc.c delete mode 100644 openpicc/application/ssc_picc.h (limited to 'openpicc') diff --git a/openpicc/Makefile b/openpicc/Makefile index 73c1c72..b60ca00 100644 --- a/openpicc/Makefile +++ b/openpicc/Makefile @@ -84,13 +84,12 @@ ARM_SRC= \ application/adc.c \ application/pll.c \ application/pio_irq.c \ - application/ssc_picc.c \ + application/ssc.c \ application/tc_cdiv_sync.c \ application/tc_fdt.c \ application/tc_cdiv.c \ application/usb_print.c \ application/iso14443_layer2a.c \ - application/iso14443_layer3a.c \ application/iso14443_sniffer.c \ application/iso14443a_manchester.c \ application/iso14443a_miller.c \ @@ -108,6 +107,7 @@ ARM_SRC= \ os/core/MemMang/heap_2.c \ os/usb/USB-CDC.c \ os/usb/USBIsr.c +# application/iso14443_layer3a.c # # Define all object files. diff --git a/openpicc/application/cmd.c b/openpicc/application/cmd.c index a96a68d..3b777b3 100644 --- a/openpicc/application/cmd.c +++ b/openpicc/application/cmd.c @@ -18,7 +18,7 @@ #include "tc_cdiv.h" #include "tc_cdiv_sync.h" #include "pio_irq.h" -#include "ssc_picc.h" +#include "ssc.h" #include "usb_print.h" #include "load_modulation.h" @@ -45,7 +45,7 @@ static const portBASE_TYPE USE_COLON_FOR_LONG_COMMANDS = 0; /* When not USE_COLON_FOR_LONG_COMMANDS then short commands will be recognized by including * their character in the string SHORT_COMMANDS * */ -static const char *SHORT_COMMANDS = "!pc+-l?hq9fjkai"; +static const char *SHORT_COMMANDS = "!pc+-l?hq9fjka#i"; /* Note that the long/short command distinction only applies to the USB serial console * */ @@ -132,16 +132,10 @@ int atoiEx(const char * nptr, char * * eptr) return sign * curval; } -static const struct {ssc_metric metric; char *description;} SSC_METRICS[] = { - {OVERFLOWS, "overflows"}, - {BUFFER_ERRORS, "internal buffer management errors"}, - {FREE_BUFFERS, "free rx buffers"}, - {LATE_FRAMES, "late frames"}, -}; -#define DYNAMIC_PIN_PLL_LOCK -1 -static struct { int pin; char * description; } PIO_PINS[] = { - {DYNAMIC_PIN_PLL_LOCK, "pll lock "}, - {OPENPICC_PIO_FRAME, "frame start"}, +#define DYNAMIC_PIN_PLL_LOCK 1 +static struct { int pin; int dynpin; char * description; } PIO_PINS[] = { + {0,DYNAMIC_PIN_PLL_LOCK, "pll lock "}, + {OPENPICC_PIO_FRAME,0, "frame start"}, }; void print_pio(void) { @@ -153,7 +147,7 @@ void print_pio(void) " *****************************************************\n\r" " *\n\r"); for(i=0; iSSC_SR); @@ -294,6 +293,21 @@ void prvExecCommand(u_int32_t cmd, portCHAR *args) { DumpStringToUSB(" * TC0_CV value: "); DumpUIntToUSB(*AT91C_TC0_CV); DumpStringToUSB("\n\r"); + DumpStringToUSB(" * TC2_CV value: "); + DumpUIntToUSB(*AT91C_TC2_CV); + DumpStringToUSB("\n\r"); + DumpStringToUSB(" * TC2_IMR value: "); + DumpUIntToUSB(*AT91C_TC2_IMR); + DumpStringToUSB("\n\r"); + DumpStringToUSB(" * TC2_SR value: "); + DumpUIntToUSB(*AT91C_TC2_SR); + DumpStringToUSB("\n\r"); + DumpStringToUSB(" * TC2_RB value: "); + DumpUIntToUSB(*AT91C_TC2_RB); + DumpStringToUSB("\n\r"); + DumpStringToUSB(" * TC2_RC value: "); + DumpUIntToUSB(*AT91C_TC2_RC); + DumpStringToUSB("\n\r"); DumpStringToUSB(" * SSC_SR value: "); DumpUIntToUSB(*AT91C_SSC_SR); DumpStringToUSB("\n\r"); @@ -312,6 +326,12 @@ void prvExecCommand(u_int32_t cmd, portCHAR *args) { DumpStringToUSB(" * SSC_TCR value: "); DumpUIntToUSB(*AT91C_SSC_TCR); DumpStringToUSB("\n\r"); + DumpStringToUSB(" * SSC_RPR value: "); + DumpUIntToUSB(*AT91C_SSC_RPR); + DumpStringToUSB("\n\r"); + DumpStringToUSB(" * SSC_RCR value: "); + DumpUIntToUSB(*AT91C_SSC_RCR); + DumpStringToUSB("\n\r"); DumpStringToUSB(" * SSC_IMR value: "); DumpUIntToUSB(*AT91C_SSC_IMR); DumpStringToUSB("\n\r"); @@ -346,6 +366,17 @@ void prvExecCommand(u_int32_t cmd, portCHAR *args) { case '!': tc_cdiv_sync_reset(); break; + case '#': + if(!OPENPICC->features.clock_switching) { + DumpStringToUSB("* This hardware is not clock switching capable\n\r"); + break; + } + clock_select = (clock_select+1) % _MAX_CLOCK_SOURCES; + ssc_select_clock(clock_select); + DumpStringToUSB("Active clock is now "); + DumpUIntToUSB(clock_select); + DumpStringToUSB("\n\r"); + break; case 'J': fdt_offset++; DumpStringToUSB("fdt_offset is now "); @@ -375,7 +406,8 @@ void prvExecCommand(u_int32_t cmd, portCHAR *args) { break; #endif case 'Q': - ssc_rx_start(); + //BROKEN new ssc code + //ssc_rx_start(); while(0) { DumpUIntToUSB(AT91C_BASE_SSC->SSC_SR); DumpStringToUSB("\n\r"); @@ -402,11 +434,13 @@ void prvExecCommand(u_int32_t cmd, portCHAR *args) { DumpStringToUSB(OPENPICC->release_name); DumpStringToUSB("\n\r *\n\r" " * test - test critical sections\n\r" + " * thru - test throughput\n\r" #if ( configUSE_TRACE_FACILITY == 1 ) " * t - print task list and stack usage\n\r" #endif " * c - print configuration\n\r" " * +,- - decrease/increase comparator threshold\n\r" + " * # - switch clock\n\r" " * l - cycle LEDs\n\r" " * p - print PIO pins\n\r" " * z 0/1- enable or disable tc_cdiv_sync\n\r" @@ -551,8 +585,9 @@ void startstop_field_meter(void) { portBASE_TYPE vCmdInit() { unsigned int i; for(i=0; iPLL_LOCK; + PIO_PINS[i].dynpin = 0; } } @@ -567,7 +602,6 @@ portBASE_TYPE vCmdInit() { } xSemaphoreTake(xFieldMeterMutex, portMAX_DELAY); - if(xTaskCreate(vCmdCode, (signed portCHAR *)"CMD", TASK_CMD_STACK, NULL, TASK_CMD_PRIORITY, &xCmdTask) != pdPASS) { return 0; diff --git a/openpicc/application/iso14443_layer2a.c b/openpicc/application/iso14443_layer2a.c index b91b67c..57754f8 100644 --- a/openpicc/application/iso14443_layer2a.c +++ b/openpicc/application/iso14443_layer2a.c @@ -38,15 +38,20 @@ #include "openpicc.h" #include "iso14443_layer2a.h" -#include "ssc_picc.h" +#include "ssc.h" #include "pll.h" #include "tc_fdt.h" #include "tc_cdiv.h" #include "tc_cdiv_sync.h" #include "load_modulation.h" +#include "pio_irq.h" + +#include "usb_print.h" + +#define PRINT_DEBUG 0 static u_int8_t fast_receive; -static volatile u_int8_t receiving = 0; +static ssc_handle_t *ssc; #ifdef FOUR_TIMES_OVERSAMPLING #define RX_DIVIDER 32 @@ -56,28 +61,11 @@ static volatile u_int8_t receiving = 0; int iso14443_receive(iso14443_receive_callback_t callback, ssc_dma_rx_buffer_t **buffer, unsigned int timeout) { - int was_receiving = receiving; - (void)callback; + (void)callback; (void)timeout; ssc_dma_rx_buffer_t* _buffer = NULL; int len; - - if(!was_receiving) { - iso14443_l2a_rx_start(); - } else { - /* - * handled by _iso14443_ssc_irq_ext below - tc_fdt_set(0xff00); - tc_cdiv_set_divider(RX_DIVIDER); - tc_cdiv_sync_reset(); - */ - } - - - if(xQueueReceive(ssc_rx_queue, &_buffer, timeout)) { - if(!was_receiving) { - iso14443_l2a_rx_stop(); - } + if(ssc_recv(ssc, &_buffer, timeout) == 0) { if(_buffer == NULL) { /* Can this happen? */ @@ -100,9 +88,7 @@ int iso14443_receive(iso14443_receive_callback_t callback, ssc_dma_rx_buffer_t * return len; } - if(!was_receiving) - iso14443_l2a_rx_stop(); - + if(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_PIO_FRAME)) tc_cdiv_sync_reset(); return -ETIMEDOUT; } @@ -112,23 +98,6 @@ int iso14443_wait_for_carrier(unsigned int timeout) return 0; } -int iso14443_l2a_rx_start(void) -{ - receiving = 1; - tc_fdt_set(0xff00); - tc_cdiv_set_divider(RX_DIVIDER); - ssc_rx_start(); - return 0; -} - -int iso14443_l2a_rx_stop(void) -{ - ssc_rx_stop(); - receiving = 0; - return 0; -} - - u_int8_t iso14443_set_fast_receive(u_int8_t enable_fast_receive) { u_int8_t old_value = fast_receive; @@ -141,32 +110,57 @@ u_int8_t iso14443_get_fast_receive(void) return fast_receive; } -void _iso14443_ssc_irq_ext(u_int32_t ssc_sr, enum ssc_mode ssc_mode, u_int8_t* samples) +static void iso14443_ssc_callback(ssc_callback_reason reason, void *data) { - (void) ssc_mode; (void) samples; - if( (ssc_sr & AT91C_SSC_CP1) && receiving) { + (void) data; + if(reason == CALLBACK_RX_FRAME_BEGIN) { + /* Busy loop for the frame end */ + int *end_asserted = data, i=0; + for(i=0; i<96000; i++) + if(*AT91C_TC2_CV > 2*128) { // FIXME magic number + *end_asserted = 1; + if(PRINT_DEBUG) usb_print_string_f("^", 0); // DEBUG OUTPUT + break; + } + return; + } + if( reason == CALLBACK_RX_FRAME_ENDED || reason == CALLBACK_RX_STARTED ) { tc_fdt_set(0xff00); tc_cdiv_set_divider(RX_DIVIDER); tc_cdiv_sync_reset(); } } +static void iso14443_rx_FRAME_cb(u_int32_t pio) +{ + (void)pio; + if(PRINT_DEBUG) usb_print_string_f("°", 0); // DEBUG OUTPUT + if(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_PIO_FRAME)) + ssc_frame_started(); +} + int iso14443_layer2a_init(u_int8_t enable_fast_receive) { pll_init(); tc_cdiv_init(); tc_fdt_init(); - //ssc_set_irq_extension((ssc_irq_ext_t)iso14443_layer3a_irq_ext); - ssc_rx_init(); - ssc_tx_init(); load_mod_init(); - load_mod_level(3); - - ssc_rx_mode_set(SSC_MODE_14443A); - ssc_set_irq_extension(_iso14443_ssc_irq_ext); iso14443_set_fast_receive(enable_fast_receive); + pio_irq_init_once(); + + if(pio_irq_register(OPENPICC_PIO_FRAME, &iso14443_rx_FRAME_cb) >= 0) { + if(PRINT_DEBUG) usb_print_string("FRAME irq registered\n\r"); // DEBUG OUTPUT + } + + ssc = ssc_open(1, 0, SSC_MODE_14443A, iso14443_ssc_callback); + if(ssc == NULL) + return -EIO; + + // FIXME should be 3 for production, when tx code is implemented + load_mod_level(0); + return 0; } diff --git a/openpicc/application/iso14443_layer2a.h b/openpicc/application/iso14443_layer2a.h index abe019a..cf31ec4 100644 --- a/openpicc/application/iso14443_layer2a.h +++ b/openpicc/application/iso14443_layer2a.h @@ -1,7 +1,7 @@ #ifndef ISO14443_LAYER2A_H_ #define ISO14443_LAYER2A_H_ -#include "ssc_picc.h" +#include "ssc_buffer.h" /* Callback type for iso14443_receive(). * Parameter buffer is being passed a pointer to an SSC Rx buffer structure that this receive happened @@ -55,9 +55,6 @@ extern int iso14443_transmit(ssc_dma_tx_buffer_t *buffer, unsigned int fdt, u_in extern int iso14443_tx_abort(void); extern int iso14443_tx_busy(void); -extern int iso14443_l2a_rx_start(void); -extern int iso14443_l2a_rx_stop(void); - /* * Wait for the presence of a reader to be detected. * Returns 0 when the PLL is locked. diff --git a/openpicc/application/iso14443_layer3a.h b/openpicc/application/iso14443_layer3a.h index 1eb94b4..ff4b1c0 100644 --- a/openpicc/application/iso14443_layer3a.h +++ b/openpicc/application/iso14443_layer3a.h @@ -15,76 +15,11 @@ enum ISO14443_STATES { ERROR, /* Some unrecoverable error has occured */ }; -/******************** RX ************************************/ -#ifdef FOUR_TIMES_OVERSAMPLING -/* definitions for four-times oversampling */ -#define ISO14443A_SAMPLE_LEN 4 -/* Sample values for the REQA and WUPA short frames */ -#define REQA 0x10410441 -#define WUPA 0x04041041 - -/* Start of frame sample for SSC compare 0 */ -#define ISO14443A_SOF_SAMPLE 0x01 -#define ISO14443A_SOF_LEN 4 -/* Length in samples of a short frame */ -#define ISO14443A_SHORT_LEN 32 -/* This is wrong: a short frame is 1 start bit, 7 data bits, 1 stop bit - * followed by no modulation for one bit period. The start bit is 'eaten' in - * SSC Compare 0, but the remaining 7+1+1 bit durations must be sampled and - * compared. At four times oversampling this would be 9*4=36 samples, which is - * more than one SSC transfer. You'd have to use two transfers of 18 samples - * each and modify the comparison code accordingly. - * Since four times oversampling doesn't work reliably anyway (every second - * sample is near an edge and might sample 0 or 1) this doesn't matter for now.*/ -#error Four times oversampling is broken, see comments in code - -#else -/* definitions for two-times oversampling */ -#define ISO14443A_SAMPLE_LEN 2 - -/* For SSC_MODE_ISO14443A_SHORT */ -#define ISO14443A_SHORT_LEN 18 -#define REQA 0x4929 -#define WUPA 0x2249 - -#define ISO14443A_SOF_SAMPLE 0x01 -#define ISO14443A_SOF_LEN 2 - -#define ISO14443A_EOF_SAMPLE 0x00 -#define ISO14443A_EOF_LEN 5 - -/* For SSC_MODE_ISO14443A */ -#define ISO14443A_SHORT_FRAME_COMPARE_LENGTH 2 -#define _ISO14443A_SHORT_FRAME_REQA { 0x29, 0x49 } -#define _ISO14443A_SHORT_FRAME_WUPA { 0x49, 0x22 } -// FIXME not correct. This should be compare_length == 3 (which is 9 at 4 per compare), but this -// needs enhanced ssc irq code to transfer the last read (incomplete) data from the ssc holding -// register to the buffer - -#endif +#include "iso14443.h" extern const u_int8_t ISO14443A_SHORT_FRAME_REQA[ISO14443A_SHORT_FRAME_COMPARE_LENGTH]; extern const u_int8_t ISO14443A_SHORT_FRAME_WUPA[ISO14443A_SHORT_FRAME_COMPARE_LENGTH]; -/* A short frame should be received in one single SSC transfer */ -#if (ISO14443A_SHORT_LEN <= 8) -/* SSC transfer size in bits */ -#define ISO14443A_SHORT_TRANSFER_SIZE 8 -#define ISO14443A_SHORT_TYPE u_int8_t - -#elif (ISO14443A_SHORT_LEN <= 16) -#define ISO14443A_SHORT_TRANSFER_SIZE 16 -#define ISO14443A_SHORT_TYPE u_int16_t - -#elif (ISO14443A_SHORT_LEN <= 32) -#define ISO14443A_SHORT_TRANSFER_SIZE 32 -#define ISO14443A_SHORT_TYPE u_int32_t - -#else -#error ISO14443A_SHORT_LEN defined too big -#endif - -#define ISO14443A_MAX_RX_FRAME_SIZE_IN_BITS (256*9 +2) /******************** TX ************************************/ /* Magic delay, don't know where it comes from */ //#define MAGIC_OFFSET -32 @@ -102,27 +37,6 @@ extern volatile int fdt_offset; #define ISO14443A_FDT_SHORT_1 (ISO14443A_FDT_SLOTLEN*9 + ISO14443A_FDT_OFFSET_1 +fdt_offset +MAGIC_OFFSET -DETECTION_DELAY) #define ISO14443A_FDT_SHORT_0 (ISO14443A_FDT_SLOTLEN*9 + ISO14443A_FDT_OFFSET_0 +fdt_offset +MAGIC_OFFSET -DETECTION_DELAY) -/* in bytes, not counting parity */ -#define MAXIMUM_FRAME_SIZE 256 - -typedef struct { - enum { TYPE_A, TYPE_B } type; - union { - struct { - enum { SHORT_FRAME, STANDARD_FRAME, AC_FRAME } format; - enum { PARITY, /* Calculate parity on the fly, ignore the parity field below */ - GIVEN_PARITY, /* Use the parity bits from the parity field below */ - NO_PARITY, /* Don't send any parity */ - } parity; - enum { ISO14443A_LAST_BIT_0 = 0, ISO14443A_LAST_BIT_1 = 1, ISO14443A_LAST_BIT_NONE } last_bit; - } a; - } parameters; - u_int32_t numbytes; - u_int8_t numbits, bit_offset; - u_int8_t data[MAXIMUM_FRAME_SIZE]; - u_int8_t parity[MAXIMUM_FRAME_SIZE/8+1]; /* parity bit for data[x] is in parity[x/8] & (1<<(x%8)) */ -} iso14443_frame; - extern const iso14443_frame ATQA_FRAME; #endif /*ISO14443_LAYER3A_H_*/ diff --git a/openpicc/application/iso14443_sniffer.c b/openpicc/application/iso14443_sniffer.c index d5cf9c2..bc326b4 100644 --- a/openpicc/application/iso14443_sniffer.c +++ b/openpicc/application/iso14443_sniffer.c @@ -28,17 +28,21 @@ #include #include "openpicc.h" +#include "ssc_buffer.h" +#include "iso14443.h" #include "iso14443_sniffer.h" #include "iso14443_layer2a.h" #include "iso14443a_miller.h" #include "usb_print.h" #include "cmd.h" +#include "led.h" static iso14443_frame rx_frame; void iso14443_sniffer (void *pvParameters) { (void)pvParameters; + (void)rx_frame; int res; /* Delay until USB print etc. are ready */ @@ -57,11 +61,11 @@ void iso14443_sniffer (void *pvParameters) } usb_print_string("Carrier detected.\n\r"); - iso14443_l2a_rx_start(); while(true) { ssc_dma_rx_buffer_t *buffer = 0; res = iso14443_receive(NULL, &buffer, 20000 * portTICK_RATE_MS); if(res >= 0) { +#if 1 DumpStringToUSB("\n\r"); DumpTimeToUSB(xTaskGetTickCount()); usb_print_string(": Frame received, consists of "); @@ -86,6 +90,10 @@ void iso14443_sniffer (void *pvParameters) usb_print_string(" bits: "); DumpBufferToUSB((char*)rx_frame.data, rx_frame.numbytes + (rx_frame.numbits+7)/8 ); usb_print_string("\n\r"); +#else + DumpUIntToUSB(buffer->len_transfers); + DumpStringToUSB("\n\r"); +#endif portENTER_CRITICAL(); buffer->state = FREE; diff --git a/openpicc/application/iso14443a_miller.c b/openpicc/application/iso14443a_miller.c index 27a0977..c902bb7 100644 --- a/openpicc/application/iso14443a_miller.c +++ b/openpicc/application/iso14443a_miller.c @@ -24,7 +24,7 @@ #include "iso14443_layer3a.h" #include "usb_print.h" -#include "ssc_picc.h" +#include "ssc_buffer.h" #include "cmd.h" #if 0 diff --git a/openpicc/application/main.c b/openpicc/application/main.c index e1749de..d9b35f1 100644 --- a/openpicc/application/main.c +++ b/openpicc/application/main.c @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -50,14 +51,89 @@ #include "iso14443_sniffer.h" #include "decoder.h" +static inline int detect_board(void) +{ + /* OpenPICC board detection logic. + * Interesting board differences: PA31 is open on OPENPICC_v0_4 and connected + * to PA18 on OPENPICC_v0_4_p1. PA18 is connected to U7 on both and might read + * differently depending on the state of U7 (primarily depending on U5 and the + * receive circuitry). + * Strategy: Enable Pullups, read PA31 and PA18, if both read low then U7 is + * switched through and this is an v0.4p1. If PA18 reads low and PA31 reads high + * then U7 is switched through and this is an v0.4. If both read high, then U7 is + * not switched through and it might be either board. In this case drive PA31 down + * and see whether PA18 follows down, then it's a v0.4p1 otherwise a v0.4. + */ + int result = -1; + + AT91PS_PIO pio = AT91C_BASE_PIOA; + u_int32_t old_OSR = pio->PIO_OSR, + old_ODSR = pio->PIO_ODSR, + old_PUSR = pio->PIO_PPUSR, + old_PSR = pio->PIO_PSR; + + pio->PIO_ODR = AT91C_PIO_PA18 | AT91C_PIO_PA31; + pio->PIO_PER = AT91C_PIO_PA18 | AT91C_PIO_PA31; + pio->PIO_PPUER = AT91C_PIO_PA18 | AT91C_PIO_PA31; + + unsigned int pa18 = AT91F_PIO_IsInputSet(pio, AT91C_PIO_PA18), + pa31 = AT91F_PIO_IsInputSet(pio, AT91C_PIO_PA31); + if(!pa18 && !pa31) { + //vLedInit(); + //vLedHaltBlinking(1); + result = OPENPICC_v0_4_p2; + } else if(!pa18 && pa31) { + vLedInit(); + vLedHaltBlinking(2); + // Needs to be tested, should be v0.4 + } else if(pa18 && pa31) { + // Can be either board + pio->PIO_OER = AT91C_PIO_PA31; + pio->PIO_CODR = AT91C_PIO_PA31; + pa18 = AT91F_PIO_IsInputSet(pio, AT91C_PIO_PA18); + if(!pa18) { + result = OPENPICC_v0_4_p2; + } else { + vLedInit(); + vLedHaltBlinking(3); + // Needs to be tested, should be v0.4 + } + + // Restore state + if( old_OSR & AT91C_PIO_PA31 ) { + pio->PIO_OER = AT91C_PIO_PA31; + if(old_ODSR & AT91C_PIO_PA31) { + pio->PIO_SODR = AT91C_PIO_PA31; + } else { + pio->PIO_CODR = AT91C_PIO_PA31; + } + } else { + pio->PIO_ODR = AT91C_PIO_PA31; + } + } + + // Restore state + if(old_PSR & AT91C_PIO_PA18) pio->PIO_PER = AT91C_PIO_PA18; else pio->PIO_PDR = AT91C_PIO_PA18; + if(old_PSR & AT91C_PIO_PA31) pio->PIO_PER = AT91C_PIO_PA31; else pio->PIO_PDR = AT91C_PIO_PA31; + + if(old_PUSR & AT91C_PIO_PA18) pio->PIO_PPUDR = AT91C_PIO_PA18; else pio->PIO_PPUER = AT91C_PIO_PA18; + if(old_PUSR & AT91C_PIO_PA31) pio->PIO_PPUDR = AT91C_PIO_PA31; else pio->PIO_PPUER = AT91C_PIO_PA31; + + return result; +} + /**********************************************************************/ static inline void prvSetupHardware (void) { /* The very, very first thing we do is setup the global OPENPICC variable to point to * the correct hardware information. - * FIXME: Detect dynamically in the future */ - OPENPICC = &OPENPICC_HARDWARE[OPENPICC_v0_4_p1]; + int release = detect_board(); + if(release < 0) { + vLedInit(); + vLedHaltBlinking(0); + } + OPENPICC = &OPENPICC_HARDWARE[release]; /* When using the JTAG debugger the hardware is not always initialised to @@ -89,68 +165,8 @@ void vApplicationIdleHook(void) //vLedSetGreen(0); disabled_green = 1; } - usb_print_flush(); } -#if 0 -// Bitrotten -void main_help_print_buffer(ssc_dma_rx_buffer_t *buffer, int *pktcount) -{ - ISO14443A_SHORT_TYPE *tmp = (ISO14443A_SHORT_TYPE*)buffer->data; - int i, dumped = 0; - unsigned int j; - for(i = buffer->len / (sizeof(*tmp)*8); i >= 0 ; i--) { - if( *tmp != 0x00000000 ) { - if(dumped == 0) { - DumpUIntToUSB(buffer->len); - DumpStringToUSB(", "); - DumpUIntToUSB((*pktcount)++); - DumpStringToUSB(": "); - } else { - DumpStringToUSB(" "); - } - dumped = 1; - DumpUIntToUSB(buffer->len / (sizeof(*tmp)*8) - i); - DumpStringToUSB(": "); - for(j=0; j> j) & 0x1) ? '1' : '_' , 0); - } - usb_print_flush(); - //DumpBufferToUSB((char*)(tmp), sizeof(*tmp)); - } - tmp++; - } - if(dumped) DumpStringToUSB("\n\r"); -} - -void vMainTestSSCRXConsumer (void *pvParameters) -{ - static int pktcount=0; - (void)pvParameters; - while(1) { - ssc_dma_rx_buffer_t* buffer; - if(xQueueReceive(ssc_rx_queue, &buffer, portMAX_DELAY)) { - portENTER_CRITICAL(); - buffer->state = PROCESSING; - portEXIT_CRITICAL(); - /*vLedBlinkGreen(); - for(i=0; ilen*8; i++) { - vLedSetGreen( buffer->data[i/8] & (1<<(i%8)) ); - } - vLedBlinkGreen();*/ - //i = usb_print_set_default_flush(0); - - main_help_print_buffer(buffer, &pktcount); - - //usb_print_set_default_flush(i); - portENTER_CRITICAL(); - buffer->state = FREE; - portEXIT_CRITICAL(); - } - } -} -#endif - /* This task pings the watchdog even when the idle task is not running * It should be started with a very high priority and will delay most of the time */ void vMainWatchdogPinger (void *pvParameters) @@ -164,6 +180,15 @@ void vMainWatchdogPinger (void *pvParameters) } } +void usb_print_flusher (void *pvParameters) +{ + (void)pvParameters; + while(1) { + usb_print_flush(); + vTaskDelay(100*portTICK_RATE_MS); + } +} + /**********************************************************************/ int main (void) { @@ -178,8 +203,8 @@ int main (void) da_init(); adc_init(); - /*xTaskCreate (vMainTestSSCRXConsumer, (signed portCHAR *) "SSC_CONSUMER", TASK_USB_STACK, - NULL, TASK_USB_PRIORITY, NULL);*/ + xTaskCreate (usb_print_flusher, (signed portCHAR *) "PRINT-FLUSH", TASK_USB_STACK, + NULL, TASK_USB_PRIORITY, NULL); /*xTaskCreate (iso14443_layer3a_state_machine, (signed portCHAR *) "ISO14443A-3", TASK_ISO_STACK, NULL, TASK_ISO_PRIORITY, NULL);*/ xTaskCreate (iso14443_sniffer, (signed portCHAR *) "ISO14443-SNIFF", TASK_ISO_STACK, diff --git a/openpicc/application/openpicc.h b/openpicc/application/openpicc.h index fc2c4d8..b4d2b7a 100644 --- a/openpicc/application/openpicc.h +++ b/openpicc/application/openpicc.h @@ -38,5 +38,6 @@ typedef int s_int32_t; #define DA_BASELINE 200 +#define DIV_ROUND_UP(a,b) ( (a+(b-1)) / b) #endif/*__OPENPICC_H__*/ diff --git a/openpicc/application/ssc.c b/openpicc/application/ssc.c new file mode 100644 index 0000000..f15b25d --- /dev/null +++ b/openpicc/application/ssc.c @@ -0,0 +1,599 @@ +/* AT91SAM7 SSC controller routines for OpenPICC + * (C) 2006 by Harald Welte + * (C) 2007-2008 Henryk Plötz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * We use SSC for both TX and RX side. + * + * RX side is interconnected with demodulated carrier + * + * TX side is interconnected with load modulation circuitry + */ + +#include +#include +#include +#include + +#include +#include + +#include "ssc.h" +#include "iso14443.h" + +#include "tc_cdiv_sync.h" +#include "tc_fdt.h" +#include "led.h" + +#include "usb_print.h" +#include "cmd.h" + +#define PRINT_DEBUG 0 + +// BROKEN Old FIQ code +int ssc_tx_pending=0, ssc_tx_fiq_fdt_cdiv=0, ssc_tx_fiq_fdt_ssc=0; + +struct _ssc_handle { + enum ssc_mode mode; + const struct openpicc_hardware *openpicc; + ssc_dma_rx_buffer_t* rx_buffer[2]; + ssc_callback_t callback; + xQueueHandle rx_queue; + AT91PS_PDC pdc; + AT91PS_SSC ssc; + u_int8_t rx_enabled, tx_enabled; + u_int8_t rx_running, tx_running; +}; + +static ssc_handle_t _ssc; +static const ssc_mode_def ssc_modes[] = { + /* Undefined, no size set */ + [SSC_MODE_NONE] = {SSC_MODE_NONE, 0, 0, 0}, + /* 14443A Frame */ + [SSC_MODE_14443A] = {SSC_MODE_14443A, + ISO14443_BITS_PER_SSC_TRANSFER * ISO14443A_SAMPLE_LEN, // transfersize_ssc + ISO14443_BITS_PER_SSC_TRANSFER * ISO14443A_SAMPLE_LEN <= 8 ? 8 : 16, // transfersize_pdc + DIV_ROUND_UP(ISO14443A_MAX_RX_FRAME_SIZE_IN_BITS, ISO14443_BITS_PER_SSC_TRANSFER) }, +}; +static struct { + ssc_metric metric; + char *name; + int value; +} ssc_metrics[] = { + [METRIC_RX_OVERFLOWS] = {METRIC_RX_OVERFLOWS, "Rx overflows", 0}, + [METRIC_FREE_RX_BUFFERS] = {METRIC_FREE_RX_BUFFERS, "Free Rx buffers", 0}, + [METRIC_MANAGEMENT_ERRORS] = {METRIC_MANAGEMENT_ERRORS, "Internal buffer management error", 0}, + [METRIC_MANAGEMENT_ERRORS_1] = {METRIC_MANAGEMENT_ERRORS_1, "Internal buffer management error type 1", 0}, + [METRIC_MANAGEMENT_ERRORS_2] = {METRIC_MANAGEMENT_ERRORS_2, "Internal buffer management error type 2", 0}, + [METRIC_MANAGEMENT_ERRORS_3] = {METRIC_MANAGEMENT_ERRORS_3, "Internal buffer management error type 3", 0}, +}; + +static ssc_dma_rx_buffer_t _rx_buffers[SSC_DMA_BUFFER_COUNT]; +ssc_dma_tx_buffer_t _tx_buffer; + +/******* PRIVATE "meat" code *************************************************/ + +#define SSC_RX_IRQ_MASK (AT91C_SSC_RXRDY | \ + AT91C_SSC_OVRUN | \ + AT91C_SSC_ENDRX | \ + AT91C_SSC_RXBUFF | \ + AT91C_SSC_RXSYN | \ + AT91C_SSC_CP0 | \ + AT91C_SSC_CP1) + +#define SSC_TX_IRQ_MASK (AT91C_SSC_TXRDY | \ + AT91C_SSC_TXEMPTY | \ + AT91C_SSC_ENDTX | \ + AT91C_SSC_TXBUFE | \ + AT91C_SSC_TXSYN) + +static __ramfunc ssc_dma_rx_buffer_t* _unload_rx(ssc_handle_t *sh); +static __ramfunc int _reload_rx(ssc_handle_t *sh); + + +static int __ramfunc _ssc_rx_irq(u_int32_t orig_sr, int start_asserted, portBASE_TYPE task_woken) +{ + int end_asserted = 0; + ssc_handle_t *sh = &_ssc; + u_int32_t orig_rcmr = sh->ssc->SSC_RCMR; + +#if PRINT_DEBUG + int old = usb_print_set_default_flush(0); // DEBUG OUTPUT + DumpStringToUSB("orig:"); // DEBUG OUTPUT + DumpUIntToUSB(orig_sr); // DEBUG OUTPUT + DumpStringToUSB("\n\r"); // DEBUG OUTPUT +#endif + u_int32_t sr = orig_sr | _ssc.ssc->SSC_SR; + + if( (sr & AT91C_SSC_RXSYN) || start_asserted) { + sh->ssc->SSC_RCMR = (orig_rcmr & (~AT91C_SSC_START)) | (AT91C_SSC_START_CONTINOUS); + /* Receiving has started */ + if(sh->callback != NULL) { + sh->callback(CALLBACK_RX_FRAME_BEGIN, &end_asserted); + if(end_asserted) + sr = orig_sr | _ssc.ssc->SSC_SR; + } + } + +#if PRINT_DEBUG + DumpStringToUSB("sr:"); // DEBUG OUTPUT + DumpUIntToUSB(sr); // DEBUG OUTPUT + DumpStringToUSB("\n\r"); // DEBUG OUTPUT + usb_print_set_default_flush(old); // DEBUG OUTPUT +#endif + + if(((sr & (AT91C_SSC_CP1 | AT91C_SSC_ENDRX)) || end_asserted) && (sh->rx_buffer[0] != NULL)) { + /* Receiving has ended */ + AT91F_PDC_DisableRx(sh->pdc); + AT91F_SSC_DisableRx(sh->ssc); + sh->ssc->SSC_RCMR = ((sh->ssc->SSC_RCMR) & (~AT91C_SSC_START)) | (AT91C_SSC_START_0); + + ssc_dma_rx_buffer_t *buffer = _unload_rx(sh); + if(buffer != NULL) { + if(sh->callback != NULL) + sh->callback(CALLBACK_RX_FRAME_ENDED, buffer); + + if(buffer->state != FREE) { + task_woken = xQueueSendFromISR(sh->rx_queue, &buffer, task_woken); + } + } + + if(_reload_rx(sh)) { + int dummy = sh->ssc->SSC_RHR; (void)dummy; + AT91F_PDC_EnableRx(sh->pdc); + AT91F_SSC_EnableRx(sh->ssc); + } else { + sh->ssc->SSC_IDR = SSC_RX_IRQ_MASK; + sh->rx_running = 0; + sh->callback(CALLBACK_RX_STOPPED, sh); + } + + } + + sh->ssc->SSC_RCMR = orig_rcmr; + return task_woken; +} + +/* Exported callback for the case when the frame start has been detected externally */ +void ssc_frame_started(void) +{ + _ssc_rx_irq(_ssc.ssc->SSC_SR, 1, pdFALSE); +} + +static void __ramfunc ssc_irq(void) __attribute__ ((naked)); +static void __ramfunc ssc_irq(void) +{ + portENTER_SWITCHING_ISR(); + vLedSetRed(1); + portBASE_TYPE task_woken = pdFALSE; + + u_int32_t sr = _ssc.ssc->SSC_SR; + if(sr & AT91C_SSC_RXSYN) { + task_woken = _ssc_rx_irq(sr, 1, task_woken); + } else if(sr & SSC_RX_IRQ_MASK) { + task_woken = _ssc_rx_irq(sr, 0, task_woken); + } + + AT91F_AIC_ClearIt(AT91C_ID_SSC); + AT91F_AIC_AcknowledgeIt(); + + vLedSetRed(0); + portEXIT_SWITCHING_ISR(task_woken); +} + +static __ramfunc ssc_dma_rx_buffer_t *_get_buffer(ssc_dma_buffer_state_t old, ssc_dma_buffer_state_t new) +{ + ssc_dma_rx_buffer_t *buffer = NULL; + int i; + for(i=0; i < SSC_DMA_BUFFER_COUNT; i++) { + if(_rx_buffers[i].state == old) { + buffer = &_rx_buffers[i]; + buffer->state = new; + break; + } + } + return buffer; +} + +/* Doesn't check sh, must be called with interrupts disabled */ +static __ramfunc int _reload_rx(ssc_handle_t *sh) +{ + int result = 0; + if(sh->rx_buffer[0] != NULL) { + ssc_metrics[METRIC_MANAGEMENT_ERRORS_1].value++; + result = 1; + goto out; + } + + ssc_dma_rx_buffer_t *buffer = _get_buffer(FREE, PENDING); + + if(buffer == NULL) { + ssc_metrics[METRIC_RX_OVERFLOWS].value++; + goto out; + } + + buffer->reception_mode = &ssc_modes[sh->mode]; + buffer->len_transfers = ssc_modes[sh->mode].transfers; + + AT91F_PDC_SetRx(sh->pdc, buffer->data, buffer->len_transfers); + sh->rx_buffer[0] = buffer; + + result = 1; +out: + return result; +} + +// Doesn't check sh, call with interrupts disabled, SSC/PDC stopped +static __ramfunc ssc_dma_rx_buffer_t* _unload_rx(ssc_handle_t *sh) +{ + if(sh->rx_buffer[0] == NULL) { + ssc_metrics[METRIC_MANAGEMENT_ERRORS_2].value++; + return NULL; + } + + ssc_dma_rx_buffer_t *buffer = sh->rx_buffer[0]; + u_int32_t rpr = sh->pdc->PDC_RPR, + rcr = sh->pdc->PDC_RCR; + AT91F_PDC_SetRx(sh->pdc, 0, 0); + sh->rx_buffer[0] = NULL; + buffer->state = FULL; + + if(rcr == 0) { + buffer->flags.overflow = 1; + } else { + buffer->flags.overflow = 0; + } + + if(rcr > 0) { + /* Append a zero to make sure the buffer decoder finds the stop bit */ + switch(buffer->reception_mode->transfersize_pdc) { + case 8: + //*((u_int8_t*)rpr++) = sh->ssc->SSC_RSHR; + *((u_int8_t*)rpr++) = 0; + --rcr; + break; + case 16: + *((u_int16_t*)rpr++) = 0; + --rcr; + break; + case 32: + *((u_int32_t*)rpr++) = 0; + --rcr; + break; + } + } + + if((buffer->len_transfers - rcr) != (rpr - (unsigned int)buffer->data)*(buffer->reception_mode->transfersize_pdc/8)) { + ssc_metrics[METRIC_MANAGEMENT_ERRORS_3].value++; + buffer->state = FREE; + return NULL; + } + + buffer->len_transfers = buffer->len_transfers - rcr; + + return buffer; +} + +void ssc_select_clock(enum ssc_clock_source clock) +{ + if(!OPENPICC->features.clock_switching) return; + switch(clock) { + case CLOCK_SELECT_PLL: + AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC->CLOCK_SWITCH); + break; + case CLOCK_SELECT_CARRIER: + AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC->CLOCK_SWITCH); + break; + default: break; + } +} + +static void _ssc_start_rx(ssc_handle_t *sh) +{ + taskENTER_CRITICAL(); + if(sh != &_ssc) goto out; + if(sh->rx_running) goto out; + if(!sh->rx_enabled) goto out; + + // Load buffer + if(!_reload_rx(sh)) + goto out; + + sh->ssc->SSC_IER = AT91C_SSC_RXSYN | \ + AT91C_SSC_CP1 | AT91C_SSC_ENDRX; + sh->rx_running = 1; + if(sh->callback != NULL) + sh->callback(CALLBACK_RX_STARTED, sh); + + // Actually enable reception + int dummy = sh->ssc->SSC_RHR; (void)dummy; + AT91F_PDC_EnableRx(sh->pdc); + AT91F_SSC_EnableRx(sh->ssc); + +out: + taskEXIT_CRITICAL(); + usb_print_string_f(sh->rx_running ? "SSC now running\n\r":"SSC not running\n\r", 0); +} + +static void _ssc_stop_rx(ssc_handle_t *sh) +{ + taskENTER_CRITICAL(); + sh->ssc->SSC_IDR = SSC_RX_IRQ_MASK; + sh->rx_running = 0; + if(sh->callback != NULL) + sh->callback(CALLBACK_RX_STOPPED, sh); + taskEXIT_CRITICAL(); +} + +/******* PRIVATE Initialization Code *****************************************/ +static void _ssc_rx_mode_set(ssc_handle_t *sh, enum ssc_mode ssc_mode) +{ + taskENTER_CRITICAL(); + int was_running = sh->rx_running; + if(was_running) _ssc_stop_rx(sh); + + u_int8_t data_len=0, num_data=0, sync_len=0; + u_int32_t start_cond=0; + u_int32_t clock_gating=0; + u_int8_t stop = 0, invert=0; + + switch(ssc_mode) { + case SSC_MODE_14443A: + /* Start on Compare 0. The funky calculations down there are designed to allow a different + * (longer) compare length for Compare 1 than for Compare 0. Both lengths are set by the + * same register. */ + start_cond = AT91C_SSC_START_0; + sync_len = ISO14443A_EOF_LEN; + sh->ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE << (ISO14443A_EOF_LEN-ISO14443A_SOF_LEN); + sh->ssc->SSC_RC1R = ISO14443A_EOF_SAMPLE; + + data_len = ssc_modes[SSC_MODE_14443A].transfersize_ssc; + + /* We are using stop on Compare 1. The docs are ambiguous but my interpretation is that + * this means that num_data is basically ignored and reception is continuous until stop + * event. Set num_data to the maximum anyways. */ + num_data = 16; + stop = 1; + + stop = 0; + start_cond = AT91C_SSC_START_CONTINOUS; + sync_len = 0; + + /* We can't use receive clock gating with RF because RF will only go up with the first + * edge, this doesn't cooperate with the longer sync len above. + * FIXME: What's the interaction between clock BURST on v0.4p1, RF and Compare 0? + * In theory this shouldn't work even without SSC clock_gating because BURST gates the + * clock before the SSC and so it shouldn't get sync_len samples before Compare 0. + * I believe there's a bug waiting to happen somewhere here. */ + clock_gating = (0x0 << 6); + //invert = AT91C_SSC_CKI; + break; + case SSC_MODE_NONE: + goto out; + break; + } + + /* Receive frame mode register */ + sh->ssc->SSC_RFMR = ((data_len-1) & 0x1f) | + (((num_data-1) & 0x0f) << 8) | + (((sync_len-1) & 0x0f) << 16); + + /* Receive clock mode register, Clock selection: RK, Clock output: None */ + sh->ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE | + clock_gating | invert | start_cond | (stop << 12); + +out: + sh->mode = ssc_mode; + if(was_running) _ssc_start_rx(sh); + taskEXIT_CRITICAL(); +} + +static inline int _init_ssc_rx(ssc_handle_t *sh) +{ + tc_cdiv_sync_init(); + tc_cdiv_sync_enable(); + + if(sh->rx_queue == NULL) { + sh->rx_queue = xQueueCreate(10, sizeof(sh->rx_buffer[0])); + if(sh->rx_queue == NULL) + goto out_fail_queue; + } + + sh->ssc = AT91C_BASE_SSC; + sh->pdc = (AT91PS_PDC) &(sh->ssc->SSC_RPR); + + AT91F_SSC_CfgPMC(); + + AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, + OPENPICC_SSC_DATA | OPENPICC_SSC_CLOCK | + OPENPICC_PIO_FRAME, + 0); + + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SSC_DATA_CONTROL); + AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SSC_DATA_CONTROL); + + if(OPENPICC->features.data_gating) { + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE); + AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE); + } + + if(OPENPICC->features.clock_switching) { + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC->CLOCK_SWITCH); + ssc_select_clock(CLOCK_SELECT_PLL); + } + + /* Disable all interrupts */ + sh->ssc->SSC_IDR = SSC_RX_IRQ_MASK; + + /* don't divide clock inside SSC, we do that in tc_cdiv */ + sh->ssc->SSC_CMR = 0; + + sh->rx_buffer[0] = sh->rx_buffer[1] = NULL; + + /* Will be set to a real value some time later + * FIXME Layering? */ + tc_fdt_set(0xff00); + + return 1; + +out_fail_queue: + return 0; +} + +static int _ssc_register_callback(ssc_handle_t *sh, ssc_callback_t _callback) +{ + if(!sh) return -EINVAL; + if(sh->callback != NULL) return -EBUSY; + sh->callback = _callback; + if(sh->callback != NULL) + sh->callback(CALLBACK_SETUP, sh); + return 0; +} + +static int _ssc_unregister_callback(ssc_handle_t *sh, ssc_callback_t _callback) +{ + if(!sh) return -EINVAL; + if(_callback == NULL || sh->callback == _callback) { + if(sh->callback != NULL) + sh->callback(CALLBACK_TEARDOWN, sh); + sh->callback = NULL; + } + return 0; +} + +/******* PUBLIC API **********************************************************/ +ssc_handle_t* ssc_open(u_int8_t init_rx, u_int8_t init_tx, enum ssc_mode mode, ssc_callback_t callback) +{ + ssc_handle_t *sh = &_ssc; + unsigned int i; + + if(sh->rx_enabled || sh->tx_enabled || sh->rx_running) { + if( ssc_close(sh) != 0) { + return NULL; + } + } + + for(i=0; itx_enabled = 0; + return NULL; + } + + if(init_rx) { + sh->rx_enabled = _init_ssc_rx(sh); + if(!sh->rx_enabled) { + ssc_close(sh); + return NULL; + } else { + _ssc_rx_mode_set(sh, mode); + } + } + + if(sh->rx_enabled || sh->tx_enabled) { + AT91F_AIC_ConfigureIt(AT91C_ID_SSC, + OPENPICC_IRQ_PRIO_SSC, + AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, (THandler)&ssc_irq); + //AT91C_AIC_SRCTYPE_INT_POSITIVE_EDGE, (THandler)&ssc_irq); + AT91F_AIC_EnableIt(AT91C_ID_SSC); + } + + if(callback != NULL) + _ssc_register_callback(sh, callback); + + if(init_rx) + _ssc_start_rx(sh); + + return sh; +} + +int ssc_recv(ssc_handle_t* sh, ssc_dma_rx_buffer_t* *buffer,unsigned int timeout) +{ + if(sh == NULL) return -EINVAL; + + taskENTER_CRITICAL(); + if(sh->rx_running) { + if(PRINT_DEBUG) usb_print_string_f("Continuing SSC Rx\n\r",0); // DEBUG OUTPUT + } else { + if(PRINT_DEBUG) usb_print_string_f("ERR: SSC halted\n\r",0); // DEBUG OUTPUT + /* Try starting the Reception */ + _ssc_start_rx(sh); + } + taskEXIT_CRITICAL(); + + if(xQueueReceive(sh->rx_queue, buffer, timeout)){ + if(*buffer != NULL) return 0; + else return -EINTR; + } + + return -ETIMEDOUT; +} + +int ssc_close(ssc_handle_t* sh) +{ + if(sh->rx_running) + _ssc_stop_rx(sh); + + if(sh->rx_enabled) { + // FIXME Implement + sh->rx_enabled = 0; + } + if(sh->tx_enabled) { + // FIXME Implement + sh->tx_enabled = 0; + } + + _ssc_unregister_callback(sh, NULL); + return 0; +} + +int ssc_get_metric(ssc_metric metric, char **description, int *value) +{ + char *_name="Undefined"; + int _value=0; + int valid=0; + + if(metric < sizeof(ssc_metrics)/sizeof(ssc_metrics[0])) { + _name = ssc_metrics[metric].name; + _value = ssc_metrics[metric].value; + valid = 1; + } + + switch(metric) { + case METRIC_FREE_RX_BUFFERS: + _value = 0; + int i; + for(i=0; i < SSC_DMA_BUFFER_COUNT; i++) + if(_rx_buffers[i].state == FREE) _value++; + break; + case METRIC_MANAGEMENT_ERRORS: + _value = ssc_metrics[METRIC_MANAGEMENT_ERRORS_1].value + + ssc_metrics[METRIC_MANAGEMENT_ERRORS_2].value + + ssc_metrics[METRIC_MANAGEMENT_ERRORS_3].value; + break; + default: + break; + } + + if(!valid) return 0; + + if(description != NULL) *description = _name; + if(value != NULL) *value = _value; + return 1; +} + diff --git a/openpicc/application/ssc.h b/openpicc/application/ssc.h new file mode 100644 index 0000000..0d96920 --- /dev/null +++ b/openpicc/application/ssc.h @@ -0,0 +1,53 @@ +#ifndef SSC_H_ +#define SSC_H_ + +#include "board.h" +#include "ssc_buffer.h" + +typedef enum { + METRIC_RX_OVERFLOWS, // No free buffer during Rx reload + METRIC_FREE_RX_BUFFERS, // Free Rx buffers, calculated on-the-fly + METRIC_MANAGEMENT_ERRORS, // One of the diverse types of management errors + METRIC_MANAGEMENT_ERRORS_1, // Internal buffer management error type 1 + METRIC_MANAGEMENT_ERRORS_2, // Internal buffer management error type 2 + METRIC_MANAGEMENT_ERRORS_3, // Internal buffer management error type 3 + _MAX_METRICS, +} ssc_metric; + +extern int ssc_get_metric(ssc_metric metric, char **description, int *value); + +typedef enum { + CALLBACK_RX_STARTED, // *data is ssh_handle_t *sh + CALLBACK_RX_STOPPED, // *data is ssh_handle_t *sh + CALLBACK_RX_FRAME_BEGIN, // *data is int *end_asserted + // may set *end_asserted = 1 to force tell the IRQ handler + // that you have detected the end of reception + CALLBACK_RX_FRAME_ENDED, // *data is ssc_dma_rx_buffer *buffer + CALLBACK_SETUP, // *data is ssh_handle_t *sh + CALLBACK_TEARDOWN, // *data is ssh_handle_t *sh +} ssc_callback_reason; +typedef void (*ssc_callback_t)(ssc_callback_reason reason, void *data); + +enum ssc_clock_source { + CLOCK_SELECT_PLL, + CLOCK_SELECT_CARRIER, + _MAX_CLOCK_SOURCES, +}; +struct _ssc_handle; + +typedef struct _ssc_handle ssc_handle_t; + +extern void ssc_select_clock(enum ssc_clock_source clock); + +extern void ssc_frame_started(void); + +/* Rx/Tx initialization separate, since Tx disables PWM output ! */ +extern ssc_handle_t* ssc_open(u_int8_t init_rx, u_int8_t init_tx, enum ssc_mode mode , ssc_callback_t callback); + +extern int ssc_recv(ssc_handle_t* sh, ssc_dma_rx_buffer_t* *buffer, unsigned int timeout); + +extern int ssc_close(ssc_handle_t* sh); + +extern void ssc_rx_stop_frame_ended(void); + +#endif /*SSC_H_*/ diff --git a/openpicc/application/ssc_buffer.h b/openpicc/application/ssc_buffer.h new file mode 100644 index 0000000..fdb977f --- /dev/null +++ b/openpicc/application/ssc_buffer.h @@ -0,0 +1,46 @@ +#ifndef SSC_BUFFER_H_ +#define SSC_BUFFER_H_ + +#define SSC_RX_BUFFER_SIZE_AS_UINT8 2048 +#define SSC_DMA_BUFFER_COUNT 4 + +#if SSC_RX_BUFFER_SIZE_AS_UINT8 < DIV_ROUND_UP((ISO14443A_MAX_RX_FRAME_SIZE_IN_BITS*ISO14443A_SAMPLE_LEN),8) +#undef SSC_RX_BUFFER_SIZE_AS_UINT8 +#define SSC_RX_BUFFER_SIZE_AS_UINT8 DIV_ROUND_UP((ISO14443A_MAX_RX_FRAME_SIZE_IN_BITS*ISO14443A_SAMPLE_LEN),8) +#endif + +typedef enum { + FREE=0, /* Buffer is free */ + PENDING, /* Buffer has been given to the DMA controller and is currently being filled */ + FULL, /* DMA controller signalled that the buffer is full */ + PROCESSING,/* The buffer is currently processed by the consumer (e.g. decoder) */ + PREFILLED, /* The buffer has been prefilled for later usage (only used for TX) */ +} ssc_dma_buffer_state_t; + +enum ssc_mode { + SSC_MODE_NONE, + SSC_MODE_14443A, +}; + +typedef struct { + enum ssc_mode mode; + u_int16_t transfersize_ssc; + u_int16_t transfersize_pdc; + u_int16_t transfers; +} ssc_mode_def; + +typedef struct { + volatile ssc_dma_buffer_state_t state; + u_int32_t len_transfers; /* Length of the content, in transfers */ + struct { + int overflow:1; + } flags; + const ssc_mode_def *reception_mode; /* Pointer to the SSC mode definition that the buffer has been loaded for (affects element size and count) */ + u_int8_t data[SSC_RX_BUFFER_SIZE_AS_UINT8]; +} ssc_dma_rx_buffer_t; + + +typedef struct { +} ssc_dma_tx_buffer_t; + +#endif /*SSC_BUFFER_H_*/ diff --git a/openpicc/application/ssc_picc.c b/openpicc/application/ssc_picc.c deleted file mode 100644 index df6d7fa..0000000 --- a/openpicc/application/ssc_picc.c +++ /dev/null @@ -1,731 +0,0 @@ -/* AT91SAM7 SSC controller routines for OpenPICC - * (C) 2006 by Harald Welte - * (C) 2007 Henryk Plötz - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * We use SSC for both TX and RX side. - * - * RX side is interconnected with demodulated carrier - * - * TX side is interconnected with load modulation circuitry - */ - -//#undef DEBUG - -#include -#include -#include -#include -#include - -#include -#include "queue.h" -#include "task.h" - -#include "dbgu.h" -#include "led.h" -#include "cmd.h" -#include "board.h" -#include "openpicc.h" - -#include "ssc_picc.h" -#include "tc_cdiv_sync.h" -#include "tc_fdt.h" - -#include "pio_irq.h" -#include "usb_print.h" -#include "iso14443_layer3a.h" - -#define DEBUG_SSC_REFILL 1 -#ifdef DEBUG_SSC_REFILL -#define DEBUGR(x, args ...) DEBUGPCRF(x, ## args) -#else -#define DEBUGR(x, args ...) -#endif - -#define DEBUG_LOAD_AND_UNLOAD 0 - -static const AT91PS_SSC ssc = AT91C_BASE_SSC; -static AT91PS_PDC rx_pdc; -static AT91PS_PDC tx_pdc; - -xQueueHandle ssc_rx_queue = NULL; - -struct ssc_state { - ssc_dma_rx_buffer_t* buffer[2]; - enum ssc_mode mode; -}; -static struct ssc_state ssc_state; - -/* Note: Only use 8, 16 or 32 for the transfersize. (These are the sizes used by the PDC and even though - * the SSC supports different sizes, all PDC tranfers will be either 8, 16 or 32, rounding up.) */ -static const ssc_mode_def ssc_sizes[] = { - /* Undefined, no size set */ - [SSC_MODE_NONE] = {SSC_MODE_NONE, 0, 0, 0}, - /* 14443A Short Frame: 1 transfer of ISO14443A_SHORT_LEN bits */ - [SSC_MODE_14443A_SHORT] = {SSC_MODE_14443A_SHORT, ISO14443A_SHORT_LEN, ISO14443A_SHORT_TRANSFER_SIZE, 1}, - /* 14443A Standard Frame: FIXME 16 transfers of 32 bits (maximum number), resulting in 512 samples */ - [SSC_MODE_14443A_STANDARD] = {SSC_MODE_14443A_STANDARD, 32, 32, 16}, - /* 14443A Frame, don't care if standard or short */ - [SSC_MODE_14443A] = {SSC_MODE_14443A, - 4 * ISO14443A_SAMPLE_LEN, // transfersize_ssc - 4 * ISO14443A_SAMPLE_LEN <= 8 ? 8 : 16, // transfersize_pdc - ISO14443A_MAX_RX_FRAME_SIZE_IN_BITS}, - [SSC_MODE_14443B] = {SSC_MODE_14443B, 32, 32, 16}, /* 64 bytes */ - [SSC_MODE_EDGE_ONE_SHOT] = {SSC_MODE_EDGE_ONE_SHOT, 32, 32, 16}, /* 64 bytes */ - [SSC_MODE_CONTINUOUS] = {SSC_MODE_CONTINUOUS, 32, 32, 511}, /* 2044 bytes */ -}; - -/* ************** SSC BUFFER HANDLING *********************** */ -static ssc_dma_rx_buffer_t dma_buffers[SSC_DMA_BUFFER_COUNT]; -ssc_dma_tx_buffer_t ssc_tx_buffer; - -static volatile int overflows; -static volatile int ssc_buffer_errors; -static volatile int late_frames = 0; - -static int ssc_count_free(void) { - int i,free = 0; - for(i=0; istate = newstate; - break; - } - } - return result; -} - - -static int __ramfunc __ssc_rx_load(int secondary); -static ssc_dma_rx_buffer_t* __ramfunc __ssc_rx_unload(int secondary); -/* - * Find and load an RX buffer into the DMA controller, using the current SSC mode - */ -static int __ramfunc __ssc_rx_load(int secondary) -{ - ssc_dma_rx_buffer_t *buffer; - - buffer = ssc_find_dma_buffer(FREE, PENDING); - if (!buffer) { - DEBUGP("no_rctx_for_refill! "); - overflows++; - return -1; - } - DEBUGR("filling SSC RX%u dma ctx: %u (len=%u) ", secondary, - req_ctx_num(buffer), buffer->size); - buffer->len_transfers = ssc_sizes[ssc_state.mode].transfers; - buffer->reception_mode = &ssc_sizes[ssc_state.mode]; - - if(ssc_state.buffer[secondary] != NULL) { - /* This condition is not expected to happen and would probably indicate a bug - * of some sort. However, instead of leaking buffers we'll just pretend it to - * be free again. */ - ssc_buffer_errors++; - usb_print_buffer_f("^", 0, 1, 0); - if(ssc_state.buffer[secondary]->state == PENDING) { - ssc_state.buffer[secondary]->state = FREE; - } - } - - if (secondary) { - AT91F_PDC_SetNextRx(rx_pdc, buffer->data, - ssc_sizes[ssc_state.mode].transfers); - ssc_state.buffer[1] = buffer; - } else { - AT91F_PDC_SetRx(rx_pdc, buffer->data, - ssc_sizes[ssc_state.mode].transfers); - ssc_state.buffer[0] = buffer; - } - - if(DEBUG_LOAD_AND_UNLOAD) { - if(secondary) {int i=usb_print_set_default_flush(0); - DumpStringToUSB("{1:"); - DumpUIntToUSB(rx_pdc->PDC_RNCR); - DumpStringToUSB(" "); - DumpUIntToUSB(rx_pdc->PDC_RNPR); - DumpStringToUSB("} "); - usb_print_set_default_flush(i);} - else {int i=usb_print_set_default_flush(0); - DumpStringToUSB("{0:"); - DumpUIntToUSB(rx_pdc->PDC_RCR); - DumpStringToUSB(" "); - DumpUIntToUSB(rx_pdc->PDC_RPR); - DumpStringToUSB("} "); - usb_print_set_default_flush(i);} - } - - return 0; -} - -/* - * Take the RX buffer(s) from the DMA controller, e.g. to abort a currently executing receive process and - * either reclaim the buffer(s) (if no transfer have been done so far) or mark them as used, updating - * the length fields to match the number of transfers that have actually executed. - * Warning: When this function executes, the mapping in ssc_state is expected to match the mapping in - * the PDC (e.g. ssc_state[0] is the RX Buffer and ssc_state[1] is the Next RX Buffer). Do not use this - * function while the PDC transfer is enabled. Especially do not run it from the SSC RX IRQ. - */ -static int __ramfunc __ssc_tx_unload_all(ssc_dma_rx_buffer_t** primary, ssc_dma_rx_buffer_t** secondary) -{ - if(primary != NULL) *primary = __ssc_rx_unload(0); else __ssc_rx_unload(0); - if(secondary != NULL) *secondary = __ssc_rx_unload(1); else __ssc_rx_unload(1); - return 1; -} -static ssc_dma_rx_buffer_t* __ramfunc __ssc_rx_unload(int secondary) -{ - ssc_dma_rx_buffer_t *buffer = ssc_state.buffer[secondary]; - if(buffer == NULL) return NULL; - - if(DEBUG_LOAD_AND_UNLOAD) { - if(secondary) {int i=usb_print_set_default_flush(0); - DumpStringToUSB("(1:"); - DumpUIntToUSB(rx_pdc->PDC_RNCR); - DumpStringToUSB(" "); - DumpUIntToUSB(rx_pdc->PDC_RNPR); - DumpStringToUSB(") "); - usb_print_set_default_flush(i);} - else {int i=usb_print_set_default_flush(0); - DumpStringToUSB("(0:"); - DumpUIntToUSB(rx_pdc->PDC_RCR); - DumpStringToUSB(" "); - DumpUIntToUSB(rx_pdc->PDC_RPR); - DumpStringToUSB(") "); - usb_print_set_default_flush(i);} - } - - u_int16_t remaining_transfers = (secondary ? rx_pdc->PDC_RNCR : rx_pdc->PDC_RCR); - u_int8_t* next_transfer_location = (u_int8_t*)(secondary ? rx_pdc->PDC_RNPR : rx_pdc->PDC_RPR); - u_int16_t elapsed_transfers = buffer->reception_mode->transfers - remaining_transfers; - - /* BUG BUG BUG For some reason the RNCR is zero, even though there have been no transfers in the secondary - * buffer. For now just assume that secondary==1 && remaining_transfers==0 is a bug condition and actually - * means elapsed_transfers == 0. Of course this will fail should they second buffer really be completely full. */ - if(secondary && remaining_transfers==0) { - remaining_transfers = buffer->reception_mode->transfers; - elapsed_transfers = 0; - } - - u_int32_t elapsed_size = buffer->reception_mode->transfersize_pdc/8 * elapsed_transfers; - - /* Consistency check */ - if( next_transfer_location - elapsed_size != buffer->data ) { - int i=usb_print_set_default_flush(0); - DumpStringToUSB("!!!"); DumpUIntToUSB(secondary); DumpStringToUSB(" "); - DumpUIntToUSB((int)next_transfer_location); DumpStringToUSB(" "); - DumpUIntToUSB(elapsed_size); DumpStringToUSB(" "); DumpUIntToUSB((int)buffer->data); DumpStringToUSB(" "); - usb_print_set_default_flush(i); - ssc_buffer_errors++; - usb_print_buffer_f("°", 0, 1, 0); - if(buffer->state == PENDING) buffer->state = FREE; - ssc_state.buffer[secondary] = NULL; - return NULL; - } - - if(secondary) { - AT91F_PDC_SetNextRx(rx_pdc, 0, 0); - } else { - AT91F_PDC_SetRx(rx_pdc, 0, 0); - } - if(buffer->state == PENDING || buffer->state==FULL) { - buffer->len_transfers = elapsed_transfers; - if(DEBUG_LOAD_AND_UNLOAD) - {int i=usb_print_set_default_flush(0); - DumpStringToUSB("<"); - DumpUIntToUSB((unsigned int)buffer); - DumpStringToUSB(": "); - DumpUIntToUSB(elapsed_transfers); - DumpStringToUSB("> "); - usb_print_set_default_flush(i);} - if(elapsed_transfers > 0) { - buffer->state = FULL; - } else { - buffer->state = FREE; - } - } - ssc_state.buffer[secondary] = NULL; - - return buffer; -} - -#define SSC_RX_IRQ_MASK (AT91C_SSC_RXRDY | \ - AT91C_SSC_OVRUN | \ - AT91C_SSC_ENDRX | \ - AT91C_SSC_RXBUFF | \ - AT91C_SSC_RXSYN | \ - AT91C_SSC_CP0 | \ - AT91C_SSC_CP1) - -#define SSC_TX_IRQ_MASK (AT91C_SSC_TXRDY | \ - AT91C_SSC_TXEMPTY | \ - AT91C_SSC_ENDTX | \ - AT91C_SSC_TXBUFE | \ - AT91C_SSC_TXSYN) - -void ssc_rx_mode_set(enum ssc_mode ssc_mode) -{ - u_int8_t data_len=0, num_data=0, sync_len=0; - u_int32_t start_cond=0; - u_int32_t clock_gating=0; - u_int8_t stop = 0; - - /* disable Rx and all Rx interrupt sources */ - AT91F_SSC_DisableRx(AT91C_BASE_SSC); - AT91F_SSC_DisableIt(ssc, SSC_RX_IRQ_MASK); - - switch (ssc_mode) { - case SSC_MODE_14443A_SHORT: - start_cond = AT91C_SSC_START_0; - sync_len = ISO14443A_SOF_LEN; - ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE; - data_len = ISO14443A_SHORT_LEN; - num_data = 2; - clock_gating = (0x2 << 6); - break; - case SSC_MODE_14443A_STANDARD: - start_cond = AT91C_SSC_START_0; - sync_len = ISO14443A_SOF_LEN; - ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE; - data_len = 32; - num_data = 16; /* FIXME */ - clock_gating = (0x2 << 6); - break; - case SSC_MODE_14443A: - start_cond = AT91C_SSC_START_0; - sync_len = ISO14443A_EOF_LEN; - ssc->SSC_RC0R = ISO14443A_SOF_SAMPLE << (ISO14443A_EOF_LEN-ISO14443A_SOF_LEN); - ssc->SSC_RC1R = ISO14443A_EOF_SAMPLE; - data_len = ssc_sizes[SSC_MODE_14443A].transfersize_ssc; - num_data = 16; /* Start with 16, then switch to continuous in the IRQ handler */ - stop = 1; /* Actually the documentation indicates that setting STOP makes switching to continuous unnecessary */ - clock_gating = (0x0 << 6); - break; - case SSC_MODE_14443B: - /* start sampling at first falling data edge */ - //start_cond = - break; - case SSC_MODE_EDGE_ONE_SHOT: - case SSC_MODE_CONTINUOUS: - /* unfortunately we don't have RD and RF interconnected - * (at least not yet in the current hardware) */ - //start_cond = AT91C_SSC_START_EDGE_RF; - start_cond = AT91C_SSC_START_CONTINOUS; - //AT91C_SSC_START_RISE_RF; - sync_len = 0; - data_len = 32; - num_data = 16; - clock_gating = (0x2 << 6); - break; - case SSC_MODE_NONE: - goto out_set_mode; - break; - } - //ssc->SSC_RFMR = AT91C_SSC_MSBF | (data_len-1) & 0x1f | - ssc->SSC_RFMR = ((data_len-1) & 0x1f) | - (((num_data-1) & 0x0f) << 8) | - (((sync_len-1) & 0x0f) << 16) - //| AT91C_SSC_MSBF - ; - ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE | - clock_gating | (0&AT91C_SSC_CKI) | start_cond | (stop << 12); - - /* Enable Rx DMA */ - AT91F_PDC_EnableRx(rx_pdc); - - /* Enable RX interrupts */ -/* - AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN | AT91C_SSC_CP0 | - AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF);*/ -out_set_mode: - ssc_state.mode = ssc_mode; -} - -/* For some reason AT91C_SSC_START_RISE_RF (or AT91C_SSC_START_HIGH_RF or ...) doesn't - * work as a start condition. Instead we'll configure TF as a PIO input pin, enable - * a PIO change interrupt, have Fast Forcing enabled for the PIO change interrupt and - * then activate the SSC TX in the FIQ handler on rising TF. ssc_tx_pending is queried - * by the fiq handler to see whether to start the transmitter. */ -//#define USE_SSC_TX_TF_WORKAROUND -volatile u_int32_t ssc_tx_pending = 0; - -/* This is the time that the TF FIQ should spin until before SWTRG'ing the tc_cdiv. - * See fdt_timing.dia. Note: This means transmission is broken without USE_SSC_TX_TF_WORKAROUND */ -volatile u_int32_t ssc_tx_fiq_fdt_cdiv = 0; -/* This is the time that the TF FIQ should spin until before starting the SSC - * There must be enough time between these two! */ -volatile u_int32_t ssc_tx_fiq_fdt_ssc = 0; -#ifndef USE_SSC_TX_TF_WORKAROUND -//#error Transmission is broken without USE_SSC_TX_TF_WORKAROUND, see comments in code -#endif - -void ssc_tf_irq(u_int32_t pio); -void ssc_tx_start(ssc_dma_tx_buffer_t *buf) -{ - u_int8_t data_len, num_data, sync_len; - u_int32_t start_cond; - - /* disable Tx */ - AT91F_PDC_DisableTx(tx_pdc); - AT91F_SSC_DisableTx(AT91C_BASE_SSC); - - /* disable all Tx related interrupt sources */ - AT91F_SSC_DisableIt(ssc, SSC_TX_IRQ_MASK); - -#ifdef USE_SSC_TX_TF_WORKAROUND - start_cond = AT91C_SSC_START_CONTINOUS; -#else - start_cond = AT91C_SSC_START_HIGH_RF; -#endif - sync_len = 1; - data_len = 32; - num_data = buf->len/(data_len/8); /* FIXME This is broken for more than 64 bytes */ - - ssc->SSC_TFMR = ((data_len-1) & 0x1f) | - (((num_data-1) & 0x0f) << 8) | - (((sync_len-1) & 0x0f) << 16); - ssc->SSC_TCMR = 0x01 | AT91C_SSC_CKO_NONE | AT91C_SSC_CKI | start_cond; - - AT91F_PDC_SetTx(tx_pdc, buf->data, num_data); - AT91F_PDC_SetNextTx(tx_pdc, 0, 0); - buf->state = PENDING; - - AT91F_SSC_EnableIt(ssc, AT91C_SSC_ENDTX); - /* Enable DMA */ - AT91F_PDC_EnableTx(tx_pdc); - //AT91F_PDC_SetTx(tx_pdc, buf->data, num_data); -#ifdef OPENPICC_USE_SSC_DATA_GATING - ssc_set_data_gate(0); -#endif - /* Start Transmission */ -#ifndef USE_SSC_TX_TF_WORKAROUND - AT91F_SSC_EnableTx(AT91C_BASE_SSC); -#else - ssc_tx_pending = 1; - pio_irq_enable(OPENPICC_SSC_TF); - if(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_SSC_TF)) { - /* TF was probably already high when we enabled the PIO change interrupt for it. */ - ssc_tf_irq(OPENPICC_SSC_TF); - vLedBlinkRed(); - late_frames++; - usb_print_string_f("Late response\n\r", 0); - } -#endif -} - -#ifdef USE_SSC_TX_TF_WORKAROUND -void ssc_tf_irq(u_int32_t pio) { - (void)pio; - if(!AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_SSC_TF)) return; - pio_irq_disable(OPENPICC_SSC_TF); - if(ssc_tx_pending) { /* Transmit has not yet been started by the FIQ */ - AT91F_SSC_EnableTx(AT91C_BASE_SSC); - ssc_tx_pending = 0; - } -} -#endif - - -static ssc_irq_ext_t irq_extension = NULL; -ssc_irq_ext_t ssc_set_irq_extension(ssc_irq_ext_t ext_handler) { - ssc_irq_ext_t old = irq_extension; - irq_extension = ext_handler; - return old; -} - -void __ramfunc ssc_rx_stop_frame_ended(void) -{ -} - -static void __ramfunc ssc_irq(void) __attribute__ ((naked)); -static void __ramfunc ssc_irq(void) -{ - portENTER_SWITCHING_ISR(); - vLedSetRed(1); - portBASE_TYPE task_woken = pdFALSE; - - u_int32_t ssc_sr = ssc->SSC_SR; - u_int32_t orig_ssc_sr = ssc_sr; - ssc_dma_rx_buffer_t *inbuf=NULL; - DEBUGP("ssc_sr=0x%08x, mode=%u: ", ssc_sr, ssc_state.mode); - - if ((ssc_sr & AT91C_SSC_CP0) && (ssc_state.mode == SSC_MODE_14443A_SHORT || ssc_state.mode == SSC_MODE_14443A)) { - /* Short frame, busy loop till the frame is received completely to - * prevent a second irq entrance delay when the actual frame end - * irq is raised. (The scheduler masks interrupts for about 56us, - * which is too much for anticollision.) */ - int i = 0; - //vLedBlinkRed(); - while( ! ((ssc_sr=ssc->SSC_SR) & (AT91C_SSC_ENDRX | AT91C_SSC_CP1)) ) { - i++; - if(i > 9600) break; /* Break out, clearly this is not a short frame or a reception error happened */ - } - ssc_sr |= orig_ssc_sr; - vLedSetRed(1); - } - - if(orig_ssc_sr & AT91C_SSC_CP1) {usb_print_string_f("oCP1 ", 0); vLedBlinkRed();} - if(ssc_sr & AT91C_SSC_CP1) usb_print_string_f("CP1 ", 0); - - if (ssc_sr & (AT91C_SSC_ENDRX | AT91C_SSC_CP1)) { - inbuf = ssc_state.buffer[0]; - if(ssc_sr & AT91C_SSC_CP1 && !(ssc_sr & AT91C_SSC_ENDRX)) { - /* Manually unload buffer 0, since only the SSC has stopped, not the PDC */ - __ssc_rx_unload(0); - __ssc_rx_load(0); - } - - inbuf->state = FULL; - task_woken = xQueueSendFromISR(ssc_rx_queue, &inbuf, task_woken); - vLedSetGreen(1); - - if(ssc_sr & AT91C_SSC_ENDRX) { - /* second buffer got propagated to primary */ - ssc_state.buffer[0] = ssc_state.buffer[1]; - ssc_state.buffer[1] = NULL; - } - - if(ssc_state.mode == SSC_MODE_EDGE_ONE_SHOT || ssc_state.mode == SSC_MODE_14443A_SHORT - || ssc_state.mode == SSC_MODE_14443A_STANDARD) { - // Stop sampling here - ssc_rx_stop(); - } else { - if (ssc_sr & AT91C_SSC_RXBUFF) { - // FIXME - DEBUGP("RXBUFF! "); - if (ssc_state.buffer[0]) { - //DEBUGP("Sending secondary RCTX(%u, len=%u) ", req_ctx_num(ssc_state.rx_ctx[0]), ssc_state.rx_ctx[0]->tot_len); - //gaportENTER_CRITICAL(); - ssc_state.buffer[0]->state = FULL; - //gaportEXIT_CRITICAL(); - task_woken = xQueueSendFromISR(ssc_rx_queue, &ssc_state.buffer[0], task_woken); - } - if(ssc_sr & AT91C_SSC_RXENA) if (__ssc_rx_load(0) == -1) - AT91F_SSC_DisableIt(ssc, AT91C_SSC_ENDRX | - AT91C_SSC_RXBUFF | - AT91C_SSC_OVRUN); - } - - if(ssc_sr & AT91C_SSC_RXENA && ssc_state.buffer[1] == NULL) if (__ssc_rx_load(1) == -1) - AT91F_SSC_DisableIt(ssc, AT91C_SSC_ENDRX | - AT91C_SSC_RXBUFF | - AT91C_SSC_OVRUN); - } - } - - if (ssc_sr & AT91C_SSC_OVRUN) - DEBUGP("RX_OVERRUN "); - - if (ssc_sr & AT91C_SSC_CP0) - DEBUGP("CP0 "); - - if (ssc_sr & AT91C_SSC_TXSYN) - DEBUGP("TXSYN "); - - if(ssc_sr & AT91C_SSC_ENDTX) { -#ifdef OPENPICC_USE_SSC_DATA_GATING - ssc_set_data_gate(1); -#endif - AT91F_SSC_EnableTx(AT91C_BASE_SSC); - //usb_print_string_f("ENDTX ", 0); - if(ssc_tx_buffer.state == PENDING) { - ssc_tx_buffer.state = FREE; - AT91F_SSC_DisableIt(ssc, SSC_TX_IRQ_MASK); - } - } - - if(ssc_sr & AT91C_SSC_TXBUFE) - DEBUGP("TXBUFE "); - - if(irq_extension != NULL) - irq_extension(ssc_sr, ssc_state.mode, inbuf?inbuf->data:NULL); - - - DEBUGPCR("I"); - AT91F_AIC_ClearIt(AT91C_ID_SSC); - AT91F_AIC_AcknowledgeIt(); - - vLedSetRed(0); - portEXIT_SWITCHING_ISR(task_woken); -} - -void ssc_print(void) -{ - DEBUGP("PDC_RPR=0x%08x ", rx_pdc->PDC_RPR); - DEBUGP("PDC_RCR=0x%08x ", rx_pdc->PDC_RCR); - DEBUGP("PDC_RNPR=0x%08x ", rx_pdc->PDC_RNPR); - DEBUGP("PDC_RNCR=0x%08x ", rx_pdc->PDC_RNCR); -} - - -void ssc_rx_unthrottle(void) -{ - AT91F_SSC_EnableIt(ssc, AT91C_SSC_ENDRX | AT91C_SSC_CP0 | - AT91C_SSC_RXBUFF | AT91C_SSC_OVRUN); -} - -void ssc_rx_start(void) -{ - //DEBUGPCRF("starting SSC RX\n"); - - __ssc_rx_load(0); - if(ssc_state.mode != SSC_MODE_14443A_SHORT) __ssc_rx_load(1); - - /* Enable Reception */ - AT91F_SSC_EnableIt(ssc, AT91C_SSC_ENDRX | AT91C_SSC_CP0 | AT91C_SSC_CP1 | - AT91C_SSC_RXBUFF | AT91C_SSC_OVRUN); - AT91F_PDC_EnableRx(rx_pdc); - AT91F_SSC_EnableRx(AT91C_BASE_SSC); - - /* Clear the flipflop */ - tc_cdiv_sync_reset(); -} - -void ssc_rx_stop(void) -{ - /* Disable reception */ - AT91F_SSC_DisableRx(AT91C_BASE_SSC); - AT91F_PDC_DisableRx(rx_pdc); - AT91F_SSC_DisableIt(ssc, SSC_RX_IRQ_MASK); - __ssc_tx_unload_all(NULL, NULL); -} - -void ssc_tx_init(void) -{ - /* IMPORTANT: Disable PA23 (PWM0) output, since it is connected to - * PA17 !! */ - AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPICC_MOD_PWM); - AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, OPENPICC_MOD_SSC | - OPENPICC_SSC_CLOCK | OPENPICC_SSC_TF, 0); -#ifdef USE_SSC_TX_TF_WORKAROUND - AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPICC_SSC_TF); - pio_irq_register(OPENPICC_SSC_TF, ssc_tf_irq); -#endif - - tx_pdc = (AT91PS_PDC) &(ssc->SSC_RPR); -} - -void ssc_set_data_gate(int enable) -{ - if(! OPENPICC->features.clock_gating) return; - if(enable) - AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE); - else - AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE); -} - -void ssc_rx_init() -{ - tc_cdiv_sync_init(); - tc_cdiv_sync_enable(); - - if(ssc_rx_queue == NULL) - ssc_rx_queue = xQueueCreate(10, sizeof(ssc_state.buffer[0])); - - rx_pdc = (AT91PS_PDC) &(ssc->SSC_RPR); - - AT91F_SSC_CfgPMC(); - - AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, - OPENPICC_SSC_DATA | OPENPICC_SSC_CLOCK | - OPENPICC_PIO_FRAME, - 0); - - AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SSC_DATA_CONTROL); - AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SSC_DATA_CONTROL); - - if(OPENPICC->features.clock_gating) { - AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE); - AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE); - } - - AT91F_AIC_ConfigureIt(AT91C_ID_SSC, - OPENPICC_IRQ_PRIO_SSC, - AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, (THandler)&ssc_irq); - - /* don't divide clock inside SSC, we do that in tc_cdiv */ - ssc->SSC_CMR = 0; - -#if 0 - ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE | - AT91C_SSC_CKI | AT91C_SSC_START_CONTINOUS; - /* Data bits per Data N = 32-1 - * Data words per Frame = 15-1 (=60 byte)*/ - ssc->SSC_RFMR = 31 | AT91C_SSC_MSBF | (14 << 8); -#endif - - ssc_rx_mode_set(SSC_MODE_NONE); - ssc_state.buffer[0] = ssc_state.buffer[1] = NULL; - -#if 0 - AT91F_PDC_EnableRx(rx_pdc); - - /* Enable RX interrupts */ - AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN | - AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF); -#endif - /* Will be set to a real value some time later */ - tc_fdt_set(0xff00); - - AT91F_AIC_EnableIt(AT91C_ID_SSC); - - //usb_hdlr_register(&ssc_usb_in, OPENPCD_CMD_CLS_SSC); - - DEBUGP("\r\n"); -} - -void ssc_fini(void) -{ -// usb_hdlr_unregister(OPENPCD_CMD_CLS_SSC); - AT91F_PDC_DisableRx(rx_pdc); - AT91F_PDC_DisableTx(tx_pdc); - AT91F_SSC_DisableTx(ssc); - AT91F_SSC_DisableRx(ssc); - AT91F_SSC_DisableIt(ssc, 0xfff); - AT91F_PMC_DisablePeriphClock(AT91C_BASE_PMC, - ((unsigned int) 1 << AT91C_ID_SSC)); -} diff --git a/openpicc/application/ssc_picc.h b/openpicc/application/ssc_picc.h deleted file mode 100644 index 3b6182a..0000000 --- a/openpicc/application/ssc_picc.h +++ /dev/null @@ -1,105 +0,0 @@ -#ifndef _SSC_H -#define _SSC_H - -#include "queue.h" -#include "iso14443_layer3a.h" - -extern void ssc_rx_start(void); -extern void ssc_rx_stop(void); - -/* Rx/Tx initialization separate, since Tx disables PWM output ! */ -extern void ssc_tx_init(void); -extern void ssc_rx_init(void); - -extern void ssc_fini(void); -extern void ssc_rx_stop(void); -extern void ssc_rx_unthrottle(void); -extern void __ramfunc ssc_rx_stop_frame_ended(void); - - -enum ssc_mode { - SSC_MODE_NONE, - SSC_MODE_14443A_SHORT, - SSC_MODE_14443A_STANDARD, - SSC_MODE_14443A, - SSC_MODE_14443B, - SSC_MODE_EDGE_ONE_SHOT, - SSC_MODE_CONTINUOUS, -}; - -extern void ssc_rx_mode_set(enum ssc_mode ssc_mode); - -typedef void (*ssc_irq_ext_t)(u_int32_t ssc_sr, enum ssc_mode ssc_mode, u_int8_t* samples); - -/* A fast method to extend the IRQ handler from the higher level code, e.g. to prepare - * an ATQA answer to REQA or WUPA in iso14443_layer3a. Normally I'd use the FreeRTOS - * primitives to wake the task and then do everything in task context, but the delay - * from SSC IRQ to the task returning from xQueueReceive is around 165us. Additionally to the - * delay from end of communication to SSC IRQ which is around 50us. This results in way more delay - * than acceptable for the synchronous responses (around 87us).*/ -extern ssc_irq_ext_t ssc_set_irq_extension(ssc_irq_ext_t ext_handler); - -/* These are various SSC performance metrics that can be queried */ -typedef enum { - OVERFLOWS, /* Overflows (e.g. no free buffer when reloading DMA controller) */ - BUFFER_ERRORS, /* Internal buffer management errors */ - FREE_BUFFERS, /* Free RX buffers */ - LATE_FRAMES, /* Frames that were not ready to be sent when the FDT passed; e.g. sent too late */ - SSC_ERRORS, /* General error count, e.g. OVERFLOWS + BUFFER_ERRORS */ -} ssc_metric; -extern int ssc_get_metric(ssc_metric metric); - -#define SSC_RX_BUFFER_SIZE 2048 -#define SSC_DMA_BUFFER_COUNT 4 - -#if SSC_RX_BUFFER_SIZE < ISO14443A_MAX_RX_FRAME_SIZE_IN_BITS -#undef SSC_RX_BUFFER_SIZE -#define SSC_RX_BUFFER_SIZE ISO14443A_MAX_RX_FRAME_SIZE_IN_BITS -#endif - -typedef enum { - FREE=0, /* Buffer is free */ - PENDING, /* Buffer has been given to the DMA controller and is currently being filled */ - FULL, /* DMA controller signalled that the buffer is full */ - PROCESSING,/* The buffer is currently processed by the consumer (e.g. decoder) */ - PREFILLED, /* The buffer has been prefilled for later usage (only used for TX) */ -} ssc_dma_buffer_state_t; - -typedef struct { - enum ssc_mode mode; - u_int16_t transfersize_ssc; - u_int16_t transfersize_pdc; - u_int16_t transfers; -} ssc_mode_def; - -typedef struct { - volatile ssc_dma_buffer_state_t state; - u_int32_t len_transfers; /* Length of the content, in transfers */ - const ssc_mode_def *reception_mode; /* Pointer to the SSC mode definition that the buffer has been loaded for (affects element size and count) */ - u_int8_t data[SSC_RX_BUFFER_SIZE]; -} ssc_dma_rx_buffer_t; - -extern xQueueHandle ssc_rx_queue; - -/* in bytes, used for the sample buffer that holds the subcarrier modulation data at fc/8 = 1695 MHz */ -#define SSC_TX_BUFFER_SIZE ((MAXIMUM_FRAME_SIZE*( (8+1)*2 ) ) + 2 + 2) - -typedef struct { - volatile ssc_dma_buffer_state_t state; - u_int32_t len; /* Length of the content */ - void *source; /* Source pointer for a prefilled buffer; set to NULL if not used */ - u_int8_t data[SSC_TX_BUFFER_SIZE]; -} ssc_dma_tx_buffer_t; - -/* Declare one TX buffer. This means that only one buffer can ever be pending for sending. That's because - * this buffer must be huge (one frame of 256 bytes of subcarrier modulation data at 1695 MHz sample - * rate is approx 4k bytes). */ -extern ssc_dma_tx_buffer_t ssc_tx_buffer; - -extern void ssc_tx_start(ssc_dma_tx_buffer_t *buf); -extern volatile u_int32_t ssc_tx_fiq_fdt_cdiv; -extern volatile u_int32_t ssc_tx_fiq_fdt_ssc; - -extern void ssc_set_data_gate(int enable); - -#endif diff --git a/openpicc/application/tc_fdt.c b/openpicc/application/tc_fdt.c index 2a306ef..3e4023e 100644 --- a/openpicc/application/tc_fdt.c +++ b/openpicc/application/tc_fdt.c @@ -40,7 +40,7 @@ #include "tc_cdiv.h" #include "tc_fdt.h" #include "usb_print.h" -#include "ssc_picc.h" +#include "ssc.h" #include "cmd.h" static AT91PS_TC tcfdt = AT91C_BASE_TC2; @@ -59,12 +59,19 @@ int tc_fdt_get_next_slot(int reference_time, int slotlen) } -/* 'count' number of carrier cycles after the last modulation pause, - * we deem the frame to have ended */ -void tc_frame_end_set(u_int16_t count) -{ - tcfdt->TC_RB = count; -} +static const struct { char* name; u_int32_t flag;} flags[] = { + { "MTIOB", AT91C_TC_MTIOB }, + { "MTIOA", AT91C_TC_MTIOA }, + { "CLKSTA", AT91C_TC_CLKSTA }, + { "ETRGS", AT91C_TC_ETRGS }, + { "LDRBS", AT91C_TC_LDRBS }, + { "LDRAS", AT91C_TC_LDRAS }, + { "CPCS", AT91C_TC_CPCS }, + { "CPBS", AT91C_TC_CPBS }, + { "CPAS", AT91C_TC_CPAS }, + { "LOVRS", AT91C_TC_LOVRS }, + { "COVFS", AT91C_TC_COVFS }, +}; static void __ramfunc tc_fdt_irq(void) __attribute__ ((naked)); static void __ramfunc tc_fdt_irq(void) @@ -74,22 +81,18 @@ static void __ramfunc tc_fdt_irq(void) u_int32_t sr = tcfdt->TC_SR; DEBUGP("tc_fdt_irq: TC2_SR=0x%08x TC2_CV=0x%08x ", sr, tcfdt->TC_CV); - + if (sr & AT91C_TC_ETRGS) { DEBUGP("Ext_trigger "); } if (sr & AT91C_TC_CPAS) { DEBUGP("FDT_expired "); } - if (sr & AT91C_TC_CPBS) { - usb_print_string_f("tc_cpbs ", 0); - DEBUGP("Frame_end "); - ssc_rx_stop_frame_ended(); - } if (sr & AT91C_TC_CPCS) { DEBUGP("Compare_C "); } DEBUGPCR(""); + AT91F_AIC_ClearIt(AT91C_ID_TC2); AT91F_AIC_AcknowledgeIt(); //vLedSetGreen(0); portRESTORE_CONTEXT(); @@ -113,9 +116,6 @@ void tc_fdt_init(void) /* Enable Clock for TC2 */ tcfdt->TC_CCR = AT91C_TC_CLKEN; - tcfdt->TC_RC = 0xffff; - tc_frame_end_set(128*2); - /* Clock XC1, Wave Mode, No automatic reset on RC comp * TIOA2 in RA comp = set, TIOA2 on RC comp = clear, * TIOA2 on EEVT = clear, TIOA2 on SWTRG = clear, @@ -128,14 +128,17 @@ void tc_fdt_init(void) AT91C_TC_EEVT_TIOB | AT91C_TC_ETRGEDG_FALLING | AT91C_TC_ENETRG | AT91C_TC_CPCSTOP ; + tcfdt->TC_RC = 0xffff; + /* Reset to start timers */ tcb->TCB_BCR = 1; AT91F_AIC_ConfigureIt(AT91C_ID_TC2, OPENPCD_IRQ_PRIO_TC_FDT, AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, (THandler)&tc_fdt_irq); + tcfdt->TC_IER = AT91C_TC_CPAS | AT91C_TC_CPCS | + AT91C_TC_ETRGS; + AT91F_AIC_ClearIt(AT91C_ID_TC2); AT91F_AIC_EnableIt(AT91C_ID_TC2); - tcfdt->TC_IER = AT91C_TC_CPAS | AT91C_TC_CPBS | AT91C_TC_CPCS | - AT91C_TC_ETRGS; } diff --git a/openpicc/application/usb_print.c b/openpicc/application/usb_print.c index ccde7de..5ff42b8 100644 --- a/openpicc/application/usb_print.c +++ b/openpicc/application/usb_print.c @@ -114,7 +114,7 @@ void usb_print_flush(void) taskEXIT_CRITICAL(); while(newstart != oldstop) { - vUSBSendByte(ringbuffer[newstart]); + vUSBSendByte_blocking(ringbuffer[newstart], 5*portTICK_RATE_MS); newstart = (newstart+1) % BUFLEN; } diff --git a/openpicc/os/usb/USB-CDC.c b/openpicc/os/usb/USB-CDC.c index 13435b4..1b840c9 100644 --- a/openpicc/os/usb/USB-CDC.c +++ b/openpicc/os/usb/USB-CDC.c @@ -261,12 +261,18 @@ vUSBCDCTask (void *pvParameters) void vUSBSendByte (portCHAR cByte) +{ + vUSBSendByte_blocking(cByte, usbNO_BLOCK); +} + +void +vUSBSendByte_blocking (portCHAR cByte, portTickType xTicksToWait) { char chunk[CHUNK_SIZE]; chunk[0] = 1; chunk[1] = cByte; /* Queue the byte to be sent. The USB task will send it. */ - xQueueSend (xTxCDC, &chunk, usbNO_BLOCK); + xQueueSend (xTxCDC, &chunk, xTicksToWait); } #define MIN(a,b) ((a)>(b)?(b):(a)) diff --git a/openpicc/os/usb/USB-CDC.h b/openpicc/os/usb/USB-CDC.h index 511c8a8..6074956 100644 --- a/openpicc/os/usb/USB-CDC.h +++ b/openpicc/os/usb/USB-CDC.h @@ -82,6 +82,7 @@ void vUSBCDCTask (void *pvParameters); /* Send cByte down the USB port. Characters are simply buffered and not sent unless the port is connected. */ void vUSBSendByte (portCHAR cByte); +void vUSBSendByte_blocking (portCHAR cByte, portTickType xTicksToWait); void vUSBSendBuffer (unsigned char *buffer, portBASE_TYPE offset, portBASE_TYPE length); portLONG vUSBRecvByte (portCHAR *cByte,portLONG size, portTickType xTicksToWait); -- cgit v1.2.3