diff options
Diffstat (limited to 'firmware/src/picc')
-rw-r--r-- | firmware/src/picc/adc.c | 145 | ||||
-rw-r--r-- | firmware/src/picc/decoder.c | 65 | ||||
-rw-r--r-- | firmware/src/picc/decoder.h | 32 | ||||
-rw-r--r-- | firmware/src/picc/decoder_miller.c | 112 | ||||
-rw-r--r-- | firmware/src/picc/decoder_nrzl.c | 91 | ||||
-rw-r--r-- | firmware/src/picc/iso14443a_manchester.c | 106 | ||||
-rw-r--r-- | firmware/src/picc/load_modulation.c | 29 | ||||
-rw-r--r-- | firmware/src/picc/load_modulation.h | 7 | ||||
-rw-r--r-- | firmware/src/picc/main_openpicc.c | 215 | ||||
-rw-r--r-- | firmware/src/picc/piccsim.h | 23 | ||||
-rw-r--r-- | firmware/src/picc/pll.c | 40 | ||||
-rw-r--r-- | firmware/src/picc/pll.h | 8 | ||||
-rw-r--r-- | firmware/src/picc/poti.c | 58 | ||||
-rw-r--r-- | firmware/src/picc/poti.h | 7 | ||||
-rw-r--r-- | firmware/src/picc/ssc_picc.c | 384 | ||||
-rw-r--r-- | firmware/src/picc/ssc_picc.h | 15 | ||||
-rw-r--r-- | firmware/src/picc/tc_fdt.c | 50 | ||||
-rw-r--r-- | firmware/src/picc/tc_fdt.h | 9 |
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 |