From d3bab6e9439b7161b7724df0cc902f4adcf1ecce Mon Sep 17 00:00:00 2001 From: henryk Date: Mon, 3 Mar 2008 09:18:19 +0000 Subject: Implement transmission Implement a 'pretender' for PoC: Since Rx is not working properly we'll just pretend to have received something based on the approximate length. Clarify length calculations in machester encoder and remove obsolete test code git-svn-id: https://svn.openpcd.org:2342/trunk@437 6dc7ffe9-61d6-0310-9af1-9938baff3ed1 --- openpicc/application/cmd.c | 3 +- openpicc/application/iso14443_layer2a.c | 92 ++++++++++- openpicc/application/iso14443_layer2a.h | 4 +- openpicc/application/iso14443a_manchester.c | 11 +- openpicc/application/iso14443a_pretender.c | 227 ++++++++++++++++++++++++++++ openpicc/application/main.c | 7 +- openpicc/application/ssc.c | 172 ++++++++++++++++++--- openpicc/application/ssc.h | 7 +- openpicc/application/ssc_buffer.h | 10 +- 9 files changed, 492 insertions(+), 41 deletions(-) create mode 100644 openpicc/application/iso14443a_pretender.c (limited to 'openpicc/application') diff --git a/openpicc/application/cmd.c b/openpicc/application/cmd.c index 3b777b3..878a991 100644 --- a/openpicc/application/cmd.c +++ b/openpicc/application/cmd.c @@ -224,8 +224,7 @@ void prvExecCommand(u_int32_t cmd, portCHAR *args) { break; } i=atoiEx(args, &args); - //BROKEN new ssc code - //ssc_set_data_gate(i); + ssc_set_gate(i); if(i==0) { DumpStringToUSB("SSC_DATA disabled \n\r"); } else { diff --git a/openpicc/application/iso14443_layer2a.c b/openpicc/application/iso14443_layer2a.c index 57754f8..d5e89cf 100644 --- a/openpicc/application/iso14443_layer2a.c +++ b/openpicc/application/iso14443_layer2a.c @@ -39,6 +39,7 @@ #include "openpicc.h" #include "iso14443_layer2a.h" #include "ssc.h" +#include "ssc_buffer.h" #include "pll.h" #include "tc_fdt.h" #include "tc_cdiv.h" @@ -51,6 +52,9 @@ #define PRINT_DEBUG 0 static u_int8_t fast_receive; +static u_int8_t tx_pending=0; +static u_int8_t rx_pending=0; +static iso14443_receive_callback_t callback=NULL; static ssc_handle_t *ssc; #ifdef FOUR_TIMES_OVERSAMPLING @@ -59,16 +63,23 @@ static ssc_handle_t *ssc; #define RX_DIVIDER 64 #endif -int iso14443_receive(iso14443_receive_callback_t callback, ssc_dma_rx_buffer_t **buffer, unsigned int timeout) +int iso14443_receive(iso14443_receive_callback_t _callback, ssc_dma_rx_buffer_t **buffer, unsigned int timeout) { - (void)callback; (void)timeout; ssc_dma_rx_buffer_t* _buffer = NULL; int len; + if(rx_pending) { + return -EALREADY; + } + rx_pending=1; + callback=_callback; + if(ssc_recv(ssc, &_buffer, timeout) == 0) { if(_buffer == NULL) { /* Can this happen? */ + rx_pending=0; + callback=NULL; return -ETIMEDOUT; } @@ -78,6 +89,10 @@ int iso14443_receive(iso14443_receive_callback_t callback, ssc_dma_rx_buffer_t * len = _buffer->len_transfers; + if(callback != NULL && !fast_receive) { + callback(_buffer, 0); + } + if(buffer != NULL) *buffer = _buffer; else { portENTER_CRITICAL(); @@ -85,13 +100,59 @@ int iso14443_receive(iso14443_receive_callback_t callback, ssc_dma_rx_buffer_t * portEXIT_CRITICAL(); } + rx_pending=0; + callback=NULL; return len; } - if(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_PIO_FRAME)) tc_cdiv_sync_reset(); + /* Note: There is the remote chance of a race condition probability here if + * a frame start was received right before the timeout for this function + * expired. In the future one might want to replace this with some safer code + * (hmm, maybe check TC2_CV?) but for now it's an essential safeguard to prevent + * a hung receiver when no proper frame end is signalled to iso14443_ssc_callback + * and therefore the callback never resets the flipflop */ + if(!tx_pending) { + if(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_PIO_FRAME)) tc_cdiv_sync_reset(); + } + + rx_pending=0; + callback=NULL; return -ETIMEDOUT; } +int iso14443_transmit(ssc_dma_tx_buffer_t *buffer, unsigned int fdt, u_int8_t async, unsigned int timeout) +{ + if(tx_pending) + return -EBUSY; + + tx_pending = 1; + + /* Immediately set up FDT and clock */ + ssc_select_clock(CLOCK_SELECT_CARRIER); + ssc_set_gate(0); + tc_fdt_set(fdt); + tc_cdiv_set_divider(8); // FIXME Magic hardcoded number + + if(!async) { + /* FIXME Implement */ + (void)timeout; + tx_pending = 0; + return -EINVAL; + } + + int ret = ssc_send(ssc, buffer); + if(ret < 0) { + tx_pending = 0; + return ret; + } + + if(!async) { + /* FIXME Wait for completion, timeout or abort */ + } + + return 0; +} + int iso14443_wait_for_carrier(unsigned int timeout) { (void)timeout; @@ -112,7 +173,6 @@ u_int8_t iso14443_get_fast_receive(void) static void iso14443_ssc_callback(ssc_callback_reason reason, void *data) { - (void) data; if(reason == CALLBACK_RX_FRAME_BEGIN) { /* Busy loop for the frame end */ int *end_asserted = data, i=0; @@ -124,11 +184,28 @@ static void iso14443_ssc_callback(ssc_callback_reason reason, void *data) } return; } - if( reason == CALLBACK_RX_FRAME_ENDED || reason == CALLBACK_RX_STARTED ) { + + if(reason == CALLBACK_TX_FRAME_ENDED) { + tx_pending = 0; + } + + if( reason == CALLBACK_RX_FRAME_ENDED && fast_receive ) { + ssc_select_clock(CLOCK_SELECT_CARRIER); /* A Tx might be coming up */ + + ssc_dma_rx_buffer_t *buffer = data; + if(callback != NULL) + callback(buffer, 1); + } + + if( (reason == CALLBACK_RX_FRAME_ENDED && !tx_pending) || reason == CALLBACK_RX_STARTING + || reason == CALLBACK_TX_FRAME_ENDED ) { + ssc_select_clock(CLOCK_SELECT_PLL); + ssc_set_gate(1); tc_fdt_set(0xff00); tc_cdiv_set_divider(RX_DIVIDER); tc_cdiv_sync_reset(); } + } static void iso14443_rx_FRAME_cb(u_int32_t pio) @@ -155,12 +232,11 @@ int iso14443_layer2a_init(u_int8_t enable_fast_receive) if(PRINT_DEBUG) usb_print_string("FRAME irq registered\n\r"); // DEBUG OUTPUT } - ssc = ssc_open(1, 0, SSC_MODE_14443A, iso14443_ssc_callback); + ssc = ssc_open(1, 1, 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); + load_mod_level(3); return 0; } diff --git a/openpicc/application/iso14443_layer2a.h b/openpicc/application/iso14443_layer2a.h index cf31ec4..858af81 100644 --- a/openpicc/application/iso14443_layer2a.h +++ b/openpicc/application/iso14443_layer2a.h @@ -23,8 +23,8 @@ typedef void (*iso14443_receive_callback_t)(ssc_dma_rx_buffer_t *buffer, u_int8_ * This call will block until a frame is received or an exception happens. Obviously it must not be run * from IRQ context. * - * Warning: When you get a buffer from the function then it's state is set to PROCESSING and you must - * FREE it yourself. + * Warning: When you get a buffer from the function then its state is set to PROCESSING and you must + * FREE it yourself. However, you MUST NOT free a buffer from the callback. * * Return values: * >= 0 Frame received, return value is buffer length (yes, 0 is a valid buffer length) diff --git a/openpicc/application/iso14443a_manchester.c b/openpicc/application/iso14443a_manchester.c index 303491f..e8640fa 100644 --- a/openpicc/application/iso14443a_manchester.c +++ b/openpicc/application/iso14443a_manchester.c @@ -104,10 +104,10 @@ int manchester_encode(u_int8_t *sample_buf, u_int16_t sample_buf_len, if(frame->parameters.a.format != STANDARD_FRAME) return -EINVAL; /* AC not implemented yet */ /* One bit data is 16 bit is 2 byte modulation data */ - enc_size = 2*2 /* SOF and EOF */ - + frame->numbytes * 8 * 2 - + ((frame->parameters.a.parity != NO_PARITY) ? 1 : 0)*8*2 - + 6; + enc_size = 1 /* SOF */ + + frame->numbytes * ((frame->parameters.a.parity != NO_PARITY) ? 9 : 8) /* bits per byte */ + + 1 /* EOF */; + enc_size = enc_size /* in bits */ * 2 /* bytes modulation data per bit raw data*/; if (sample_buf_len < enc_size) return -EINVAL; @@ -115,9 +115,6 @@ int manchester_encode(u_int8_t *sample_buf, u_int16_t sample_buf_len, memset(sample_buf, 0, enc_size); samples16 = (u_int16_t*)sample_buf; - (*samples16) = 5; - samples16+=2; // SSC workaround - //*(samples16++) = 0xb; /* SOF */ *(samples16++) = MANCHESTER_SEQ_D; diff --git a/openpicc/application/iso14443a_pretender.c b/openpicc/application/iso14443a_pretender.c new file mode 100644 index 0000000..6f62330 --- /dev/null +++ b/openpicc/application/iso14443a_pretender.c @@ -0,0 +1,227 @@ +/*************************************************************** + * + * OpenPICC - ISO 14443 Layer 3 response pretender, + * proof of concept + * + * Copyright 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; version 2. + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +*/ + +#include +#include +#include +#include +#include + +#include "openpicc.h" +#include "ssc_buffer.h" +#include "iso14443.h" +#include "iso14443_layer2a.h" +#include "iso14443a_pretender.h" +#include "iso14443a_manchester.h" +#include "usb_print.h" +#include "cmd.h" +#include "led.h" + +static const iso14443_frame ATQA_FRAME = { + TYPE_A, + {{STANDARD_FRAME, PARITY, ISO14443A_LAST_BIT_NONE}}, + 2, + 0, 0, + {4, 0}, + {} +}; + +static ssc_dma_tx_buffer_t tx_buffer; + +static void fast_receive_callback(ssc_dma_rx_buffer_t *buffer, u_int8_t in_irq) +{ + (void)buffer; (void)in_irq; + unsigned int buffy=0; + u_int32_t cv = *AT91C_TC2_CV; + + const iso14443_frame *tx_frame = NULL; + int fdt = 0; + + switch(buffer->len_transfers) { + case 3: case 4: /* REQA (7 bits) */ + tx_frame = &ATQA_FRAME; + fdt = 1172; + break; + case 6: case 7: /* ANTICOL (2 bytes) */ + case 22: /* SELECT (9 bytes) */ + + break; + case 577: + usb_print_string_f("f", 0); + buffy=(unsigned int)buffer; + break; + } + + /* Add some extra room to the fdt for testing */ + fdt += 3*128; + + int ret = 0; + if(tx_frame != NULL) { + tx_buffer.source = (void*)tx_frame; + tx_buffer.len = sizeof(tx_buffer.data); + ret = manchester_encode(tx_buffer.data, + tx_buffer.len, + tx_frame); + if(ret >= 0) { + tx_buffer.state = FULL; + tx_buffer.len = ret; + if( iso14443_transmit(&tx_buffer, fdt, 1, 0) < 0) { + tx_buffer.state = FREE; + usb_print_string_f("Tx failed ", 0); + } + } else tx_buffer.state = FREE; + + } + + u_int32_t cv2 = *AT91C_TC2_CV; + int old=usb_print_set_default_flush(0); + DumpUIntToUSB(cv); + DumpStringToUSB(":"); + DumpUIntToUSB(cv2); + if(ret<0) { + DumpStringToUSB("!"); + DumpUIntToUSB(-ret); + } + if(buffy!=0) { + DumpStringToUSB("§"); + DumpUIntToUSB(buffy); + } + usb_print_set_default_flush(old); + usb_print_string_f("%", 0); +} + +void iso14443a_pretender (void *pvParameters) +{ + (void)pvParameters; + int res; + + /* Delay until USB print etc. are ready */ + int i; + for(i=0; i<=1000; i++) { + vLedSetBrightness(LED_RED, i); + vLedSetBrightness(LED_GREEN, i); + vTaskDelay(1*portTICK_RATE_MS); + } + + for(i=0; i<=1000; i+=4) { + vLedSetBrightness(LED_GREEN, 1000-i); + vTaskDelay(1*portTICK_RATE_MS); + } + + do { + res = iso14443_layer2a_init(1); + if(res < 0) { + usb_print_string("Pretender: Initialization failed\n\r"); + for(i=0; i<=10000; i++) { + vLedSetBrightness(LED_RED, abs(1000-(i%2000))); + vTaskDelay(1*portTICK_RATE_MS); + } + } + } while(res < 0); + + + usb_print_string("Waiting for carrier. "); + while(iso14443_wait_for_carrier(1000 * portTICK_RATE_MS) != 0) { + } + usb_print_string("Carrier detected.\n\r"); + + for(i=0; i<=1000; i+=4) { + vLedSetBrightness(LED_RED, 1000-i); + vTaskDelay(1*portTICK_RATE_MS); + } + + vTaskDelay(250*portTICK_RATE_MS); + vLedSetGreen(0); + vLedSetRed(0); + + int current_detected = 0, last_detected = 0; + portTickType last_switched = 0; +#define DETECTED_14443A_3 1 +#define DETECTED_14443A_4 2 +#define DETECTED_MIFARE 4 + + while(true) { + ssc_dma_rx_buffer_t *buffer = 0; + res = iso14443_receive(fast_receive_callback, &buffer, 1000 * portTICK_RATE_MS); + if(res >= 0) { + DumpUIntToUSB(buffer->len_transfers); + DumpStringToUSB("\n\r"); + + switch(buffer->len_transfers) { + case 3: case 4: /* REQA (7 bits) */ + current_detected |= DETECTED_14443A_3; + break; + case 6: case 7: /* ANTICOL (2 bytes) */ + case 22: /* SELECT (9 bytes) */ + current_detected |= DETECTED_14443A_3; + break; + case 10: case 11: /* AUTH1A (or any other four byte frame) */ + case 19: case 20: /* AUTH2 (8 bytes) */ + current_detected |= DETECTED_MIFARE; + break; + } + + portENTER_CRITICAL(); + buffer->state = FREE; + portEXIT_CRITICAL(); + } else { + if(res != -ETIMEDOUT) { + usb_print_string("Receive error: "); + switch(res) { + case -ENETDOWN: usb_print_string("PLL is not locked or PLL lock lost\n\r"); break; + case -EBUSY: usb_print_string("A Tx is currently running or pending, can't receive\n\r"); break; + case -EALREADY: usb_print_string("There's already an iso14443_receive() invocation running\n\r"); break; + } + vTaskDelay(1000 * portTICK_RATE_MS); // FIXME Proper error handling, e.g. wait for Tx end in case of EBUSY + } else if(0) { + DumpStringToUSB("\n\r"); + DumpTimeToUSB(xTaskGetTickCount()); + usb_print_string(": -- Mark --"); + } + } + + if( last_switched < xTaskGetTickCount()-(1000*portTICK_RATE_MS) ) { + last_detected = current_detected; + current_detected = 0; + last_switched = xTaskGetTickCount(); + } + + if(last_detected & (DETECTED_14443A_3 | DETECTED_14443A_4 | DETECTED_MIFARE)) { + if(last_detected & DETECTED_MIFARE) { + vLedSetGreen(0); + vLedSetRed(1); + } else if(last_detected & DETECTED_14443A_4) { + vLedSetGreen(1); + vLedSetRed(0); + } else { + vLedSetGreen(1); + vLedSetRed(1); + } + } else { + vLedSetGreen(0); + vLedSetRed(0); + } + + } +} diff --git a/openpicc/application/main.c b/openpicc/application/main.c index d9b35f1..2397c61 100644 --- a/openpicc/application/main.c +++ b/openpicc/application/main.c @@ -49,6 +49,7 @@ #include "usb_print.h" #include "iso14443_layer3a.h" #include "iso14443_sniffer.h" +#include "iso14443a_pretender.h" #include "decoder.h" static inline int detect_board(void) @@ -207,9 +208,11 @@ int main (void) 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, + /*xTaskCreate (iso14443_sniffer, (signed portCHAR *) "ISO14443-SNIFF", TASK_ISO_STACK, + NULL, TASK_ISO_PRIORITY, NULL);*/ + xTaskCreate (iso14443a_pretender, (signed portCHAR *) "ISO14443A-PRETEND", TASK_ISO_STACK, NULL, TASK_ISO_PRIORITY, NULL); - + xTaskCreate (vUSBCDCTask, (signed portCHAR *) "USB", TASK_USB_STACK, NULL, TASK_USB_PRIORITY, NULL); diff --git a/openpicc/application/ssc.c b/openpicc/application/ssc.c index f15b25d..5603256 100644 --- a/openpicc/application/ssc.c +++ b/openpicc/application/ssc.c @@ -50,6 +50,7 @@ struct _ssc_handle { enum ssc_mode mode; const struct openpicc_hardware *openpicc; ssc_dma_rx_buffer_t* rx_buffer[2]; + ssc_dma_tx_buffer_t* tx_buffer; ssc_callback_t callback; xQueueHandle rx_queue; AT91PS_PDC pdc; @@ -79,6 +80,7 @@ static struct { [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}, + [METRIC_LATE_TX_FRAMES] = {METRIC_LATE_TX_FRAMES, "Late Tx frames", 0}, }; static ssc_dma_rx_buffer_t _rx_buffers[SSC_DMA_BUFFER_COUNT]; @@ -152,9 +154,22 @@ static int __ramfunc _ssc_rx_irq(u_int32_t orig_sr, int start_asserted, portBASE } if(_reload_rx(sh)) { + /* Clear the receive holding register. Is this necessary? */ int dummy = sh->ssc->SSC_RHR; (void)dummy; AT91F_PDC_EnableRx(sh->pdc); - AT91F_SSC_EnableRx(sh->ssc); + if(!sh->tx_running) { + /* Only start the receiver if no Tx has been started. Warning: Need + * to make sure that the receiver is restarted again when the Tx is over. + * Note that this is only necessary when not using Compare 0 to start + * reception. When using Compare 0 the data gating mechanism will keep + * CP0 from happening and thus keep the receiver stopped. + * + * Note also that we're not resetting rx_running to 0, because conceptionally + * the Rx is still running,. So rx_running=1 and tx_running=1 means SSC_RX + * stopped and SSC_RX should be started as soon as SSC_TX stops. + */ + AT91F_SSC_EnableRx(sh->ssc); + } } else { sh->ssc->SSC_IDR = SSC_RX_IRQ_MASK; sh->rx_running = 0; @@ -173,6 +188,34 @@ void ssc_frame_started(void) _ssc_rx_irq(_ssc.ssc->SSC_SR, 1, pdFALSE); } +static int __ramfunc _ssc_tx_irq(u_int32_t sr, portBASE_TYPE task_woken) +{ + ssc_handle_t *sh = &_ssc; + + if( sr & AT91C_SSC_ENDTX ) { + /* Tx has ended */ + AT91F_PDC_DisableTx(sh->pdc); + AT91F_SSC_DisableTx(sh->ssc); + sh->ssc->SSC_IDR = SSC_TX_IRQ_MASK; + + if(sh->tx_buffer) { + sh->tx_buffer->state = FREE; + sh->tx_running = 0; + } + + if(sh->rx_running) { + /* Receiver has been suspended by the pending transmission. Restart it. */ + AT91F_SSC_EnableRx(sh->ssc); + } + + if(sh->callback) + sh->callback(CALLBACK_TX_FRAME_ENDED, sh->tx_buffer); + sh->tx_buffer = NULL; + } + + return task_woken; +} + static void __ramfunc ssc_irq(void) __attribute__ ((naked)); static void __ramfunc ssc_irq(void) { @@ -187,6 +230,10 @@ static void __ramfunc ssc_irq(void) task_woken = _ssc_rx_irq(sr, 0, task_woken); } + if(sr & SSC_TX_IRQ_MASK) { + task_woken = _ssc_tx_irq(sr, task_woken); + } + AT91F_AIC_ClearIt(AT91C_ID_SSC); AT91F_AIC_AcknowledgeIt(); @@ -301,6 +348,18 @@ void ssc_select_clock(enum ssc_clock_source clock) } } +void ssc_set_gate(int data_enabled) { + if(OPENPICC->features.data_gating) { + if(data_enabled) { + AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE); + usb_print_string_f("(", 0); + } else { + AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE); + usb_print_string_f(")", 0); + } + } +} + static void _ssc_start_rx(ssc_handle_t *sh) { taskENTER_CRITICAL(); @@ -316,7 +375,7 @@ static void _ssc_start_rx(ssc_handle_t *sh) AT91C_SSC_CP1 | AT91C_SSC_ENDRX; sh->rx_running = 1; if(sh->callback != NULL) - sh->callback(CALLBACK_RX_STARTED, sh); + sh->callback(CALLBACK_RX_STARTING, sh); // Actually enable reception int dummy = sh->ssc->SSC_RHR; (void)dummy; @@ -412,11 +471,6 @@ static inline int _init_ssc_rx(ssc_handle_t *sh) 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, @@ -425,11 +479,6 @@ static inline int _init_ssc_rx(ssc_handle_t *sh) 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); @@ -441,6 +490,10 @@ static inline int _init_ssc_rx(ssc_handle_t *sh) /* don't divide clock inside SSC, we do that in tc_cdiv */ sh->ssc->SSC_CMR = 0; + unsigned int i; + for(i=0; irx_buffer[0] = sh->rx_buffer[1] = NULL; /* Will be set to a real value some time later @@ -453,6 +506,29 @@ out_fail_queue: return 0; } +static inline int _init_ssc_tx(ssc_handle_t *sh) +{ + /* 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); + + if(OPENPICC->features.clock_switching) { + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC->CLOCK_SWITCH); + /* Users: remember to ssc_select_clock(CLOCK_SELECT_CARRIER) as + * early as possible, e.g. right after receive end */ + } + + /* Disable all interrupts */ + sh->ssc->SSC_IDR = SSC_TX_IRQ_MASK; + + /* don't divide clock inside SSC, we do that in tc_cdiv */ + sh->ssc->SSC_CMR = 0; + + return 1; +} + static int _ssc_register_callback(ssc_handle_t *sh, ssc_callback_t _callback) { if(!sh) return -EINVAL; @@ -478,7 +554,6 @@ static int _ssc_unregister_callback(ssc_handle_t *sh, ssc_callback_t _callback) 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) { @@ -486,13 +561,25 @@ ssc_handle_t* ssc_open(u_int8_t init_rx, u_int8_t init_tx, enum ssc_mode mode, s } } - for(i=0; issc = AT91C_BASE_SSC; + sh->pdc = (AT91PS_PDC) &(sh->ssc->SSC_RPR); + + AT91F_SSC_CfgPMC(); + + if(OPENPICC->features.data_gating) { + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC->DATA_GATE); + ssc_set_gate(1); + } + } + if(init_tx) { - // FIXME Implement - sh->tx_enabled = 0; - return NULL; + sh->tx_enabled = _init_ssc_tx(sh); + if(!sh->tx_enabled) { + ssc_close(sh); + return NULL; + } } if(init_rx) { @@ -544,6 +631,55 @@ int ssc_recv(ssc_handle_t* sh, ssc_dma_rx_buffer_t* *buffer,unsigned int timeout return -ETIMEDOUT; } +/* Must be called with IRQs disabled. E.g. from IRQ context or within a critical section. */ +int ssc_send(ssc_handle_t* sh, ssc_dma_tx_buffer_t *buffer) +{ + if(sh == NULL) return -EINVAL; + if(!sh->tx_enabled) return -EINVAL; + if(sh->tx_running) return -EBUSY; + + sh->tx_buffer = buffer; + sh->tx_running = 1; + + /* disable Tx */ + sh->ssc->SSC_IDR = SSC_TX_IRQ_MASK; + AT91F_PDC_DisableTx(sh->pdc); + AT91F_SSC_DisableTx(sh->ssc); + + int start_cond = AT91C_SSC_START_HIGH_RF; + + int sync_len = 1; + int data_len = 32; + int num_data = buffer->len/(data_len/8); /* FIXME This is broken for more than 64 bytes, or is it? */ + int num_data_ssc = num_data > 16 ? 16 : num_data; + + sh->ssc->SSC_TFMR = ((data_len-1) & 0x1f) | + (((num_data_ssc-1) & 0x0f) << 8) | + (((sync_len-1) & 0x0f) << 16); + sh->ssc->SSC_TCMR = 0x01 | AT91C_SSC_CKO_NONE | (AT91C_SSC_CKI&0) | start_cond; + + AT91F_PDC_SetTx(sh->pdc, buffer->data, num_data); + AT91F_PDC_SetNextTx(sh->pdc, 0, 0); + buffer->state = PENDING; + + sh->ssc->SSC_IER = AT91C_SSC_ENDTX; + /* Enable DMA */ + AT91F_PDC_EnableTx(sh->pdc); + //AT91F_PDC_SetTx(sh->pdc, buffer->data, num_data); + + /* Disable Receiver, see comments in _ssc_rx_irq */ + AT91F_SSC_DisableRx(sh->ssc); + /* Start Transmission */ + AT91F_SSC_EnableTx(sh->ssc); + vLedSetGreen(1); + + if(AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_SSC_TF)) { + ssc_metrics[METRIC_LATE_TX_FRAMES].value++; + } + + return 0; +} + int ssc_close(ssc_handle_t* sh) { if(sh->rx_running) diff --git a/openpicc/application/ssc.h b/openpicc/application/ssc.h index 0d96920..ac74255 100644 --- a/openpicc/application/ssc.h +++ b/openpicc/application/ssc.h @@ -11,18 +11,21 @@ typedef enum { 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 + METRIC_LATE_TX_FRAMES, // Frames that only reached the SSC when TF already was high _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_STARTING, // *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_TX_FRAME_BEGIN, + CALLBACK_TX_FRAME_ENDED, CALLBACK_SETUP, // *data is ssh_handle_t *sh CALLBACK_TEARDOWN, // *data is ssh_handle_t *sh } ssc_callback_reason; @@ -38,6 +41,7 @@ struct _ssc_handle; typedef struct _ssc_handle ssc_handle_t; extern void ssc_select_clock(enum ssc_clock_source clock); +extern void ssc_set_gate(int data_enabled); extern void ssc_frame_started(void); @@ -45,6 +49,7 @@ extern void ssc_frame_started(void); 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_send(ssc_handle_t* sh, ssc_dma_tx_buffer_t* buffer); extern int ssc_close(ssc_handle_t* sh); diff --git a/openpicc/application/ssc_buffer.h b/openpicc/application/ssc_buffer.h index fdb977f..7c79013 100644 --- a/openpicc/application/ssc_buffer.h +++ b/openpicc/application/ssc_buffer.h @@ -1,9 +1,14 @@ #ifndef SSC_BUFFER_H_ #define SSC_BUFFER_H_ +#include "iso14443.h" + #define SSC_RX_BUFFER_SIZE_AS_UINT8 2048 #define SSC_DMA_BUFFER_COUNT 4 +/* in bytes, used for the sample buffer that holds the subcarrier modulation data at fc/8 = 1695 MHz */ +#define SSC_TX_BUFFER_SIZE_AS_UINT8 ((MAXIMUM_FRAME_SIZE*( (8+1)*2 ) ) + 2 + 2) + #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) @@ -39,8 +44,11 @@ typedef struct { u_int8_t data[SSC_RX_BUFFER_SIZE_AS_UINT8]; } ssc_dma_rx_buffer_t; - typedef struct { + volatile ssc_dma_buffer_state_t state; + u_int32_t len; /* Length of the content in bytes */ + void *source; /* Source pointer for a prefilled buffer; set to NULL if not used */ + u_int8_t data[SSC_TX_BUFFER_SIZE_AS_UINT8]; } ssc_dma_tx_buffer_t; #endif /*SSC_BUFFER_H_*/ -- cgit v1.2.3