summaryrefslogtreecommitdiff
path: root/openpicc/application
diff options
context:
space:
mode:
authorhenryk <henryk@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>2008-03-03 09:18:19 +0000
committerhenryk <henryk@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>2008-03-03 09:18:19 +0000
commitd3bab6e9439b7161b7724df0cc902f4adcf1ecce (patch)
treec160196b7d058ebc3670805540c1bba0d869f357 /openpicc/application
parent2f730c90916047ec0b419ed9469fa44f537b8ab6 (diff)
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
Diffstat (limited to 'openpicc/application')
-rw-r--r--openpicc/application/cmd.c3
-rw-r--r--openpicc/application/iso14443_layer2a.c92
-rw-r--r--openpicc/application/iso14443_layer2a.h4
-rw-r--r--openpicc/application/iso14443a_manchester.c11
-rw-r--r--openpicc/application/iso14443a_pretender.c227
-rw-r--r--openpicc/application/main.c7
-rw-r--r--openpicc/application/ssc.c172
-rw-r--r--openpicc/application/ssc.h7
-rw-r--r--openpicc/application/ssc_buffer.h10
9 files changed, 492 insertions, 41 deletions
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 <henryk@ploetzli.ch>
+ *
+ ***************************************************************
+
+ 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 <FreeRTOS.h>
+#include <board.h>
+#include <task.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#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; i<sizeof(_rx_buffers)/sizeof(_rx_buffers[0]); i++)
+ memset(&_rx_buffers[i], 0, sizeof(_rx_buffers[i]));
+
sh->rx_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; i<sizeof(_rx_buffers)/sizeof(_rx_buffers[0]); i++)
- memset(&_rx_buffers[i], 0, sizeof(_rx_buffers[i]));
+ if(init_rx || init_tx) {
+ sh->ssc = 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_*/
personal git repositories of Harald Welte. Your mileage may vary