summaryrefslogtreecommitdiff
path: root/firmware/src/picc
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/src/picc')
-rw-r--r--firmware/src/picc/adc.c145
-rw-r--r--firmware/src/picc/decoder.c65
-rw-r--r--firmware/src/picc/decoder.h32
-rw-r--r--firmware/src/picc/decoder_miller.c112
-rw-r--r--firmware/src/picc/decoder_nrzl.c91
-rw-r--r--firmware/src/picc/iso14443a_manchester.c106
-rw-r--r--firmware/src/picc/load_modulation.c29
-rw-r--r--firmware/src/picc/load_modulation.h7
-rw-r--r--firmware/src/picc/main_openpicc.c215
-rw-r--r--firmware/src/picc/piccsim.h23
-rw-r--r--firmware/src/picc/pll.c40
-rw-r--r--firmware/src/picc/pll.h8
-rw-r--r--firmware/src/picc/poti.c58
-rw-r--r--firmware/src/picc/poti.h7
-rw-r--r--firmware/src/picc/ssc_picc.c384
-rw-r--r--firmware/src/picc/ssc_picc.h15
-rw-r--r--firmware/src/picc/tc_fdt.c50
-rw-r--r--firmware/src/picc/tc_fdt.h9
18 files changed, 1396 insertions, 0 deletions
diff --git a/firmware/src/picc/adc.c b/firmware/src/picc/adc.c
new file mode 100644
index 0000000..31b3469
--- /dev/null
+++ b/firmware/src/picc/adc.c
@@ -0,0 +1,145 @@
+/* AT91SAM7 ADC controller routines for OpenPCD / OpenPICC
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <AT91SAM7.h>
+#include <lib_AT91SAM7.h>
+#include <openpcd.h>
+
+#include <os/usb_handler.h>
+#include "../openpcd.h"
+#include <os/dbgu.h>
+
+#define OPENPICC_ADC_CH_FIELDSTR AT91C_ADC_CH4
+#define OPENPICC_ADC_CH_PLL_DEM AT91C_ADC_CH5
+
+#define DEBUG_ADC
+
+#ifdef DEBUG_ADC
+#define DEBUGADC DEBUGP
+#else
+#define DEBUGADC do { } while (0)
+#endif
+
+static const AT91PS_ADC adc = AT91C_BASE_ADC;
+
+enum adc_states {
+ ADC_NONE,
+ ADC_READ_CONTINUOUS,
+ ADC_READ_CONTINUOUS_USB,
+ ADC_READ_SINGLE,
+};
+
+struct adc_state {
+ enum adc_states state;
+ struct req_ctx *rctx;
+};
+
+static struct adc_state adc_state;
+
+static void adc_irq(void)
+{
+ u_int32_t sr = adc->ADC_SR;
+ struct req_ctx *rctx = adc_state.rctx;
+
+ DEBUGADC("adc_irq(SR=0x%08x, IMR=0x%08x, state=%u): ",
+ sr, adc->ADC_IMR, adc_state.state);
+
+ switch (adc_state.state) {
+ case ADC_NONE:
+ //break;
+ case ADC_READ_CONTINUOUS_USB:
+ if (sr & AT91C_ADC_EOC4)
+ DEBUGADC("CDR4=0x%4x ", adc->ADC_CDR4);
+ if (sr & AT91C_ADC_EOC5)
+ DEBUGADC("CDR5=0x%4x ", adc->ADC_CDR5);
+ if (sr & AT91C_ADC_ENDRX) {
+ /* rctx full, get rid of it */
+ DEBUGADC("sending rctx (val=%s) ",
+ hexdump(rctx->tx.data[4], 2));
+
+ req_ctx_set_state(rctx, RCTX_STATE_UDP_EP2_PENDING);
+ adc_state.state = ADC_NONE;
+ adc_state.rctx = NULL;
+
+ //AT91F_PDC_SetRx(AT91C_BASE_PDC_ADC, NULL, 0);
+
+ /* Disable EOC interrupts since we don't want to
+ * re-start conversion any further*/
+ AT91F_ADC_DisableIt(AT91C_BASE_ADC, AT91C_ADC_ENDRX);
+ //AT91C_ADC_EOC4|AT91C_ADC_EOC5|AT91C_ADC_ENDRX);
+ AT91F_PDC_DisableRx(AT91C_BASE_PDC_ADC);
+ DEBUGADC("disabled IT/RX ");
+ } else {
+ if (sr & (AT91C_ADC_EOC4|AT91C_ADC_EOC5)) {
+ /* re-start conversion, since we need more values */
+ AT91F_ADC_StartConversion(adc);
+ }
+ }
+ break;
+ }
+
+ AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_ADC);
+ DEBUGADC("cleeared ADC IRQ in AIC\r\n");
+}
+
+#if 0
+u_int16_t adc_read_fieldstr(void)
+{
+ return adc->ADC_CDR4;
+}
+
+u_int16_T adc_read_pll_dem(void)
+{
+ return adc
+}
+#endif
+
+static int adc_usb_in(struct req_ctx *rctx)
+{
+ struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ struct openpcd_hdr *pih = (struct openpcd_hdr *) &rctx->tx.data[0];
+
+ switch (poh->cmd) {
+ case OPENPCD_CMD_ADC_READ:
+ DEBUGADC("ADC_READ(chan=%u, len=%u) ", poh->reg, poh->val);
+ //channel = poh->reg;
+ if (adc_state.rctx) {
+ /* FIXME: do something */
+ req_ctx_put(rctx);
+ }
+
+ adc_state.state = ADC_READ_CONTINUOUS_USB;
+ adc_state.rctx = rctx;
+ memcpy(pih, poh, sizeof(*pih));
+ rctx->tx.tot_len = sizeof(*pih) + poh->val * 2;
+ AT91F_PDC_SetRx(AT91C_BASE_PDC_ADC, rctx->rx.data, poh->val);
+ AT91F_PDC_EnableRx(AT91C_BASE_PDC_ADC);
+ AT91F_ADC_EnableChannel(AT91C_BASE_ADC, OPENPICC_ADC_CH_FIELDSTR);
+ AT91F_ADC_EnableIt(AT91C_BASE_ADC, AT91C_ADC_ENDRX |
+ OPENPICC_ADC_CH_FIELDSTR);
+ AT91F_ADC_StartConversion(adc);
+ break;
+ }
+}
+
+int adc_init(void)
+{
+ AT91F_ADC_CfgPMC();
+ AT91F_ADC_CfgTimings(AT91C_BASE_ADC, 48 /*MHz*/, 5 /*MHz*/,
+ 20/*uSec*/, 700/*nSec*/);
+#if 0
+ AT91F_ADC_EnableChannel(AT91C_BASE_ADC, OPENPICC_ADC_CH_FIELDSTR |
+ OPENPICC_ADC_CH_PLL_DEM);
+#endif
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_ADC,
+ AT91C_AIC_PRIOR_LOWEST,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &adc_irq);
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_ADC);
+
+ usb_hdlr_register(&adc_usb_in, OPENPCD_CMD_CLS_ADC);
+}
diff --git a/firmware/src/picc/decoder.c b/firmware/src/picc/decoder.c
new file mode 100644
index 0000000..9f9191a
--- /dev/null
+++ b/firmware/src/picc/decoder.c
@@ -0,0 +1,65 @@
+/* Decoder Core for OpenPCD / OpenPICC
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <picc/decoder.h>
+
+#include <os/dbgu.h>
+
+static struct decoder_algo *decoder_algo[DECODER_NUM_ALGOS];
+
+static int get_next_data(struct decoder_state *st, u_int8_t *data)
+{
+ u_int8_t parity_sample;
+ u_int32_t bytesample;
+
+ bytesample = st->algo->get_next_bytesample(st, &parity_sample);
+
+ return st->algo->decode_sample(bytesample, data);
+}
+
+/* iterate over sample buffer (size N bytes) and decode data */
+int decoder_decode(u_int8_t algo, const char *sample_buf,
+ int sample_buf_size, char *data_buf)
+{
+ int i, ret;
+ struct decoder_state st;
+
+ if (algo >= DECODER_NUM_ALGOS)
+ return -EINVAL;
+
+ st.buf = sample_buf;
+ st.buf32 = (u_int32_t *) st.buf;
+ st.bit_ofs = 0;
+ st.algo = decoder_algo[algo];
+
+ for (i = 0; i < (sample_buf_size*8)/st.algo->bits_per_sampled_char;
+ i++) {
+ ret = get_next_data(&st, &data_buf[i]);
+ if (ret < 0) {
+ DEBUGPCR("decoder error %d at data byte %u",
+ ret, i);
+ return ret;
+ }
+ }
+
+ return i+1;
+}
+
+int decoder_register(int algnum, struct decoder_algo *algo)
+{
+ if (algnum >= DECODER_NUM_ALGOS)
+ return -EINVAL;
+
+ decoder_algo[algnum] = algo;
+
+ return 0;
+}
+
+void decoder_init(void)
+{
+ decoder_register(DECODER_MILLER, &miller_decoder);
+ decoder_register(DECODER_NRZL, &nrzl_decoder);
+}
diff --git a/firmware/src/picc/decoder.h b/firmware/src/picc/decoder.h
new file mode 100644
index 0000000..aef0e20
--- /dev/null
+++ b/firmware/src/picc/decoder.h
@@ -0,0 +1,32 @@
+#ifndef _DECODER_H
+#define _DECODER_H
+
+struct decoder_state;
+
+struct decoder_algo {
+ u_int8_t oversampling_rate;
+ u_int8_t bits_per_sampled_char;
+ u_int32_t bytesample_mask;
+ int (*decode_sample)(const u_int32_t sample, u_int8_t data);
+ u_int32_t (*get_next_bytesample)(struct decoder_state *st, u_int8_t *parity_sample);
+};
+
+struct decoder_state {
+ struct decoder_algo *algo;
+ u_int8_t bit_ofs;
+ const char *buf;
+ const u_int32_t *buf32;
+};
+
+extern int decoder_register(int algnum, struct decoder_algo *algo);
+extern int decoder_decode(u_int8_t algo, const char *sample_buf,
+ int sample_buf_size, char *data_buf);
+
+#define DECODER_MILLER 0
+#define DECODER_NRZL 1
+#define DECODER_NUM_ALGOS 2
+
+static struct decoder_algo nrzl_decoder;
+static struct decoder_algo miller_decoder;
+
+#endif
diff --git a/firmware/src/picc/decoder_miller.c b/firmware/src/picc/decoder_miller.c
new file mode 100644
index 0000000..1394a17
--- /dev/null
+++ b/firmware/src/picc/decoder_miller.c
@@ -0,0 +1,112 @@
+/*
+ * ISO14443A modified Miller decoder
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * LSB First LSB hex
+ * Sequence X 0010 0100 0x4
+ * Sequence Y 0000 0000 0x0
+ * Sequence Z 1000 0001 0x1
+ *
+ * Logic 1 Sequence X
+ * Logic 0 Sequence Y with two exceptions:
+ * - if there are more contiguous 0, Z used from second one
+ * - if the first bit after SOF is 0, sequence Z used for all contig 0's
+ * SOF Sequence Z
+ * EOF Logic 0 followed by Sequence Y
+ *
+ * cmd hex bits symbols hex (quad-sampled)
+ *
+ * REQA 0x26 S 0110010 E Z ZXXYZXY ZY 0x10410441
+ * WUPA 0x52 S 0100101 E Z ZXYZXYX YY 0x04041041
+ *
+ * SOF is 'eaten' by SSC start condition (Compare 0). Remaining bits are
+ * mirrored, e.g. samples for LSB of first byte are & 0xf
+ *
+ */
+
+#include <sys/types.h>
+
+#include <os/dbgu.h>
+#include <picc/decoder.h>
+
+
+#define OVERSAMPLING_RATE 4
+
+/* definitions for four-times oversampling */
+#define SEQ_X 0x4
+#define SEQ_Y 0x0
+#define SEQ_Z 0x1
+
+/* decode a single sampled bit */
+static u_int8_t miller_decode_sampled_bit(u_int32_t sampled_bit)
+{
+ switch (sampled_bit) {
+ case SEQ_X:
+ return 1;
+ break;
+ case SEQ_Z:
+ case SEQ_Y:
+ return 0;
+ break;
+ default:
+ DEBUGP("unknown sequence sample `%x' ", sampled_bit);
+ return 2;
+ break;
+ }
+}
+
+/* decode a single 32bit data sample of an 8bit miller encoded word */
+static int miller_decode_sample(u_int32_t sample, u_int8_t *data)
+{
+ u_int8_t ret = 0;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(sample)/OVERSAMPLING_RATE; i++) {
+ u_int8_t bit = miller_decode_sampled_bit(sample & 0xf);
+
+ if (bit == 1)
+ ret |= 1;
+ /* else do nothing since ret was initialized with 0 */
+
+ /* skip shifting in case of last data bit */
+ if (i == sizeof(sample)/OVERSAMPLING_RATE)
+ break;
+
+ sample = sample >> OVERSAMPLING_RATE;
+ ret = ret << 1;
+ }
+
+ *data = ret;
+
+ return ret;
+}
+
+static u_int32_t get_next_bytesample(struct decoder_state *ms,
+ u_int8_t *parity_sample)
+{
+ u_int32_t ret = 0;
+
+ /* get remaining bits from the current word */
+ ret = *(ms->buf32) >> ms->bit_ofs;
+ /* move to next word */
+ ms->buf32++;
+
+ /* if required, get remaining bits from next word */
+ if (ms->bit_ofs)
+ ret |= *(ms->buf32) << (32 - ms->bit_ofs);
+
+ *parity_sample = (*(ms->buf32) >> ms->bit_ofs & 0xf);
+
+ /* increment bit offset (modulo 32) */
+ ms->bit_ofs = (ms->bit_ofs + OVERSAMPLING_RATE) % 32;
+
+ return ret;
+}
+
+static struct decoder_algo miller_decoder = {
+ .oversampling_rate = OVERSAMPLING_RATE,
+ .bits_per_sampled_char = 9 * OVERSAMPLING_RATE,
+ .bytesample_mask = 0xffffffff,
+ .decode_sample = &miller_decode_sample,
+ .get_next_bytesample = &get_next_bytesample,
+};
diff --git a/firmware/src/picc/decoder_nrzl.c b/firmware/src/picc/decoder_nrzl.c
new file mode 100644
index 0000000..a1b79aa
--- /dev/null
+++ b/firmware/src/picc/decoder_nrzl.c
@@ -0,0 +1,91 @@
+/* ISO 14443 B Rx (PCD->PICC) implementation
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * speed(kbps) 106 212 424 848
+ * etu 128/fc 64/fc 32/fc 16/fc
+ * etu(usec) 9.4 4.7 2.35 1.18
+ *
+ * NRZ-L coding with logic level
+ *
+ * logic 1: carrier high field amplitude (no modulation)
+ * logic 0: carrier low field amplitude
+ *
+ * Character transmission format:
+ * start bit: logic 0
+ * data: eight bits, lsb first
+ * stop bit logic 1
+ *
+ * Frame Format:
+ *
+ * SOF char [EGT char, ...] EOF
+ *
+ * SOF: falling edge, 10..11 etu '0', rising edge in 1etu, 2..3etu '1'
+ * EGT: between 0 and 57uS
+ * EOF: falling edge, 10..11 etu '0', rising edge in 1etu
+ *
+ *
+ * Sampling
+ * - sample once per bit clock, exactly in the middle of it
+ * - synchronize CARRIER_DIV TC0 to first falling edge
+ * - Configure CARRIER_DIV RA compare (rising edge) to be at
+ * etu/2 carrier clocks.
+ * - problem: SOF 12..14etu length, therefore we cannot specify
+ * SOF as full start condition and then sample with 10bit
+ * frames :(
+ *
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <os/dbgu.h>
+#include <picc/decoder.h>
+
+/* currently this code will only work with oversampling_rate == 1 */
+#define OVERSAMPLING_RATE 1
+
+static u_int32_t get_next_bytesample(struct decoder_state *st,
+ u_int8_t *parity_sample)
+{
+ u_int32_t ret = 0;
+ u_int8_t bits_per_sampled_char = st->algo->bits_per_sampled_char;
+ u_int8_t bytesample_mask = st->algo->bytesample_mask;
+
+ /* FIXME: shift start and stop bit into parity_sample and just
+ * return plain 8-bit data word */
+
+ /* first part of 10-databit bytesample */
+ ret = (*(st->buf32) >> st->bit_ofs) & bytesample_mask;
+
+ if (st->bit_ofs > 32 - bits_per_sampled_char) {
+ /* second half of 10-databit bytesample */
+ st->buf32++;
+ ret |= (*(st->buf32) << (32 - st->bit_ofs));
+ }
+ st->bit_ofs = (st->bit_ofs + bits_per_sampled_char) % 32;
+
+ return ret & bytesample_mask;
+}
+
+static int nrzl_decode_sample(const u_int32_t sample, u_int8_t *data)
+{
+ *data = (sample >> 1) & 0xff;
+
+ if (!(sample & 0x01)) {
+ DEBUGPCRF("invalid start bit 0!");
+ return -EIO;
+ }
+ if (sample & 0x20) {
+ DEBUGPCRF("invalid stop bit 1!");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static struct decoder_algo nrzl_decoder = {
+ .oversampling_rate = OVERSAMPLING_RATE,
+ .bits_per_sampled_char = 10 * OVERSAMPLING_RATE,
+ .bytesample_mask = 0x3ff,
+ .decode_sample = &nrzl_decode_sample,
+ .get_next_bytesample = &get_next_bytesample,
+};
diff --git a/firmware/src/picc/iso14443a_manchester.c b/firmware/src/picc/iso14443a_manchester.c
new file mode 100644
index 0000000..aca832c
--- /dev/null
+++ b/firmware/src/picc/iso14443a_manchester.c
@@ -0,0 +1,106 @@
+/*
+ * ISO14443A Manchester encoder
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * Definitions for 106kBps, at sampling clock 1695kHz
+ *
+ * bit sample pattern for one bit cycle
+ * MSB first LSB first hex LSB first
+ * Sequence D 1010101000000000 0000000001010101 0x0055
+ * Sequence E 0000000010101010 0101010100000000 0x5500
+ * Sequence F 1010101010101010 0101010101010101 0x5555
+ *
+ * Logic 1 Sequence D
+ * Logic 0 Sequence E
+ * SOF Sequence D
+ * EOF Sequence F
+ *
+ * 212/424/848kBps: BPSK.
+ *
+ * SOF: 32 subcarrier clocks + bit '0'
+ *
+ * SOF: hex LSB first: 0x55555555 55555555 + bit '0'
+ *
+ * EOF: even parity of last byte (!)
+ *
+ */
+
+#define MANCHESTER_SEQ_D 0x0055
+#define MANCHESTER_SEQ_E 0x5500
+#define MANCHESTER_SEQ_F 0x5555
+
+static u_int32_t manchester_sample_size(u_int8_t frame_bytelen)
+{
+ /* 16 bits (2 bytes) per bit => 16 bytes samples per data byte,
+ * plus 16bit (2 bytes) parity per data byte
+ * plus 16bit (2 bytes) SOF plus 16bit (2 bytes) EOF */
+ return (frame_bytelen*18) + 2 + 2;
+
+ /* this results in a maximum samples-per-frame size of 4612 bytes
+ * for a 256byte frame */
+}
+
+struct manch_enc_state {
+ const char *data;
+ char *samples;
+ u_int16_t *samples16;
+};
+
+static void manchester_enc_byte(struct manch_enc_state *mencs, u_int8_t data)
+{
+ int i;
+ u_int8_t sum_1 = 0;
+
+ /* append 8 sample blobs, one for each bit */
+ for (i = 0; i < 8; i++) {
+ if (data & (1 << i)) {
+ *(mencs->samples16) = MANCHESTER_SEQ_D;
+ sum_1++;
+ } else {
+ *(mencs->samples16) = MANCHESTER_SEQ_E;
+ }
+ mencs->samples16++
+ }
+ /* append odd parity */
+ if (sum_1 & 0x01)
+ *(mencs->samples16) = MANCHESTER_SEQ_E;
+ else
+ *(mencs->samples16) = MANCHESTER_SEQ_D;
+ mencs->samples16++
+}
+
+int manchester_encode(char *sample_buf, u_int16_t sample_buf_len,
+ const char *data, u_int8_t data_len)
+{
+ int i, enc_size;
+ struct manch_enc_state mencs
+
+ enc_size = manchester_sample_size(data_len);
+
+ if (sample_buf_len < enc_size)
+ return -EINVAL;
+
+ /* SOF */
+ *(mencs.samples16++) = MANCHESTER_SEQ_D;
+
+ for (i = 0; i < data_len; i++)
+ manchester_enc_byte(mencs, data[i]);
+
+ /* EOF */
+ *(mencs.samples16++) = MANCHESTER_SEQ_F;
+
+ return enc_size;
+}
+
+#define BPSK_SPEED_212
+
+
+static u_int32_t bpsk_sample_size(u_int8_t frame_bytelen)
+
+int bpsk_encode(char *sample_buf, u_int16_t sample_buf_len,
+ const char *data, u_int8_t data_len)
+{
+ /* burst of 32 sub carrier cycles */
+ memset(sample_buf, 0x55, 8);
+
+}
diff --git a/firmware/src/picc/load_modulation.c b/firmware/src/picc/load_modulation.c
new file mode 100644
index 0000000..65c59d1
--- /dev/null
+++ b/firmware/src/picc/load_modulation.c
@@ -0,0 +1,29 @@
+#include <sys/types.h>
+#include <lib_AT91SAM7.h>
+
+#include "../openpcd.h"
+
+void load_mod_level(u_int8_t level)
+{
+ if (level > 3)
+ level = 3;
+
+ if (level & 0x1)
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD1);
+ else
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD1);
+
+ if (level & 0x2)
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD2);
+ else
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD2);
+}
+
+void load_mod_init(void)
+{
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD1);
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD2);
+
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD1);
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_LOAD2);
+}
diff --git a/firmware/src/picc/load_modulation.h b/firmware/src/picc/load_modulation.h
new file mode 100644
index 0000000..71f9d6f
--- /dev/null
+++ b/firmware/src/picc/load_modulation.h
@@ -0,0 +1,7 @@
+#ifndef _LOAD_MODULATION_H
+#define _LOAD_MODULATION_H
+
+extern void load_mod_level(u_int8_t level);
+extern void load_mod_init(void);
+
+#endif
diff --git a/firmware/src/picc/main_openpicc.c b/firmware/src/picc/main_openpicc.c
new file mode 100644
index 0000000..312194b
--- /dev/null
+++ b/firmware/src/picc/main_openpicc.c
@@ -0,0 +1,215 @@
+#include <errno.h>
+#include <include/lib_AT91SAM7.h>
+#include <include/openpcd.h>
+#include <os/dbgu.h>
+#include <os/led.h>
+#include <os/pcd_enumerate.h>
+#include <os/usb_handler.h>
+#include "../openpcd.h"
+#include <os/main.h>
+#include <os/pwm.h>
+#include <os/tc_cdiv.h>
+#include <os/pio_irq.h>
+#include <picc/poti.h>
+#include <picc/pll.h>
+#include <picc/ssc_picc.h>
+#include <picc/load_modulation.h>
+
+static const u_int16_t cdivs[] = { 128, 64, 32, 16 };
+static u_int8_t cdiv_idx = 0;
+
+static u_int16_t duty_percent = 22;
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+static u_int32_t pwm_freq[] = { 105937, 211875, 423750, 847500 };
+static u_int8_t pwm_freq_idx = 0;
+
+static u_int8_t load_mod = 0;
+
+void _init_func(void)
+{
+ pio_irq_init();
+ pll_init();
+ poti_init();
+ load_mod_init();
+ tc_cdiv_init();
+ //tc_fdt_init();
+ pwm_init();
+ adc_init();
+ ssc_rx_init();
+ // ssc_tx_init();
+
+ AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPICC_PIO_BOOTLDR);
+}
+
+static void help(void)
+{
+ DEBUGPCR("q: poti decrease q: poti increase\r\n"
+ "e: poti retransmit P: PLL inhibit toggle");
+ DEBUGPCR("o: decrease duty p: increase duty\r\n"
+ "k: stop pwm l: start pwn\r\n"
+ "n: decrease freq m: incresae freq");
+ DEBUGPCR("u: PA23 const 1 y: PA23 const 0\r\n"
+ "t: PA23 PWM0 L: display PLL LOCK\r\n"
+ "{: decrease cdiv_idx }: increse cdiv idx\r\n"
+ "<: decrease cdiv_phase >: increase cdiv_phase");
+ DEBUGPCR("v: decrease load_mod b: increase load_mod\r\n"
+ "B: read button S: toggle nSLAVE_RESET\r\n"
+ "a: SSC stop s: SSC start\r\n"
+ "d: SSC mode select");
+}
+
+int _main_dbgu(char key)
+{
+ static u_int8_t poti = 64;
+ static u_int8_t pll_inh = 1;
+ static u_int8_t ssc_mode = 1;
+
+ DEBUGPCRF("main_dbgu");
+
+ switch (key) {
+ case 'q':
+ if (poti > 0)
+ poti--;
+ poti_comp_carr(poti);
+ DEBUGPCRF("Poti: %u", poti);
+ break;
+ case 'w':
+ if (poti < 127)
+ poti++;
+ poti_comp_carr(poti);
+ DEBUGPCRF("Poti: %u", poti);
+ break;
+ case 'e':
+ poti_comp_carr(poti);
+ DEBUGPCRF("Poti: %u", poti);
+ break;
+ case 'P':
+ pll_inh++;
+ pll_inh &= 0x01;
+ pll_inhibit(pll_inh);
+ DEBUGPCRF("PLL Inhibit: %u", pll_inh);
+ break;
+ case 'L':
+ DEBUGPCRF("PLL Lock: %u", pll_is_locked());
+ break;
+ case 'o':
+ if (duty_percent >= 1)
+ duty_percent--;
+ pwm_duty_set_percent(0, duty_percent);
+ break;
+ case 'p':
+ if (duty_percent <= 99)
+ duty_percent++;
+ pwm_duty_set_percent(0, duty_percent);
+ break;
+ case 'k':
+ pwm_stop(0);
+ break;
+ case 'l':
+ pwm_start(0);
+ break;
+ case 'n':
+ if (pwm_freq_idx > 0) {
+ pwm_freq_idx--;
+ pwm_stop(0);
+ pwm_freq_set(0, pwm_freq[pwm_freq_idx]);
+ pwm_start(0);
+ pwm_duty_set_percent(0, 22); /* 22% of 9.43uS = 2.07uS */
+ }
+ break;
+ case 'm':
+ if (pwm_freq_idx < ARRAY_SIZE(pwm_freq)-1) {
+ pwm_freq_idx++;
+ pwm_stop(0);
+ pwm_freq_set(0, pwm_freq[pwm_freq_idx]);
+ pwm_start(0);
+ pwm_duty_set_percent(0, 22); /* 22% of 9.43uS = 2.07uS */
+ }
+ break;
+ case 'u':
+ DEBUGPCRF("PA23 output high");
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ break;
+ case 'y':
+ DEBUGPCRF("PA23 output low");
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, AT91C_PIO_PA23);
+ break;
+ case 't':
+ DEBUGPCRF("PA23 PeriphA (PWM)");
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, 0, AT91C_PA23_PWM0);
+ break;
+ case '?':
+ help();
+ break;
+ case '<':
+ tc_cdiv_phase_inc();
+ break;
+ case '>':
+ tc_cdiv_phase_dec();
+ break;
+ case '{':
+ if (cdiv_idx > 0)
+ cdiv_idx--;
+ tc_cdiv_set_divider(cdivs[cdiv_idx]);
+ break;
+ case '}':
+ if (cdiv_idx < ARRAY_SIZE(cdivs)-1)
+ cdiv_idx++;
+ tc_cdiv_set_divider(cdivs[cdiv_idx]);
+ break;
+ case 'v':
+ if (load_mod > 0)
+ load_mod--;
+ load_mod_level(load_mod);
+ DEBUGPCR("load_mod: %u\n", load_mod);
+ break;
+ case 'b':
+ if (load_mod < 3)
+ load_mod++;
+ load_mod_level(load_mod);
+ DEBUGPCR("load_mod: %u\n", load_mod);
+ break;
+ case 'B':
+ DEBUGPCRF("Button status: %u\n",
+ AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, AT91F_PIO_IsInputSet));
+ break;
+ case 'S':
+ if (AT91F_PIO_IsOutputSet(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET)) {
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET);
+ DEBUGPCRF("nSLAVE_RESET == LOW");
+ } else {
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET);
+ DEBUGPCRF("nSLAVE_RESET == HIGH");
+ }
+ break;
+ case 'a':
+ DEBUGPCRF("SSC RX STOP");
+ ssc_rx_stop();
+ break;
+ case 's':
+ DEBUGPCRF("SSC RX START");
+ ssc_rx_start();
+ break;
+ case 'd':
+ ssc_rx_mode_set(++ssc_mode);
+ DEBUGPCRF("SSC MODE %u", ssc_mode);
+ break;
+ }
+
+ tc_cdiv_print();
+
+ return -EINVAL;
+}
+
+void _main_func(void)
+{
+ /* first we try to get rid of pending to-be-sent stuff */
+ usb_out_process();
+
+ /* next we deal with incoming reqyests from USB EP1 (OUT) */
+ usb_in_process();
+
+ ssc_rx_unthrottle();
+}
diff --git a/firmware/src/picc/piccsim.h b/firmware/src/picc/piccsim.h
new file mode 100644
index 0000000..3cf6514
--- /dev/null
+++ b/firmware/src/picc/piccsim.h
@@ -0,0 +1,23 @@
+
+#include <librfid/rfid_layer2_iso14443a.h>
+
+struct piccsim_state {
+ enum rfid_layer2_id l2prot;
+ unsigned char uid[10];
+ u_int8_t uid_len;
+ union {
+ struct {
+ enum iso14443a_state state;
+ enum iso14443a_level level;
+ u_int32_t flags;
+ } iso14443a;
+ struct {
+ } iso14443b;
+ } l2;
+
+ union {
+ u_int32_t flags;
+ } proto;
+}
+
+#define PICCSIM_PROT_F_AUTO_WTX 0x01
diff --git a/firmware/src/picc/pll.c b/firmware/src/picc/pll.c
new file mode 100644
index 0000000..bed08ef
--- /dev/null
+++ b/firmware/src/picc/pll.c
@@ -0,0 +1,40 @@
+
+#include <sys/types.h>
+#include <lib_AT91SAM7.h>
+#include <os/pio_irq.h>
+#include <os/dbgu.h>
+#include "../openpcd.h"
+
+void pll_inhibit(int inhibit)
+{
+ if (inhibit)
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_PLL_INHIBIT);
+ else
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_PLL_INHIBIT);
+}
+
+int pll_is_locked(void)
+{
+ return AT91F_PIO_IsInputSet(AT91C_BASE_PIOA, OPENPICC_PIO_PLL_LOCK);
+}
+
+static void pll_lock_change_cb(u_int32_t pio)
+{
+ DEBUGPCRF("PLL LOCK: %d", pll_is_locked());
+#if 1
+ if (pll_is_locked())
+ led_switch(1, 1);
+ else
+ led_switch(1, 0);
+#endif
+}
+
+void pll_init(void)
+{
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_PLL_INHIBIT);
+ AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPICC_PIO_PLL_LOCK);
+ pll_inhibit(0);
+
+ pio_irq_register(OPENPICC_PIO_PLL_LOCK, &pll_lock_change_cb);
+ pio_irq_enable(OPENPICC_PIO_PLL_LOCK);
+}
diff --git a/firmware/src/picc/pll.h b/firmware/src/picc/pll.h
new file mode 100644
index 0000000..fc00105
--- /dev/null
+++ b/firmware/src/picc/pll.h
@@ -0,0 +1,8 @@
+#ifndef _PLL_H
+#define _PLL_H
+
+extern int pll_is_locked(void);
+extern void pll_inhibit(int inhibit);
+extern void pll_init(void);
+
+#endif
diff --git a/firmware/src/picc/poti.c b/firmware/src/picc/poti.c
new file mode 100644
index 0000000..2cc8e5c
--- /dev/null
+++ b/firmware/src/picc/poti.c
@@ -0,0 +1,58 @@
+/* SPI Potentiometer AD 7367 Driver for OpenPICC
+ * (C) by Harald Welte <hwelte@hmw-consulting.de>
+ */
+
+#include <sys/types.h>
+#include <lib_AT91SAM7.h>
+#include "../openpcd.h"
+
+static const AT91PS_SPI spi = AT91C_BASE_SPI;
+
+void poti_comp_carr(u_int8_t position)
+{
+ volatile int i;
+
+ while (!(spi->SPI_SR & AT91C_SPI_TDRE)) { }
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SS2_DT_THRESH);
+ //for (i = 0; i < 0xff; i++) { }
+ /* shift one left, since it is a seven-bit value written as 8 bit xfer */
+ spi->SPI_TDR = position & 0x7f;
+ while (!(spi->SPI_SR & AT91C_SPI_TDRE)) { }
+ for (i = 0; i < 0xff; i++) { }
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SS2_DT_THRESH);
+}
+
+void poti_reset(void)
+{
+ volatile int i;
+ AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET);
+ for (i = 0; i < 0xff; i++) { }
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET);
+}
+
+void poti_init(void)
+{
+ AT91F_SPI_CfgPMC();
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA,
+ AT91C_PA13_MOSI | AT91C_PA14_SPCK, 0);
+
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SS2_DT_THRESH);
+ AT91F_PIO_SetOutput(AT91C_BASE_PIOA, OPENPICC_PIO_SS2_DT_THRESH);
+
+ AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, OPENPICC_PIO_nSLAVE_RESET);
+ poti_reset();
+
+#if 0
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_SPI,
+ OPENPCD_IRQ_PRIO_SPI,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &spi_irq);
+ AT91G_AIC_EnableIt(AT9C_BASE_AIC, AT91C_ID_SPI);
+#endif
+ AT91F_SPI_CfgMode(spi, AT91C_SPI_MSTR |
+ AT91C_SPI_PS_FIXED | AT91C_SPI_MODFDIS);
+ /* CPOL = 0, NCPHA = 1, CSAAT = 0, BITS = 0000, SCBR = 13 (3.69MHz),
+ * DLYBS = 6 (125nS), DLYBCT = 0 */
+ AT91F_SPI_CfgCs(spi, 0, AT91C_SPI_BITS_8 | AT91C_SPI_NCPHA |
+ (13 << 8) | (6 << 16));
+ AT91F_SPI_Enable(spi);
+}
diff --git a/firmware/src/picc/poti.h b/firmware/src/picc/poti.h
new file mode 100644
index 0000000..92ec00d
--- /dev/null
+++ b/firmware/src/picc/poti.h
@@ -0,0 +1,7 @@
+#ifndef _POTI_H
+#define _POTI_H
+
+extern void poti_comp_carr(u_int8_t position);
+extern void poti_init(void);
+
+#endif
diff --git a/firmware/src/picc/ssc_picc.c b/firmware/src/picc/ssc_picc.c
new file mode 100644
index 0000000..fac2c62
--- /dev/null
+++ b/firmware/src/picc/ssc_picc.c
@@ -0,0 +1,384 @@
+/* AT91SAM7 SSC controller routines for OpenPCD
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * 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 <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <AT91SAM7.h>
+#include <lib_AT91SAM7.h>
+#include <openpcd.h>
+
+#include <os/usb_handler.h>
+#include <os/dbgu.h>
+#include "../openpcd.h"
+
+/* definitions for four-times oversampling */
+#define REQA 0x10410441
+#define WUPA 0x04041041
+
+static const AT91PS_SSC ssc = AT91C_BASE_SSC;
+static AT91PS_PDC rx_pdc;
+
+enum ssc_mode {
+ SSC_MODE_NONE,
+ SSC_MODE_14443A_SHORT,
+ SSC_MODE_14443A_STANDARD,
+ SSC_MODE_14443B,
+ SSC_MODE_EDGE_ONE_SHOT,
+};
+
+struct ssc_state {
+ struct req_ctx *rx_ctx[2];
+ enum ssc_mode mode;
+};
+static struct ssc_state ssc_state;
+
+/* This is for four-times oversampling */
+#define ISO14443A_SOF_SAMPLE 0x08
+#define ISO14443A_SOF_LEN 4
+
+void ssc_rx_mode_set(enum ssc_mode ssc_mode)
+{
+ u_int8_t data_len, num_data, sync_len;
+ u_int32_t start_cond;
+
+ /* disable Rx */
+ ssc->SSC_CR = AT91C_SSC_RXDIS;
+
+ /* disable all Rx related interrupt sources */
+ AT91F_SSC_DisableIt(ssc, AT91C_SSC_RXRDY |
+ AT91C_SSC_OVRUN | AT91C_SSC_ENDRX |
+ AT91C_SSC_RXBUFF | AT91C_SSC_RXSYN |
+ AT91C_SSC_CP0 | AT91C_SSC_CP1);
+
+ 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 = 32;
+ num_data = 1;
+ 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 = 1;
+ num_data = 1; /* FIXME */
+ break;
+ case SSC_MODE_14443B:
+ /* start sampling at first falling data edge */
+ //start_cond =
+ break;
+ case SSC_MODE_EDGE_ONE_SHOT:
+ //start_cond = AT91C_SSC_START_EDGE_RF;
+ start_cond = AT91C_SSC_START_CONTINOUS;
+ sync_len = 0;
+ data_len = 8;
+ num_data = 50;
+ break;
+ default:
+ return;
+ }
+ ssc->SSC_RFMR = (data_len-1) & 0x1f |
+ (((num_data-1) & 0x0f) << 8) |
+ (((sync_len-1) & 0x0f) << 16);
+ ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE | start_cond;
+
+ AT91F_PDC_EnableRx(rx_pdc);
+ /* Enable RX interrupts */
+ AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN |
+ AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF);
+
+ ssc_state.mode = ssc_mode;
+
+}
+
+static void ssc_tx_mode_set(enum ssc_mode ssc_mode)
+{
+ u_int8_t data_len, num_data, sync_len;
+ u_int32_t start_cond;
+
+ /* disable Tx */
+ ssc->SSC_CR = AT91C_SSC_TXDIS;
+
+ /* disable all Tx related interrupt sources */
+ ssc->SSC_IDR = AT91C_SSC_TXRDY | AT91C_SSC_TXEMPTY | AT91C_SSC_ENDTX |
+ AT91C_SSC_TXBUFE | AT91C_SSC_TXSYN;
+
+ switch (ssc_mode) {
+ case SSC_MODE_14443A_SHORT:
+ start_cond = AT91C_SSC_START_RISE_RF;
+ sync_len = ISO14443A_SOF_LEN;
+ data_len = 32;
+ num_data = 1;
+ 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 = 1;
+ num_data = 1; /* FIXME */
+ break;
+ }
+ ssc->SSC_TFMR = (data_len-1) & 0x1f |
+ (((num_data-1) & 0x0f) << 8) |
+ (((sync_len-1) & 0x0f) << 16);
+ ssc->SSC_TCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE | start_cond;
+
+#if 0
+ /* Enable RX interrupts */
+ AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN |
+ AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF);
+ AT91F_PDC_EnableRx(rx_pdc);
+
+ ssc_state.mode = ssc_mode;
+#endif
+}
+
+
+
+
+static struct openpcd_hdr opcd_ssc_hdr = {
+ .cmd = OPENPCD_CMD_SSC_READ,
+};
+
+static inline void init_opcdhdr(struct req_ctx *rctx)
+{
+ memcpy(&rctx->tx.data[0], &opcd_ssc_hdr, sizeof(opcd_ssc_hdr));
+ rctx->tx.tot_len = MAX_HDRSIZE + MAX_REQSIZE -1;
+}
+
+#ifdef DEBUG_SSC_REFILL
+#define DEBUGR(x, args ...) DEBUGPCRF(x, ## args)
+#else
+#define DEBUGR(x, args ...)
+#endif
+
+static char dmabuf1[512];
+static char dmabuf2[512];
+
+/* Try to refill RX dma descriptors. Return values:
+ * 0) no dma descriptors empty
+ * 1) filled next/secondary descriptor
+ * 2) filled both primary and secondary descriptor
+ * -1) no free request contexts to use
+ * -2) only one free request context, but two free descriptors
+ */
+static int8_t ssc_rx_refill(void)
+{
+#if 1
+ struct req_ctx *rctx;
+
+ rctx = req_ctx_find_get(RCTX_STATE_FREE, RCTX_STATE_SSC_RX_BUSY);
+ if (!rctx) {
+ DEBUGPCRF("no rctx for refill!");
+ return -1;
+ }
+ init_opcdhdr(rctx);
+
+ if (AT91F_PDC_IsRxEmpty(rx_pdc)) {
+ DEBUGR("filling primary SSC RX dma ctx");
+ AT91F_PDC_SetRx(rx_pdc, &rctx->rx.data[MAX_HDRSIZE],
+ (sizeof(rctx->rx.data)-MAX_HDRSIZE)>>2);
+ ssc_state.rx_ctx[0] = rctx;
+
+ /* If primary is empty, secondary must be empty, too */
+ rctx = req_ctx_find_get(RCTX_STATE_FREE,
+ RCTX_STATE_SSC_RX_BUSY);
+ if (!rctx) {
+ DEBUGPCRF("no rctx for secondary refill!");
+ return -2;
+ }
+ init_opcdhdr(rctx);
+ }
+
+ if (AT91F_PDC_IsNextRxEmpty(rx_pdc)) {
+ DEBUGR("filling secondary SSC RX dma ctx");
+ AT91F_PDC_SetNextRx(rx_pdc, &rctx->rx.data[MAX_HDRSIZE],
+ (sizeof(rctx->rx.data)-MAX_HDRSIZE)>2);
+ ssc_state.rx_ctx[1] = rctx;
+ return 2;
+ } else {
+ /* we were unable to fill*/
+ DEBUGPCRF("prim/secnd DMA busy, can't refill");
+ req_ctx_put(rctx);
+ return 0;
+ }
+#else
+ if (AT91F_PDC_IsRxEmpty(rx_pdc))
+ AT91F_PDC_SetRx(rx_pdc, dmabuf1, sizeof(dmabuf1)>>2);
+
+ if (AT91F_PDC_IsNextRxEmpty(rx_pdc))
+ AT91F_PDC_SetNextRx(rx_pdc, dmabuf2, sizeof(dmabuf2)>>2);
+ else
+ DEBUGPCRF("prim/secnd DMA busy, can't refill");
+#endif
+}
+
+#define ISO14443A_FDT_SHORT_1 1236
+#define ISO14443A_FDT_SHORT_0 1172
+
+static void ssc_irq(void)
+{
+ u_int32_t ssc_sr = ssc->SSC_SR;
+ DEBUGP("ssc_sr=0x%08x, mode=%u: ", ssc_sr, ssc_state.mode);
+
+ if (ssc_sr & AT91C_SSC_OVRUN)
+ DEBUGP("RX OVERRUN ");
+
+ switch (ssc_state.mode) {
+ case SSC_MODE_14443A_SHORT:
+ if (ssc_sr & AT91C_SSC_RXSYN)
+ DEBUGP("RXSYN ");
+ if (ssc_sr & AT91C_SSC_RXRDY) {
+ u_int32_t sample = ssc->SSC_RHR;
+ DEBUGP("RXRDY=0x%08x ", sample);
+ /* Try to set FDT compare register ASAP */
+ if (sample == REQA) {
+ tc_fdt_set(ISO14443A_FDT_SHORT_0);
+ /* FIXME: prepare and configure ATQA response */
+ } else if (sample == WUPA) {
+ tc_fdt_set(ISO14443A_FDT_SHORT_1);
+ /* FIXME: prepare and configure ATQA response */
+ } else
+ DEBUGP("<== unknown ");
+ }
+ break;
+
+ case SSC_MODE_14443A_STANDARD:
+ case SSC_MODE_EDGE_ONE_SHOT:
+
+ if (ssc_sr & (AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF)) {
+#if 1
+ /* Mark primary RCTX as ready to send for usb */
+ req_ctx_set_state(ssc_state.rx_ctx[0],
+ RCTX_STATE_UDP_EP2_PENDING);
+ /* second buffer gets propagated to primary */
+ ssc_state.rx_ctx[0] = ssc_state.rx_ctx[1];
+ ssc_state.rx_ctx[1] = NULL;
+#endif
+
+ if (ssc_sr & AT91C_SSC_RXBUFF) {
+ DEBUGP("RXBUFF, shouldn't happen! ");
+#if 0
+ req_ctx_set_state(ssc_state.rx_ctx[0],
+ RCTX_STATE_UDP_EP2_PENDING);
+#endif
+ }
+ if (ssc_rx_refill() == -1)
+ AT91F_AIC_DisableIt(ssc, AT91C_SSC_ENDRX |
+ AT91C_SSC_RXBUFF |
+ AT91C_SSC_OVRUN);
+ }
+ break;
+ }
+ DEBUGPCR("");
+ AT91F_AIC_ClearIt(AT91C_BASE_AIC, AT91C_ID_SSC);
+}
+
+
+void ssc_rx_unthrottle(void)
+{
+ AT91F_SSC_EnableIt(ssc, AT91C_SSC_ENDRX |
+ AT91C_SSC_RXBUFF | AT91C_SSC_OVRUN);
+}
+
+void ssc_rx_start(void)
+{
+ DEBUGPCRF("starting SSC RX\n");
+
+ /* Enable Reception */
+ AT91F_SSC_EnableRx(ssc);
+}
+
+void ssc_rx_stop(void)
+{
+ /* Disable reception */
+ AT91F_SSC_DisableRx(ssc);
+}
+
+void ssc_tx_init(void)
+{
+ /* IMPORTANT: Disable PA23 (PWM0) output, since it is connected to
+ * PA17 !! */
+ AT91F_PIO_CfgInput(AT91C_BASE_PIOA, OPENPCD_PIO_MFIN_PWM);
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, OPENPCD_PIO_MFIN_SSC_TX |
+ OPENPCD_PIO_MFOUT_SSC_RX | OPENPCD_PIO_SSP_CKIN,
+ 0);
+}
+
+static int ssc_usb_in(struct req_ctx *rctx)
+{
+ struct openpcd_hdr *poh = (struct openpcd_hdr *) &rctx->rx.data[0];
+ struct openpcd_hdr *pih = (struct openpcd_hdr *) &rctx->tx.data[0];
+
+ switch (poh->cmd) {
+ case OPENPCD_CMD_SSC_READ:
+ /* FIXME: allow host to specify mode */
+ ssc_rx_mode_set(SSC_MODE_EDGE_ONE_SHOT);
+ ssc_rx_start();
+ break;
+ case OPENPCD_CMD_SSC_WRITE:
+ /* FIXME: implement this */
+ //ssc_tx_start()
+ break;
+ }
+
+ req_ctx_put(rctx);
+ return -EINVAL;
+}
+
+void ssc_rx_init(void)
+{
+ rx_pdc = (AT91PS_PDC) &(ssc->SSC_RPR);
+
+ AT91F_SSC_CfgPMC();
+
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA,
+ OPENPCD_PIO_MFOUT_SSC_RX | OPENPCD_PIO_SSP_CKIN,
+ 0);
+
+ AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_SSC,
+ OPENPCD_IRQ_PRIO_SSC,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &ssc_irq);
+ /* Reset */
+ //ssc->SSC_CR = AT91C_SSC_SWRST;
+
+ /* don't divide clock */
+ ssc->SSC_CMR = 0;
+
+ ssc->SSC_RCMR = AT91C_SSC_CKS_RK | AT91C_SSC_CKO_NONE |
+ 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);
+
+ ssc_rx_mode_set(SSC_MODE_EDGE_ONE_SHOT);
+
+ AT91F_PDC_EnableRx(rx_pdc);
+
+ /* Enable RX interrupts */
+ AT91F_SSC_EnableIt(ssc, AT91C_SSC_OVRUN |
+ AT91C_SSC_ENDRX | AT91C_SSC_RXBUFF);
+ AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_SSC);
+
+ usb_hdlr_register(&ssc_usb_in, OPENPCD_CMD_CLS_SSC);
+}
+
+void ssc_fini(void)
+{
+ usb_hdlr_unregister(OPENPCD_CMD_CLS_SSC);
+ AT91F_PDC_DisableRx(rx_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/firmware/src/picc/ssc_picc.h b/firmware/src/picc/ssc_picc.h
new file mode 100644
index 0000000..710ee45
--- /dev/null
+++ b/firmware/src/picc/ssc_picc.h
@@ -0,0 +1,15 @@
+#ifndef _SSC_H
+#define _SSC_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_unthrottle(void);
+
+#endif
diff --git a/firmware/src/picc/tc_fdt.c b/firmware/src/picc/tc_fdt.c
new file mode 100644
index 0000000..041001d
--- /dev/null
+++ b/firmware/src/picc/tc_fdt.c
@@ -0,0 +1,50 @@
+/* OpenPC TC (Timer / Clock) support code
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * PICC Simulator Side:
+ * In order to support responding to synchronous frames (REQA/WUPA/ANTICOL),
+ * we need a second Timer/Counter (TC2). This unit is reset by an external
+ * event (rising edge of modulation pause PCD->PICC) connected to TIOB2, and
+ * counts up to a configurable number of carrier clock cycles (RA). Once the
+ * RA value is reached, TIOA2 will see a rising edge. This rising edge will
+ * be interconnected to TF (Tx Frame) of the SSC to start transmitting our
+ * synchronous response.
+ *
+ */
+
+#include <lib_AT91SAM7.h>
+#include <AT91SAM7.h>
+#include <os/dbgu.h>
+
+#include "../openpcd.h"
+#include <os/tc_cdiv.h>
+#include <picc/tc_fdt.h>
+
+void tc_fdt_set(u_int16_t count)
+{
+ tcb->TCB_TC2.TC_RA = count;
+}
+
+void tc_fdt_init(void)
+{
+ AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, AT91C_PA15_TF,
+ AT91C_PA26_TIOA2 | AT91C_PA27_TIOB2);
+ AT91F_PMC_EnablePeriphClock(AT91C_BASE_PMC,
+ ((unsigned int) 1 << AT91C_ID_TC2));
+ /* Enable Clock for TC2 */
+ tcb->TCB_TC2.TC_CCR = AT91C_TC_CLKEN;
+
+ /* Clock XC1, Wave Mode, No automatic reset on RC comp
+ * TIOA2 in RA comp = set, TIOA2 on RC comp = clear,
+ * TIOB2 as input, EEVT = TIOB2, Reset/Trigger on EEVT */
+ tcb->TCB_TC2.TC_CMR = AT91C_TC_CLKS_XC1 | AT91C_TC_WAVE |
+ AT91C_TC_WAVESEL_UP |
+ AT91C_TC_ACPA_SET | AT91C_TC_ACPC_CLEAR |
+ AT91C_TC_BEEVT_NONE | AT91C_TC_BCPB_NONE |
+ AT91C_TC_EEVT_TIOB | AT91C_TC_ETRGEDG_RISING |
+ AT91C_TC_ENETRG ;
+
+ /* Reset to start timers */
+ tcb->TCB_BCR = 1;
+}
+
diff --git a/firmware/src/picc/tc_fdt.h b/firmware/src/picc/tc_fdt.h
new file mode 100644
index 0000000..b39b935
--- /dev/null
+++ b/firmware/src/picc/tc_fdt.h
@@ -0,0 +1,9 @@
+#ifndef _TC_FDT_H
+#define _TC_FDT_H
+
+#include <sys/types.h>
+
+extern void tc_fdt_init(void);
+extern void tc_fdt_set(u_int16_t count);
+
+#endif
personal git repositories of Harald Welte. Your mileage may vary