X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/0422e2a47109a7845998c2130ae40c6d04e56d09..6949aca9fa0e37539fc277bac78e3d7a22117467:/armsrc/lfops.c diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 6ac4e725..c2d908df 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -6,6 +6,7 @@ //----------------------------------------------------------------------------- #include #include "apps.h" +#include "hitag2.h" #include "../common/crc16.c" void AcquireRawAdcSamples125k(BOOL at134khz) @@ -41,12 +42,12 @@ void DoAcquisition125k(BOOL at134khz) memset(dest,0,n); i = 0; for(;;) { - if(SSC_STATUS & (SSC_STATUS_TX_READY)) { - SSC_TRANSMIT_HOLDING = 0x43; + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0x43; LED_D_ON(); } - if(SSC_STATUS & (SSC_STATUS_RX_READY)) { - dest[i] = (BYTE)SSC_RECEIVE_HOLDING; + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + dest[i] = (BYTE)AT91C_BASE_SSC->SSC_RHR; i++; LED_D_OFF(); if(i >= n) { @@ -61,6 +62,10 @@ void ModThenAcquireRawAdcSamples125k(int delay_off,int period_0,int period_1,BYT { BOOL at134khz; + /* Make sure the tag is reset */ + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(2500); + // see if 'h' was specified if(command[strlen((char *) command) - 1] == 'h') at134khz= TRUE; @@ -77,6 +82,8 @@ void ModThenAcquireRawAdcSamples125k(int delay_off,int period_0,int period_1,BYT // Give it a bit of time for the resonant antenna to settle. SpinDelay(50); + // And a little more time for the tag to fully power up + SpinDelay(2000); // Now set up the SSC to get the ADC samples that are now streaming at us. FpgaSetupSsc(); @@ -95,11 +102,12 @@ void ModThenAcquireRawAdcSamples125k(int delay_off,int period_0,int period_1,BYT FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); } LED_D_ON(); - if(*(command++) == '0') + if(*(command++) == '0') { SpinDelayUs(period_0); - else + } else { SpinDelayUs(period_1); } + } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelayUs(delay_off); @@ -115,66 +123,146 @@ void ModThenAcquireRawAdcSamples125k(int delay_off,int period_0,int period_1,BYT DoAcquisition125k(at134khz); } -void AcquireTiType(void) -{ - int i; - // tag transmission is <20ms, sampling at 2M gives us 40K samples max - // each sample is 1 bit stuffed into a DWORD so we need 1250 DWORDS - int n = 1250; +/* blank r/w tag data stream +...0000000000000000 01111111 +1010101010101010101010101010101010101010101010101010101010101010 +0011010010100001 +01111111 +101010101010101[0]000... - // clear buffer - DbpIntegers((DWORD)BigBuf, sizeof(BigBuf), 0x12345678); - memset(BigBuf,0,sizeof(BigBuf)); - - // Set up the synchronous serial port - PIO_DISABLE = (1<>1; - SSC_RECEIVE_CLOCK_MODE = SSC_CLOCK_MODE_SELECT(0); - SSC_RECEIVE_FRAME_MODE = SSC_FRAME_MODE_BITS_IN_WORD(32) | SSC_FRAME_MODE_MSB_FIRST; - SSC_TRANSMIT_CLOCK_MODE = 0; - SSC_TRANSMIT_FRAME_MODE = 0; + // TI tags charge at 134.2Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz - LED_D_ON(); + // Place FPGA in passthrough mode, in this mode the CROSS_LO line + // connects to SSP_DIN and the SSP_DOUT logic level controls + // whether we're modulating the antenna (high) + // or listening to the antenna (low) + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU); - // modulate antenna - PIO_OUTPUT_DATA_SET = (1<0) ) { + cycles++; + // after 16 cycles, measure the frequency + if (cycles>15) { + cycles=0; + samples=i-samples; // number of samples in these 16 cycles + + // TI bits are coming to us lsb first so shift them + // right through our 128 bit right shift register + shift0 = (shift0>>1) | (shift1 << 31); + shift1 = (shift1>>1) | (shift2 << 31); + shift2 = (shift2>>1) | (shift3 << 31); + shift3 >>= 1; + + // check if the cycles fall close to the number + // expected for either the low or high frequency + if ( (samples>(sampleslo-threshold)) && (samples<(sampleslo+threshold)) ) { + // low frequency represents a 1 + shift3 |= (1<<31); + } else if ( (samples>(sampleshi-threshold)) && (samples<(sampleshi+threshold)) ) { + // high frequency represents a 0 + } else { + // probably detected a gay waveform or noise + // use this as gaydar or discard shift register and start again + shift3 = shift2 = shift1 = shift0 = 0; + } + samples = i; - LED_D_OFF(); + // for each bit we receive, test if we've detected a valid tag - i = 0; - for(;;) { - if(SSC_STATUS & SSC_STATUS_RX_READY) { - BigBuf[i] = SSC_RECEIVE_HOLDING; // store 32 bit values in buffer - i++; if(i >= n) return; + // if we see 17 zeroes followed by 6 ones, we might have a tag + // remember the bits are backwards + if ( ((shift0 & 0x7fffff) == 0x7e0000) ) { + // if start and end bytes match, we have a tag so break out of the loop + if ( ((shift0>>16)&0xff) == ((shift3>>8)&0xff) ) { + cycles = 0xF0B; //use this as a flag (ugly but whatever) + break; + } + } } - WDT_HIT(); + } } - // return stolen pin to SSP - PIO_DISABLE = (1<>24) | (shift1 << 8); + shift1 = (shift1>>24) | (shift2 << 8); + + // align 16 bit crc into lower half of shift2 + shift2 = ((shift2>>24) | (shift3 << 8)) & 0x0ffff; + + // if r/w tag, check ident match + if ( shift3&(1<<15) ) { + DbpString("Info: TI tag is rewriteable"); + // only 15 bits compare, last bit of ident is not valid + if ( ((shift3>>16)^shift0)&0x7fff ) { + DbpString("Error: Ident mismatch!"); + } else { + DbpString("Info: TI tag ident is valid"); + } + } else { + DbpString("Info: TI tag is readonly"); + } -void ReadTItag() -{ + // WARNING the order of the bytes in which we calc crc below needs checking + // i'm 99% sure the crc algorithm is correct, but it may need to eat the + // bytes in reverse or something + // calculate CRC + DWORD crc=0; + + crc = update_crc16(crc, (shift0)&0xff); + crc = update_crc16(crc, (shift0>>8)&0xff); + crc = update_crc16(crc, (shift0>>16)&0xff); + crc = update_crc16(crc, (shift0>>24)&0xff); + crc = update_crc16(crc, (shift1)&0xff); + crc = update_crc16(crc, (shift1>>8)&0xff); + crc = update_crc16(crc, (shift1>>16)&0xff); + crc = update_crc16(crc, (shift1>>24)&0xff); + + DbpString("Info: Tag data_hi, data_lo, crc = "); + DbpIntegers(shift1, shift0, shift2&0xffff); + if (crc != (shift2&0xffff)) { + DbpString("Error: CRC mismatch, expected"); + DbpIntegers(0, 0, crc); + } else { + DbpString("Info: CRC is good"); + } + } } void WriteTIbyte(BYTE b) @@ -186,37 +274,91 @@ void WriteTIbyte(BYTE b) { if (b&(1<PIO_PDR = GPIO_SSC_DIN; + AT91C_BASE_PIOA->PIO_ASR = GPIO_SSC_DIN; - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // steal this pin from the SSP and use it to control the modulation + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + + AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST; + AT91C_BASE_SSC->SSC_CR = AT91C_SSC_RXEN | AT91C_SSC_TXEN; + + // Sample at 2 Mbit/s, so TI tags are 16.2 vs. 14.9 clocks long + // 48/2 = 24 MHz clock must be divided by 12 + AT91C_BASE_SSC->SSC_CMR = 12; + + AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(0); + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(32) | AT91C_SSC_MSBF; + AT91C_BASE_SSC->SSC_TCMR = 0; + AT91C_BASE_SSC->SSC_TFMR = 0; + + LED_D_ON(); + + // modulate antenna + HIGH(GPIO_SSC_DOUT); + + // Charge TI tag for 50ms. + SpinDelay(50); + + // stop modulating antenna and listen + LOW(GPIO_SSC_DOUT); + + LED_D_OFF(); + + i = 0; + for(;;) { + if(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + BigBuf[i] = AT91C_BASE_SSC->SSC_RHR; // store 32 bit values in buffer + i++; if(i >= TIBUFLEN) break; + } + WDT_HIT(); + } + + // return stolen pin to SSP + AT91C_BASE_PIOA->PIO_PDR = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_ASR = GPIO_SSC_DIN | GPIO_SSC_DOUT; + + char *dest = (char *)BigBuf; + n = TIBUFLEN*32; + // unpack buffer + for (i=TIBUFLEN-1; i>=0; i--) { +// DbpIntegers(0, 0, BigBuf[i]); + for (j=0; j<32; j++) { + if(BigBuf[i] & (1 << j)) { + dest[--n] = 1; + } else { + dest[--n] = -1; + } + } + } } // arguments: 64bit data split into 32bit idhi:idlo and optional 16bit crc @@ -252,8 +394,8 @@ void WriteTItag(DWORD idhi, DWORD idlo, WORD crc) LED_A_ON(); // steal this pin from the SSP and use it to control the modulation - PIO_ENABLE = (1<PIO_PER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; // writing algorithm: // a high bit consists of a field off for 1ms and field on for 1ms @@ -266,7 +408,7 @@ void WriteTItag(DWORD idhi, DWORD idlo, WORD crc) // finish with 15ms programming time // modulate antenna - PIO_OUTPUT_DATA_SET = (1<>8 )&0xff ); // crc hi WriteTIbyte(0x00); // write frame lo WriteTIbyte(0x03); // write frame hi - PIO_OUTPUT_DATA_SET = (1<PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; - PIO_OUTPUT_ENABLE = (1 << GPIO_SSC_DOUT); - PIO_OUTPUT_DISABLE = (1 << 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) +#define OPEN_COIL() HIGH(GPIO_SSC_DOUT) i = 0; for(;;) { - while(!(PIO_PIN_DATA_STATUS & (1<PIO_PDSR & GPIO_SSC_CLK)) { if(BUTTON_PRESS()) { DbpString("Stopped"); return; @@ -331,7 +473,7 @@ void SimulateTagLowFrequency(int period, int ledcontrol) if (ledcontrol) LED_D_OFF(); - while(PIO_PIN_DATA_STATUS & (1<PIO_PDSR & GPIO_SSC_CLK) { if(BUTTON_PRESS()) { DbpString("Stopped"); return; @@ -344,6 +486,199 @@ void SimulateTagLowFrequency(int period, 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 = TC_CMR_TCCLKS_TIMER_CLOCK1 | + 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; iTC_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 static void fc(int c, int *n) { BYTE *dest = (BYTE *)BigBuf; @@ -482,13 +817,13 @@ void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol) m = sizeof(BigBuf); memset(dest,128,m); for(;;) { - if(SSC_STATUS & (SSC_STATUS_TX_READY)) { - SSC_TRANSMIT_HOLDING = 0x43; + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0x43; if (ledcontrol) LED_D_ON(); } - if(SSC_STATUS & (SSC_STATUS_RX_READY)) { - dest[i] = (BYTE)SSC_RECEIVE_HOLDING; + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + dest[i] = (BYTE)AT91C_BASE_SSC->SSC_RHR; // we don't care about actual value, only if it's more or less than a // threshold essentially we capture zero crossings for later analysis if(dest[i] < 127) dest[i] = 0; else dest[i] = 1;