MAJOR update, added hitag2 reader, emulation and eavesdropping, lots of new code, including FPGA tweaks, part 2

This commit is contained in:
roel@libnfc.org 2012-09-18 13:53:17 +00:00
commit d19929cbe8
18 changed files with 1355 additions and 454 deletions

View file

@ -433,17 +433,17 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol)
{
int i;
uint8_t *tab = (uint8_t *)BigBuf;
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_SIMULATOR);
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT);
AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK;
AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK;
#define SHORT_COIL() LOW(GPIO_SSC_DOUT)
#define OPEN_COIL() HIGH(GPIO_SSC_DOUT)
i = 0;
for(;;) {
while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) {
@ -453,18 +453,18 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol)
}
WDT_HIT();
}
if (ledcontrol)
LED_D_ON();
if(tab[i])
OPEN_COIL();
else
SHORT_COIL();
if (ledcontrol)
LED_D_OFF();
while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) {
if(BUTTON_PRESS()) {
DbpString("Stopped");
@ -472,7 +472,7 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol)
}
WDT_HIT();
}
i++;
if(i == period) {
i = 0;
@ -484,197 +484,9 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol)
}
}
/* Provides a framework for bidirectional LF tag communication
* Encoding is currently Hitag2, but the general idea can probably
* be transferred to other encodings.
*
* The new FPGA code will, for the LF simulator mode, give on SSC_FRAME
* (PA15) a thresholded version of the signal from the ADC. Setting the
* ADC path to the low frequency peak detection signal, will enable a
* somewhat reasonable receiver for modulation on the carrier signal
* that is generated by the reader. The signal is low when the reader
* field is switched off, and high when the reader field is active. Due
* to the way that the signal looks like, mostly only the rising edge is
* useful, your mileage may vary.
*
* Neat perk: PA15 can not only be used as a bit-banging GPIO, but is also
* TIOA1, which can be used as the capture input for timer 1. This should
* make it possible to measure the exact edge-to-edge time, without processor
* intervention.
*
* Arguments: divisor is the divisor to be sent to the FPGA (e.g. 95 for 125kHz)
* t0 is the carrier frequency cycle duration in terms of MCK (384 for 125kHz)
*
* The following defines are in carrier periods:
*/
#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */
#define HITAG_T_1_MIN 24 /* T[1] should be 26..30 */
#define HITAG_T_EOF 40 /* T_EOF should be > 36 */
#define HITAG_T_WRESP 208 /* T_wresp should be 204..212 */
static void hitag_handle_frame(int t0, int frame_len, char *frame);
//#define DEBUG_RA_VALUES 1
#define DEBUG_FRAME_CONTENTS 1
void SimulateTagLowFrequencyBidir(int divisor, int t0)
{
#if DEBUG_RA_VALUES || DEBUG_FRAME_CONTENTS
int i = 0;
#endif
char frame[10];
int frame_pos=0;
DbpString("Starting Hitag2 emulator, press button to end");
hitag2_init();
/* Set up simulator mode, frequency divisor which will drive the FPGA
* and analog mux selection.
*/
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_SIMULATOR);
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor);
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
RELAY_OFF();
/* Set up Timer 1:
* Capture mode, timer source MCK/2 (TIMER_CLOCK1), TIOA is external trigger,
* external trigger rising edge, load RA on rising edge of TIOA, load RB on rising
* edge of TIOA. Assign PA15 to TIOA1 (peripheral B)
*/
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1);
AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK |
AT91C_TC_ETRGEDG_RISING |
AT91C_TC_ABETRG |
AT91C_TC_LDRA_RISING |
AT91C_TC_LDRB_RISING;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN |
AT91C_TC_SWTRG;
/* calculate the new value for the carrier period in terms of TC1 values */
t0 = t0/2;
int overflow = 0;
while(!BUTTON_PRESS()) {
WDT_HIT();
if(AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) {
int ra = AT91C_BASE_TC1->TC_RA;
if((ra > t0*HITAG_T_EOF) | overflow) ra = t0*HITAG_T_EOF+1;
#if DEBUG_RA_VALUES
if(ra > 255 || overflow) ra = 255;
((char*)BigBuf)[i] = ra;
i = (i+1) % 8000;
#endif
if(overflow || (ra > t0*HITAG_T_EOF) || (ra < t0*HITAG_T_0_MIN)) {
/* Ignore */
} else if(ra >= t0*HITAG_T_1_MIN ) {
/* '1' bit */
if(frame_pos < 8*sizeof(frame)) {
frame[frame_pos / 8] |= 1<<( 7-(frame_pos%8) );
frame_pos++;
}
} else if(ra >= t0*HITAG_T_0_MIN) {
/* '0' bit */
if(frame_pos < 8*sizeof(frame)) {
frame[frame_pos / 8] |= 0<<( 7-(frame_pos%8) );
frame_pos++;
}
}
overflow = 0;
LED_D_ON();
} else {
if(AT91C_BASE_TC1->TC_CV > t0*HITAG_T_EOF) {
/* Minor nuisance: In Capture mode, the timer can not be
* stopped by a Compare C. There's no way to stop the clock
* in software, so we'll just have to note the fact that an
* overflow happened and the next loaded timer value might
* have wrapped. Also, this marks the end of frame, and the
* still running counter can be used to determine the correct
* time for the start of the reply.
*/
overflow = 1;
if(frame_pos > 0) {
/* Have a frame, do something with it */
#if DEBUG_FRAME_CONTENTS
((char*)BigBuf)[i++] = frame_pos;
memcpy( ((char*)BigBuf)+i, frame, 7);
i+=7;
i = i % sizeof(BigBuf);
#endif
hitag_handle_frame(t0, frame_pos, frame);
memset(frame, 0, sizeof(frame));
}
frame_pos = 0;
}
LED_D_OFF();
}
}
DbpString("All done");
}
static void hitag_send_bit(int t0, int bit) {
if(bit == 1) {
/* Manchester: Loaded, then unloaded */
LED_A_ON();
SHORT_COIL();
while(AT91C_BASE_TC1->TC_CV < t0*15);
OPEN_COIL();
while(AT91C_BASE_TC1->TC_CV < t0*31);
LED_A_OFF();
} else if(bit == 0) {
/* Manchester: Unloaded, then loaded */
LED_B_ON();
OPEN_COIL();
while(AT91C_BASE_TC1->TC_CV < t0*15);
SHORT_COIL();
while(AT91C_BASE_TC1->TC_CV < t0*31);
LED_B_OFF();
}
AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; /* Reset clock for the next bit */
}
static void hitag_send_frame(int t0, int frame_len, const char const * frame, int fdt)
{
OPEN_COIL();
AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
/* Wait for HITAG_T_WRESP carrier periods after the last reader bit,
* not that since the clock counts since the rising edge, but T_wresp is
* with respect to the falling edge, we need to wait actually (T_wresp - T_g)
* periods. The gap time T_g varies (4..10).
*/
while(AT91C_BASE_TC1->TC_CV < t0*(fdt-8));
int saved_cmr = AT91C_BASE_TC1->TC_CMR;
AT91C_BASE_TC1->TC_CMR &= ~AT91C_TC_ETRGEDG; /* Disable external trigger for the clock */
AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; /* Reset the clock and use it for response timing */
int i;
for(i=0; i<5; i++)
hitag_send_bit(t0, 1); /* Start of frame */
for(i=0; i<frame_len; i++) {
hitag_send_bit(t0, !!(frame[i/ 8] & (1<<( 7-(i%8) ))) );
}
OPEN_COIL();
AT91C_BASE_TC1->TC_CMR = saved_cmr;
}
/* Callback structure to cleanly separate tag emulation code from the radio layer. */
static int hitag_cb(const char* response_data, const int response_length, const int fdt, void *cb_cookie)
{
hitag_send_frame(*(int*)cb_cookie, response_length, response_data, fdt);
return 0;
}
/* Frame length in bits, frame contents in MSBit first format */
static void hitag_handle_frame(int t0, int frame_len, char *frame)
{
hitag2_handle_command(frame, frame_len, hitag_cb, &t0);
}
// compose fc/8 fc/10 waveform