From 37867fbf3b68221db3fa2ee3e06e34b1b9fa9257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Dr=C3=B6scher?= Date: Sun, 29 Jul 2018 11:50:37 +0200 Subject: [PATCH 1/8] change: clean up Legic interface I see no adventage in poluting all sources that include legicrf.h with our internal depedencies (includes) and function names. --- armsrc/legicrf.h | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 5c3bd81b9..4bcf04899 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -11,38 +11,11 @@ #ifndef __LEGICRF_H #define __LEGICRF_H -#include "proxmark3.h" // -#include "apps.h" -#include "util.h" // -#include "string.h" -#include "legic_prng.h" // legic PRNG impl -#include "crc.h" // legic crc-4 -#include "ticks.h" // timers -#include "legic.h" // legic_card_select_t struct +#include "proxmark3.h" -extern void LegicRfSimulate(int phase, int frame, int reqresp); -extern int LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv); -extern void LegicRfWriter(uint16_t offset, uint16_t byte, uint8_t iv, uint8_t *data); extern void LegicRfInfo(void); - -uint32_t get_key_stream(int skip, int count); -void frame_send_tag(uint16_t response, uint8_t bits); -void frame_sendAsReader(uint32_t data, uint8_t bits); - -int legic_read_byte( uint16_t index, uint8_t cmd_sz); -bool legic_write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz); - -int legic_select_card(legic_card_select_t *p_card); -int legic_select_card_iv(legic_card_select_t *p_card, uint8_t iv); - -void LegicCommonInit(bool clear_mem); - -// emulator mem -void LegicEMemSet(uint32_t arg0, uint32_t arg1, uint8_t *data); -void LegicEMemGet(uint32_t arg0, uint32_t arg1); -void legic_emlset_mem(uint8_t *data, int offset, int numofbytes); -void legic_emlget_mem(uint8_t *data, int offset, int numofbytes); - -void ice_legic_setup(); +extern void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv); +extern void LegicRfWriter(uint16_t offset, uint16_t byte, uint8_t iv, uint8_t *data); +extern void LegicRfSimulate(int phase, int frame, int reqresp); #endif /* __LEGICRF_H */ From 33eb2f5fa08358ced56322b90236fa02b02baa55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Dr=C3=B6scher?= Date: Sun, 29 Jul 2018 11:57:24 +0200 Subject: [PATCH 2/8] change: remove dead legic code This code was either disabled or never reached. --- armsrc/legicrf.c | 762 +---------------------------------------------- 1 file changed, 1 insertion(+), 761 deletions(-) diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index dc4c8d4f6..035e01e98 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -30,43 +30,8 @@ static int legic_phase_drift; static int legic_frame_drift; static int legic_reqresp_drift; -AT91PS_TC timer; -AT91PS_TC prng_timer; -/* -static void setup_timer(void) { - // Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging - // this it won't be terribly accurate but should be good enough. - // - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); - timer = AT91C_BASE_TC1; - timer->TC_CCR = AT91C_TC_CLKDIS; - timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - // - // Set up Timer 2 to use for measuring time between frames in - // tag simulation mode. Runs 4x faster as Timer 1 - // - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC2); - prng_timer = AT91C_BASE_TC2; - prng_timer->TC_CCR = AT91C_TC_CLKDIS; - prng_timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV2_CLOCK; - prng_timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; -} - - AT91C_BASE_PMC->PMC_PCER |= (0x1 << 12) | (0x1 << 13) | (0x1 << 14); - AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; - - // fast clock - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz)/32 -- tick=1.5mks - AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | - AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; - AT91C_BASE_TC0->TC_RA = 1; - AT91C_BASE_TC0->TC_RC = 0xBFFF + 1; // 0xC000 - -*/ // At TIMER_CLOCK3 (MCK/32) // testing calculating in ticks. 1.5ticks = 1us @@ -126,20 +91,9 @@ static void frame_clean(struct legic_frame * const f) { f->bits = 0; } -// Prng works when waiting in 99.1us cycles. -// and while sending/receiving in bit frames (100, 60) -/*static void CalibratePrng( uint32_t time){ - // Calculate Cycles based on timer 100us - uint32_t i = (time - sendFrameStop) / 100 ; - // substract cycles of finished frames - int k = i - legic_prng_count()+1; - // substract current frame length, rewind to beginning - if ( k > 0 ) - legic_prng_forward(k); } -*/ /* Generate Keystream */ uint32_t get_key_stream(int skip, int count) { @@ -575,36 +529,7 @@ int legic_select_card(legic_card_select_t *p_card){ } //----------------------------------------------------------------------------- -// Work with emulator memory -// -// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not -// involved in dealing with emulator memory. But if it is called later, it might -// destroy the Emulator Memory. //----------------------------------------------------------------------------- -// arg0 = offset -// arg1 = num of bytes -void LegicEMemSet(uint32_t arg0, uint32_t arg1, uint8_t *data) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - legic_emlset_mem(data, arg0, arg1); -} -// arg0 = offset -// arg1 = num of bytes -void LegicEMemGet(uint32_t arg0, uint32_t arg1) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; - legic_emlget_mem(buf, arg0, arg1); - LED_B_ON(); - cmd_send(CMD_ACK, arg0, arg1, 0, buf, USB_CMD_DATA_SIZE); - LED_B_OFF(); -} -void legic_emlset_mem(uint8_t *data, int offset, int numofbytes) { - cardmem = BigBuf_get_EM_addr(); - memcpy(cardmem + offset, data, numofbytes); -} -void legic_emlget_mem(uint8_t *data, int offset, int numofbytes) { - cardmem = BigBuf_get_EM_addr(); - memcpy(data, cardmem + offset, numofbytes); -} void LegicRfInfo(void){ @@ -932,691 +857,6 @@ void LegicRfSimulate(int phase, int frame, int reqresp) LEDsoff(); cmd_send(CMD_ACK, 1, 0, 0, 0, 0); } - - -//----------------------------------------------------------------------------- -// Code up a string of octets at layer 2 (including CRC, we don't generate -// that here) so that they can be transmitted to the reader. Doesn't transmit -// them yet, just leaves them ready to send in ToSend[]. -//----------------------------------------------------------------------------- -// static void CodeLegicAsTag(const uint8_t *cmd, int len) -// { - // int i; - - // ToSendReset(); - - // // Transmit a burst of ones, as the initial thing that lets the - // // reader get phase sync. This (TR1) must be > 80/fs, per spec, - // // but tag that I've tried (a Paypass) exceeds that by a fair bit, - // // so I will too. - // for(i = 0; i < 20; i++) { - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // } - - // // Send SOF. - // for(i = 0; i < 10; i++) { - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // } - // for(i = 0; i < 2; i++) { - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // } - - // for(i = 0; i < len; i++) { - // int j; - // uint8_t b = cmd[i]; - - // // Start bit - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // ToSendStuffBit(0); - - // // Data bits - // for(j = 0; j < 8; j++) { - // if(b & 1) { - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // } else { - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // } - // b >>= 1; - // } - - // // Stop bit - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // } - - // // Send EOF. - // for(i = 0; i < 10; i++) { - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // ToSendStuffBit(0); - // } - // for(i = 0; i < 2; i++) { - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // ToSendStuffBit(1); - // } - - // // Convert from last byte pos to length - // ToSendMax++; -// } - -//----------------------------------------------------------------------------- -// The software UART that receives commands from the reader, and its state -// variables. -//----------------------------------------------------------------------------- -/* -static struct { - enum { - STATE_UNSYNCD, - STATE_GOT_FALLING_EDGE_OF_SOF, - STATE_AWAITING_START_BIT, - STATE_RECEIVING_DATA - } state; - uint16_t shiftReg; - int bitCnt; - int byteCnt; - int byteCntMax; - int posCnt; - uint8_t *output; -} Uart; -*/ -/* Receive & handle a bit coming from the reader. - * - * This function is called 4 times per bit (every 2 subcarrier cycles). - * Subcarrier frequency fs is 212kHz, 1/fs = 4,72us, i.e. function is called every 9,44us - * - * LED handling: - * LED A -> ON once we have received the SOF and are expecting the rest. - * LED A -> OFF once we have received EOF or are in error state or unsynced - * - * Returns: true if we received a EOF - * false if we are still waiting for some more - */ -// static RAMFUNC int HandleLegicUartBit(uint8_t bit) -// { - // switch(Uart.state) { - // case STATE_UNSYNCD: - // if(!bit) { - // // we went low, so this could be the beginning of an SOF - // Uart.state = STATE_GOT_FALLING_EDGE_OF_SOF; - // Uart.posCnt = 0; - // Uart.bitCnt = 0; - // } - // break; - - // case STATE_GOT_FALLING_EDGE_OF_SOF: - // Uart.posCnt++; - // if(Uart.posCnt == 2) { // sample every 4 1/fs in the middle of a bit - // if(bit) { - // if(Uart.bitCnt > 9) { - // // we've seen enough consecutive - // // zeros that it's a valid SOF - // Uart.posCnt = 0; - // Uart.byteCnt = 0; - // Uart.state = STATE_AWAITING_START_BIT; - // LED_A_ON(); // Indicate we got a valid SOF - // } else { - // // didn't stay down long enough - // // before going high, error - // Uart.state = STATE_UNSYNCD; - // } - // } else { - // // do nothing, keep waiting - // } - // Uart.bitCnt++; - // } - // if(Uart.posCnt >= 4) Uart.posCnt = 0; - // if(Uart.bitCnt > 12) { - // // Give up if we see too many zeros without - // // a one, too. - // LED_A_OFF(); - // Uart.state = STATE_UNSYNCD; - // } - // break; - - // case STATE_AWAITING_START_BIT: - // Uart.posCnt++; - // if(bit) { - // if(Uart.posCnt > 50/2) { // max 57us between characters = 49 1/fs, max 3 etus after low phase of SOF = 24 1/fs - // // stayed high for too long between - // // characters, error - // Uart.state = STATE_UNSYNCD; - // } - // } else { - // // falling edge, this starts the data byte - // Uart.posCnt = 0; - // Uart.bitCnt = 0; - // Uart.shiftReg = 0; - // Uart.state = STATE_RECEIVING_DATA; - // } - // break; - - // case STATE_RECEIVING_DATA: - // Uart.posCnt++; - // if(Uart.posCnt == 2) { - // // time to sample a bit - // Uart.shiftReg >>= 1; - // if(bit) { - // Uart.shiftReg |= 0x200; - // } - // Uart.bitCnt++; - // } - // if(Uart.posCnt >= 4) { - // Uart.posCnt = 0; - // } - // if(Uart.bitCnt == 10) { - // if((Uart.shiftReg & 0x200) && !(Uart.shiftReg & 0x001)) - // { - // // this is a data byte, with correct - // // start and stop bits - // Uart.output[Uart.byteCnt] = (Uart.shiftReg >> 1) & 0xff; - // Uart.byteCnt++; - - // if(Uart.byteCnt >= Uart.byteCntMax) { - // // Buffer overflowed, give up - // LED_A_OFF(); - // Uart.state = STATE_UNSYNCD; - // } else { - // // so get the next byte now - // Uart.posCnt = 0; - // Uart.state = STATE_AWAITING_START_BIT; - // } - // } else if (Uart.shiftReg == 0x000) { - // // this is an EOF byte - // LED_A_OFF(); // Finished receiving - // Uart.state = STATE_UNSYNCD; - // if (Uart.byteCnt != 0) { - // return TRUE; - // } - // } else { - // // this is an error - // LED_A_OFF(); - // Uart.state = STATE_UNSYNCD; - // } - // } - // break; - - // default: - // LED_A_OFF(); - // Uart.state = STATE_UNSYNCD; - // break; - // } - - // return false; -// } -/* - -static void UartReset() { - Uart.byteCntMax = 3; - Uart.state = STATE_UNSYNCD; - Uart.byteCnt = 0; - Uart.bitCnt = 0; - Uart.posCnt = 0; - memset(Uart.output, 0x00, 3); -} -*/ -// static void UartInit(uint8_t *data) { - // Uart.output = data; - // UartReset(); -// } - -//============================================================================= -// An LEGIC reader. We take layer two commands, code them -// appropriately, and then send them to the tag. We then listen for the -// tag's response, which we leave in the buffer to be demodulated on the -// PC side. -//============================================================================= -/* -static struct { - enum { - DEMOD_UNSYNCD, - DEMOD_PHASE_REF_TRAINING, - DEMOD_AWAITING_FALLING_EDGE_OF_SOF, - DEMOD_GOT_FALLING_EDGE_OF_SOF, - DEMOD_AWAITING_START_BIT, - DEMOD_RECEIVING_DATA - } state; - int bitCount; - int posCount; - int thisBit; - uint16_t shiftReg; - uint8_t *output; - int len; - int sumI; - int sumQ; -} Demod; -*/ -/* - * Handles reception of a bit from the tag - * - * This function is called 2 times per bit (every 4 subcarrier cycles). - * Subcarrier frequency fs is 212kHz, 1/fs = 4,72us, i.e. function is called every 9,44us - * - * LED handling: - * LED C -> ON once we have received the SOF and are expecting the rest. - * LED C -> OFF once we have received EOF or are unsynced - * - * Returns: true if we received a EOF - * false if we are still waiting for some more - * - */ - -/* -static RAMFUNC int HandleLegicSamplesDemod(int ci, int cq) -{ - int v = 0; - int ai = ABS(ci); - int aq = ABS(cq); - int halfci = (ai >> 1); - int halfcq = (aq >> 1); - - switch(Demod.state) { - case DEMOD_UNSYNCD: - - CHECK_FOR_SUBCARRIER() - - if(v > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected - Demod.state = DEMOD_PHASE_REF_TRAINING; - Demod.sumI = ci; - Demod.sumQ = cq; - Demod.posCount = 1; - } - break; - - case DEMOD_PHASE_REF_TRAINING: - if(Demod.posCount < 8) { - - CHECK_FOR_SUBCARRIER() - - if (v > SUBCARRIER_DETECT_THRESHOLD) { - // set the reference phase (will code a logic '1') by averaging over 32 1/fs. - // note: synchronization time > 80 1/fs - Demod.sumI += ci; - Demod.sumQ += cq; - ++Demod.posCount; - } else { - // subcarrier lost - Demod.state = DEMOD_UNSYNCD; - } - } else { - Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; - } - break; - - case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: - - MAKE_SOFT_DECISION() - - //Dbprintf("ICE: %d %d %d %d %d", v, Demod.sumI, Demod.sumQ, ci, cq ); - // logic '0' detected - if (v <= 0) { - - Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; - - // start of SOF sequence - Demod.posCount = 0; - } else { - // maximum length of TR1 = 200 1/fs - if(Demod.posCount > 25*2) Demod.state = DEMOD_UNSYNCD; - } - ++Demod.posCount; - break; - - case DEMOD_GOT_FALLING_EDGE_OF_SOF: - ++Demod.posCount; - - MAKE_SOFT_DECISION() - - if(v > 0) { - // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges - if(Demod.posCount < 10*2) { - Demod.state = DEMOD_UNSYNCD; - } else { - LED_C_ON(); // Got SOF - Demod.state = DEMOD_AWAITING_START_BIT; - Demod.posCount = 0; - Demod.len = 0; - } - } else { - // low phase of SOF too long (> 12 etu) - if(Demod.posCount > 13*2) { - Demod.state = DEMOD_UNSYNCD; - LED_C_OFF(); - } - } - break; - - case DEMOD_AWAITING_START_BIT: - ++Demod.posCount; - - MAKE_SOFT_DECISION() - - if(v > 0) { - // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs - if(Demod.posCount > 3*2) { - Demod.state = DEMOD_UNSYNCD; - LED_C_OFF(); - } - } else { - // start bit detected - Demod.bitCount = 0; - Demod.posCount = 1; // this was the first half - Demod.thisBit = v; - Demod.shiftReg = 0; - Demod.state = DEMOD_RECEIVING_DATA; - } - break; - - case DEMOD_RECEIVING_DATA: - - MAKE_SOFT_DECISION() - - if(Demod.posCount == 0) { - // first half of bit - Demod.thisBit = v; - Demod.posCount = 1; - } else { - // second half of bit - Demod.thisBit += v; - Demod.shiftReg >>= 1; - // logic '1' - if(Demod.thisBit > 0) - Demod.shiftReg |= 0x200; - - ++Demod.bitCount; - - if(Demod.bitCount == 10) { - - uint16_t s = Demod.shiftReg; - - if((s & 0x200) && !(s & 0x001)) { - // stop bit == '1', start bit == '0' - uint8_t b = (s >> 1); - Demod.output[Demod.len] = b; - ++Demod.len; - Demod.state = DEMOD_AWAITING_START_BIT; - } else { - Demod.state = DEMOD_UNSYNCD; - LED_C_OFF(); - - if(s == 0x000) { - // This is EOF (start, stop and all data bits == '0' - return true; - } - } - } - Demod.posCount = 0; - } - break; - - default: - Demod.state = DEMOD_UNSYNCD; - LED_C_OFF(); - break; - } - return false; -} -*/ -/* -// Clear out the state of the "UART" that receives from the tag. -static void DemodReset() { - Demod.len = 0; - Demod.state = DEMOD_UNSYNCD; - Demod.posCount = 0; - Demod.sumI = 0; - Demod.sumQ = 0; - Demod.bitCount = 0; - Demod.thisBit = 0; - Demod.shiftReg = 0; - memset(Demod.output, 0x00, 3); } -static void DemodInit(uint8_t *data) { - Demod.output = data; - DemodReset(); -} -*/ - -/* - * Demodulate the samples we received from the tag, also log to tracebuffer - * quiet: set to 'TRUE' to disable debug output - */ - - /* - #define LEGIC_DMA_BUFFER_SIZE 256 - - static void GetSamplesForLegicDemod(int n, bool quiet) -{ - int max = 0; - bool gotFrame = false; - int lastRxCounter = LEGIC_DMA_BUFFER_SIZE; - int ci, cq, samples = 0; - - BigBuf_free(); - - // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); - - // The response (tag -> reader) that we're receiving. - // Set up the demodulator for tag -> reader responses. - DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); - - // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t*) BigBuf_malloc(LEGIC_DMA_BUFFER_SIZE); - int8_t *upTo = dmaBuf; - - // Setup and start DMA. - if ( !FpgaSetupSscDma((uint8_t*) dmaBuf, LEGIC_DMA_BUFFER_SIZE) ){ - if (MF_DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); - return; - } - - // Signal field is ON with the appropriate LED: - LED_D_ON(); - for(;;) { - int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; - if(behindBy > max) max = behindBy; - - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (LEGIC_DMA_BUFFER_SIZE-1)) > 2) { - ci = upTo[0]; - cq = upTo[1]; - upTo += 2; - if(upTo >= dmaBuf + LEGIC_DMA_BUFFER_SIZE) { - upTo = dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = LEGIC_DMA_BUFFER_SIZE; - } - lastRxCounter -= 2; - if(lastRxCounter <= 0) - lastRxCounter = LEGIC_DMA_BUFFER_SIZE; - - samples += 2; - - gotFrame = HandleLegicSamplesDemod(ci , cq ); - if ( gotFrame ) - break; - } - - if(samples > n || gotFrame) - break; - } - - FpgaDisableSscDma(); - - if (!quiet && Demod.len == 0) { - Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", - max, - samples, - gotFrame, - Demod.len, - Demod.sumI, - Demod.sumQ - ); - } - - //Tracing - if (Demod.len > 0) { - uint8_t parity[MAX_PARITY_SIZE] = {0x00}; - LogTrace(Demod.output, Demod.len, 0, 0, parity, false); - } -} - -*/ - -//----------------------------------------------------------------------------- -// Transmit the command (to the tag) that was placed in ToSend[]. -//----------------------------------------------------------------------------- -/* -static void TransmitForLegic(void) -{ - int c; - - FpgaSetupSsc(); - - while(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) - AT91C_BASE_SSC->SSC_THR = 0xff; - - // Signal field is ON with the appropriate Red LED - LED_D_ON(); - - // Signal we are transmitting with the Green LED - LED_B_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - - for(c = 0; c < 10;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - c++; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } - WDT_HIT(); - } - - c = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c]; - legic_prng_forward(1); // forward the lfsr - c++; - if(c >= ToSendMax) { - break; - } - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } - WDT_HIT(); - } - LED_B_OFF(); -} -*/ - -//----------------------------------------------------------------------------- -// Code a layer 2 command (string of octets, including CRC) into ToSend[], -// so that it is ready to transmit to the tag using TransmitForLegic(). -//----------------------------------------------------------------------------- -/* -static void CodeLegicBitsAsReader(const uint8_t *cmd, uint8_t cmdlen, int bits) -{ - int i, j; - uint8_t b; - - ToSendReset(); - - // Send SOF - for(i = 0; i < 7; i++) - ToSendStuffBit(1); - - - for(i = 0; i < cmdlen; i++) { - // Start bit - ToSendStuffBit(0); - - // Data bits - b = cmd[i]; - for(j = 0; j < bits; j++) { - if(b & 1) { - ToSendStuffBit(1); - } else { - ToSendStuffBit(0); - } - b >>= 1; - } - } - - // Convert from last character reference to length - ++ToSendMax; -} -*/ -/** - Convenience function to encode, transmit and trace Legic comms - **/ -/* - static void CodeAndTransmitLegicAsReader(const uint8_t *cmd, uint8_t cmdlen, int bits) -{ - CodeLegicBitsAsReader(cmd, cmdlen, bits); - TransmitForLegic(); - if (tracing) { - uint8_t parity[1] = {0x00}; - LogTrace(cmd, cmdlen, 0, 0, parity, true); - } -} - -*/ -// Set up LEGIC communication -/* -void ice_legic_setup() { - - // standard things. - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); - set_tracing(true); - DemodReset(); - UartReset(); - - // Set up the synchronous serial port - FpgaSetupSsc(); - - // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - // Signal field is on with the appropriate LED - LED_D_ON(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - SpinDelay(20); - // Start the timer - //StartCountSspClk(); - - // initalize CRC - crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); - - // initalize prng - legic_prng_init(0); -} -*/ \ No newline at end of file +} \ No newline at end of file From 1adff322b1a467cd14fcfd2b60b398f16e83b76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Dr=C3=B6scher?= Date: Sun, 29 Jul 2018 12:07:54 +0200 Subject: [PATCH 3/8] change: remove broken legic simulator It will be rewritten in a later commit --- armsrc/legicrf.c | 338 +---------------------------------------------- 1 file changed, 2 insertions(+), 336 deletions(-) diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 035e01e98..e0475bc9b 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -41,37 +41,8 @@ static int legic_reqresp_drift; #define TAG_BIT_PERIOD 142 // 100us == 100 * 1.5 == 150ticks #define TAG_FRAME_WAIT 495 // 330us from READER frame end to TAG frame start. 330 * 1.5 == 495 -#define RWD_TIME_FUZZ 20 // rather generous 13us, since the peak detector + hysteresis fuzz quite a bit - -#define SIM_DIVISOR 586 /* prng_time/SIM_DIVISOR count prng needs to be forwared */ -#define SIM_SHIFT 900 /* prng_time+SIM_SHIFT shift of delayed start */ - #define OFFSET_LOG 1024 -#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz))) - -#ifndef SHORT_COIL -# define SHORT_COIL LOW(GPIO_SSC_DOUT); -#endif -#ifndef OPEN_COIL -# define OPEN_COIL HIGH(GPIO_SSC_DOUT); -#endif -#ifndef LINE_IN -# define LINE_IN AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; -#endif -// Pause pulse, off in 20us / 30ticks, -// ONE / ZERO bit pulse, -// one == 80us / 120ticks -// zero == 40us / 60ticks -#ifndef COIL_PULSE -# define COIL_PULSE(x) \ - do { \ - SHORT_COIL; \ - WaitTicks( (RWD_TIME_PAUSE) ); \ - OPEN_COIL; \ - WaitTicks((x)); \ - } while (0); -#endif // ToDo: define a meaningful maximum size for auth_table. The bigger this is, the lower will be the available memory for traces. // Historically it used to be FREE_BUFFER_SIZE, which was 2744. @@ -121,33 +92,8 @@ uint32_t get_key_stream(int skip, int count) { return legic_prng_get_bits(count); } -/* Send a frame in tag mode, the FPGA must have been set up by - * LegicRfSimulate - */ -void frame_send_tag(uint16_t response, uint8_t bits) { - uint16_t mask = 1; - - /* Bitbang the response */ - SHORT_COIL; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - /* TAG_FRAME_WAIT -> shift by 2 */ - legic_prng_forward(3); - response ^= legic_prng_get_bits(bits); - - /* Wait for the frame start */ - WaitTicks( TAG_FRAME_WAIT ); - - for (; mask < BITMASK(bits); mask <<= 1) { - if (response & mask) - OPEN_COIL - else - SHORT_COIL - WaitTicks(TAG_BIT_PERIOD); - } - SHORT_COIL; } /* Send a frame in reader mode, the FPGA must have been set up by @@ -571,292 +517,12 @@ OUT: LEDsoff(); } -/* Handle (whether to respond) a frame in tag mode - * Only called when simulating a tag. - */ -static void frame_handle_tag(struct legic_frame const * const f) -{ - // log - //uint8_t cmdbytes[] = {bits, BYTEx(data, 0), BYTEx(data, 1)}; - //LogTrace(cmdbytes, sizeof(cmdbytes), starttime, GET_TICKS, NULL, false); - //Dbprintf("ICE: enter frame_handle_tag: %02x ", f->bits); - - /* First Part of Handshake (IV) */ - if(f->bits == 7) { - - LED_C_ON(); - - // Reset prng timer - //ResetTimer(prng_timer); - ResetTicks(); - - // IV from reader. - legic_prng_init(f->data); - - Dbprintf("ICE: IV: %02x ", f->data); - - // We should have three tagtypes with three different answers. - legic_prng_forward(2); - //frame_send_tag(0x3d, 6); /* MIM1024 0x3d^0x26 = 0x1B */ - frame_send_tag(0x1d, 6); // MIM256 - - legic_state = STATE_IV; - legic_read_count = 0; - legic_prng_bc = 0; - legic_prng_iv = f->data; - - //ResetTimer(timer); - //WaitUS(280); - WaitTicks(388); - return; - } - - /* 0x19==??? */ - if(legic_state == STATE_IV) { - uint32_t local_key = get_key_stream(3, 6); - int xored = 0x39 ^ local_key; - if((f->bits == 6) && (f->data == xored)) { - legic_state = STATE_CON; - - ResetTimer(timer); - WaitTicks(300); - return; - - } else { - legic_state = STATE_DISCON; - LED_C_OFF(); - Dbprintf("iv: %02x frame: %02x key: %02x xored: %02x", legic_prng_iv, f->data, local_key, xored); - return; - } - } - - /* Read */ - if(f->bits == 11) { - if(legic_state == STATE_CON) { - uint32_t key = get_key_stream(2, 11); //legic_phase_drift, 11); - uint16_t addr = f->data ^ key; - addr >>= 1; - uint8_t data = cardmem[addr]; - - uint32_t crc = legic4Crc(LEGIC_READ, addr, data, 11) << 8; - - //legic_read_count++; - //legic_prng_forward(legic_reqresp_drift); - - frame_send_tag(crc | data, 12); - //ResetTimer(timer); - legic_prng_forward(2); - WaitTicks(330); - return; - } - } - - /* Write */ - if (f->bits == 23 || f->bits == 21 ) { - uint32_t key = get_key_stream(-1, 23); //legic_frame_drift, 23); - uint16_t addr = f->data ^ key; - addr >>= 1; - addr &= 0x3ff; - uint32_t data = f->data ^ key; - data >>= 11; - data &= 0xff; - - cardmem[addr] = data; - /* write command */ - legic_state = STATE_DISCON; - LED_C_OFF(); - Dbprintf("write - addr: %x, data: %x", addr, data); - // should send a ACK after 3.6ms - return; - } - - if(legic_state != STATE_DISCON) { - Dbprintf("Unexpected: sz:%u, Data:%03.3x, State:%u, Count:%u", f->bits, f->data, legic_state, legic_read_count); - Dbprintf("IV: %03.3x", legic_prng_iv); - } - - legic_state = STATE_DISCON; - legic_read_count = 0; - WaitMS(10); - LED_C_OFF(); - return; } -/* Read bit by bit untill full frame is received - * Call to process frame end answer - */ -static void emit(int bit) { - - switch (bit) { - case 1: - frame_append_bit(¤t_frame, 1); - break; - case 0: - frame_append_bit(¤t_frame, 0); - break; - default: - if(current_frame.bits <= 4) { - frame_clean(¤t_frame); - } else { - frame_handle_tag(¤t_frame); - frame_clean(¤t_frame); - } - WDT_HIT(); - break; - } } -void LegicRfSimulate(int phase, int frame, int reqresp) -{ - /* ADC path high-frequency peak detector, FPGA in high-frequency simulator mode, - * modulation mode set to 212kHz subcarrier. We are getting the incoming raw - * envelope waveform on DIN and should send our response on DOUT. - * - * The LEGIC RF protocol is pulse-pause-encoding from reader to card, so we'll - * measure the time between two rising edges on DIN, and no encoding on the - * subcarrier from card to reader, so we'll just shift out our verbatim data - * on DOUT, 1 bit is 100us. The time from reader to card frame is still unclear, - * seems to be 330us. - */ - - int old_level = 0, active = 0; - volatile int32_t level = 0; - - legic_state = STATE_DISCON; - legic_phase_drift = phase; - legic_frame_drift = frame; - legic_reqresp_drift = reqresp; - - - /* to get the stream of bits from FPGA in sim mode.*/ - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // Set up the synchronous serial port - //FpgaSetupSsc(); - // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); - //FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); - - #define LEGIC_DMA_BUFFER 256 - // The DMA buffer, used to stream samples from the FPGA - //uint8_t *dmaBuf = BigBuf_malloc(LEGIC_DMA_BUFFER); - //uint8_t *data = dmaBuf; - // Setup and start DMA. - // if ( !FpgaSetupSscDma((uint8_t*) dmaBuf, LEGIC_DMA_BUFFER) ){ - // if (MF_DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); - // return; - // } - - //StartCountSspClk(); - /* Bitbang the receiver */ - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; - - // need a way to determine which tagtype we are simulating - - // hook up emulator memory - cardmem = BigBuf_get_EM_addr(); - - clear_trace(); - set_tracing(true); - - crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); - - StartTicks(); - - LED_B_ON(); - DbpString("Starting Legic emulator, press button to end"); - - /* - * The mode FPGA_HF_SIMULATOR_MODULATE_212K works like this. - * - A 1-bit input to the FPGA becomes 8 pulses on 212kHz (fc/64) (18.88us). - * - A 0-bit input to the FPGA becomes an unmodulated time of 18.88us - * - * In this mode the SOF can be written as 00011101 = 0x1D - * The EOF can be written as 10111000 = 0xb8 - * A logic 1 is 01 - * A logic 0 is 10 - volatile uint8_t b; - uint8_t i = 0; - while( !BUTTON_PRESS() ) { - WDT_HIT(); - - // not sending anything. - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; - } - - // receive - if ( AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY ) { - b = (uint8_t) AT91C_BASE_SSC->SSC_RHR; - bd[i] = b; - ++i; - // if(OutOfNDecoding(b & 0x0f)) - // *len = Uart.byteCnt; - } - - } - */ - - while(!BUTTON_PRESS() && !usb_poll_validate_length()) { - - level = !!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - - uint32_t time = GET_TICKS; - - if (level != old_level) { - if (level == 1) { - - //Dbprintf("start, %u ", time); - StartTicks(); - // did we get a signal - if (FUZZ_EQUAL(time, RWD_TIME_1, RWD_TIME_FUZZ)) { - // 1 bit - emit(1); - active = 1; - LED_A_ON(); - } else if (FUZZ_EQUAL(time, RWD_TIME_0, RWD_TIME_FUZZ)) { - // 0 bit - emit(0); - active = 1; - LED_A_ON(); - } else if (active) { - // invalid - emit(-1); - active = 0; - LED_A_OFF(); - } - } - } - - - /* Frame end */ - if(time >= (RWD_TIME_1 + RWD_TIME_FUZZ) && active) { - emit(-1); - active = 0; - LED_A_OFF(); - } - - /* - * Disable the counter, Then wait for the clock to acknowledge the - * shutdown in its status register. Reading the SR has the - * side-effect of clearing any pending state in there. - */ - //if(time >= (20*RWD_TIME_1) && (timer->TC_SR & AT91C_TC_CLKSTA)) - if(time >= (20 * RWD_TIME_1) ) - StopTicks(); - - old_level = level; - WDT_HIT(); } - WDT_HIT(); - DbpString("LEGIC Prime emulator stopped"); - switch_off_tag_rwd(); - FpgaDisableSscDma(); - LEDsoff(); - cmd_send(CMD_ACK, 1, 0, 0, 0, 0); +void LegicRfSimulate(int phase, int frame, int reqresp) { + cmd_send(CMD_ACK, 0, 0, 0, 0, 0); //TODO Implement } -} - -} \ No newline at end of file From 7517c894d29e3bbabb6832a29a3e160bd6787250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Dr=C3=B6scher?= Date: Sun, 29 Jul 2018 12:23:35 +0200 Subject: [PATCH 4/8] change: remove jerry-riged hysteresis based receiver from hi_read_tx This future got obsolete by a x-correlation based receiver. This reverts commit 24fe4dffb49ca5c50983c54f1b1d51028c06390d. --- fpga/hi_read_tx.v | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v index bede40965..756683cdd 100644 --- a/fpga/hi_read_tx.v +++ b/fpga/hi_read_tx.v @@ -71,19 +71,8 @@ always @(negedge ssp_clk) assign ssp_frame = (hi_byte_div == 3'b000); -// Implement a hysteresis to give out the received signal on -// ssp_din. Sample at fc. -assign adc_clk = ck_1356meg; +assign ssp_din = 1'b0; -// ADC data appears on the rising edge, so sample it on the falling edge -reg after_hysteresis; -always @(negedge adc_clk) -begin - if(& adc_d[6:4]) after_hysteresis <= 1'b1; - else if(~(| adc_d[6:4])) after_hysteresis <= 1'b0; -end +assign dbg = ssp_frame; -assign ssp_din = after_hysteresis; -assign dbg = after_hysteresis; - -endmodule +endmodule \ No newline at end of file From d7c57dbc08e7d4d256d3bbd337e44a04cd451c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Dr=C3=B6scher?= Date: Sun, 29 Jul 2018 11:47:54 +0200 Subject: [PATCH 5/8] add: xcorr 211.875 kHz option The FPGA supported this frequency for a long time, just the ARM code had no define to enable it. --- armsrc/fpgaloader.h | 1 + 1 file changed, 1 insertion(+) diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 66cca0510..21731b571 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -74,6 +74,7 @@ extern void switch_off(void); // Options for the HF reader, correlating against rx from tag #define FPGA_HF_READER_RX_XCORR_848_KHZ (1<<0) #define FPGA_HF_READER_RX_XCORR_SNOOP (1<<1) +#define FPGA_HF_READER_RX_XCORR_QUARTER (1<<2) // Options for the HF simulated tag, how to modulate #define FPGA_HF_SIMULATOR_NO_MODULATION (0<<0) // 0000 #define FPGA_HF_SIMULATOR_MODULATE_BPSK (1<<0) // 0001 From 78d5188922c6a9bf0e6d22e54308c7ff0932d7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Dr=C3=B6scher?= Date: Sun, 29 Jul 2018 12:18:08 +0200 Subject: [PATCH 6/8] change: legic reader now uses xcorrelation and ssc based io - Even tough legic tags transmit just AM using xcorrelation results in a significantly better signal quality. - Switching from bit bang to a hardware based ssc frees up CPU time for other tasks e.g. demodulation --- armsrc/legicrf.c | 902 +++++++++++++++++++++-------------------------- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes 2 files changed, 408 insertions(+), 494 deletions(-) diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index e0475bc9b..7837322e0 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -1,6 +1,7 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz // 2016 Iceman +// 2018 AntiCat (rwd rewritten) // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -10,517 +11,430 @@ //----------------------------------------------------------------------------- #include "legicrf.h" -static struct legic_frame { - uint8_t bits; - uint32_t data; -} current_frame; +#include "ticks.h" /* timers */ +#include "crc.h" /* legic crc-4 */ +#include "legic_prng.h" /* legic PRNG impl */ +#include "legic.h" /* legic_card_select_t struct */ -static enum { - STATE_DISCON, - STATE_IV, - STATE_CON, -} legic_state; +struct legic_frame { + uint32_t bits; /* length of frame */ + uint8_t data[24]; /* preprocessed bits */ +}; -static crc_t legic_crc; -static int legic_read_count; -static uint32_t legic_prng_bc; -static uint32_t legic_prng_iv; +union frame_encoder { + uint32_t uint32; /* SAM7S512 does not support unaligned access as a */ + uint8_t uint8[4]; /* workaround 32bit values are converted to 4x8bit */ +}; -static int legic_phase_drift; -static int legic_frame_drift; -static int legic_reqresp_drift; +static uint8_t* legic_mem; /* card memory, used for read, write and sim */ +static legic_card_select_t card;/* metadata of currently selected card */ +static crc_t legic_crc; +static int32_t input_threshold; /* values > threshold are 1 else 0 */ +// LEGIC RF is using the common timer functions: StartCountUS() and GetCountUS() +#define RWD_TIME_PAUSE 20 /* 20us */ +#define RWD_TIME_1 100 /* READER_TIME_PAUSE 20us off + 80us on = 100us */ +#define RWD_TIME_0 60 /* READER_TIME_PAUSE 20us off + 40us on = 60us */ +#define TAG_FRAME_WAIT 330 /* 330us from READER frame end to TAG frame start */ +#define TAG_BIT_PERIOD 100 /* 100us */ +#define LEGIC_CARD_MEMSIZE 1024 /* The largest Legic Prime card is 1k */ +//----------------------------------------------------------------------------- +// I/O interface abstraction (ARM <-> FPGA) +//----------------------------------------------------------------------------- -// At TIMER_CLOCK3 (MCK/32) -// testing calculating in ticks. 1.5ticks = 1us -#define RWD_TIME_1 120 // READER_TIME_PAUSE 20us off, 80us on = 100us 80 * 1.5 == 120ticks -#define RWD_TIME_0 60 // READER_TIME_PAUSE 20us off, 40us on = 60us 40 * 1.5 == 60ticks -#define RWD_TIME_PAUSE 30 // 20us == 20 * 1.5 == 30ticks */ -#define TAG_BIT_PERIOD 142 // 100us == 100 * 1.5 == 150ticks -#define TAG_FRAME_WAIT 495 // 330us from READER frame end to TAG frame start. 330 * 1.5 == 495 +static inline uint8_t rx_byte_from_fpga() { + for(;;) { + WDT_HIT(); -#define OFFSET_LOG 1024 - - -// ToDo: define a meaningful maximum size for auth_table. The bigger this is, the lower will be the available memory for traces. -// Historically it used to be FREE_BUFFER_SIZE, which was 2744. -#define LEGIC_CARD_MEMSIZE 1024 -static uint8_t* cardmem; - -static void frame_append_bit(struct legic_frame * const f, uint8_t bit) { - // Overflow, won't happen - if (f->bits >= 31) return; - - f->data |= (bit << f->bits); - f->bits++; -} - -static void frame_clean(struct legic_frame * const f) { - f->data = 0; - f->bits = 0; -} - - - -} - -/* Generate Keystream */ -uint32_t get_key_stream(int skip, int count) { - - int i; - - // Use int to enlarge timer tc to 32bit - legic_prng_bc += prng_timer->TC_CV; - - // reset the prng timer. - - /* If skip == -1, forward prng time based */ - if(skip == -1) { - i = (legic_prng_bc + SIM_SHIFT)/SIM_DIVISOR; /* Calculate Cycles based on timer */ - i -= legic_prng_count(); /* substract cycles of finished frames */ - i -= count; /* substract current frame length, rewind to beginning */ - legic_prng_forward(i); - } else { - legic_prng_forward(skip); - } - - i = (count == 6) ? -1 : legic_read_count; - - /* Generate KeyStream */ - return legic_prng_get_bits(count); -} - - - -} - -/* Send a frame in reader mode, the FPGA must have been set up by - * LegicRfReader - */ -void frame_sendAsReader(uint32_t data, uint8_t bits){ - - uint32_t starttime = GET_TICKS, send = 0, mask = 1; - - // xor lsfr onto data. - send = data ^ legic_prng_get_bits(bits); - - for (; mask < BITMASK(bits); mask <<= 1) { - if (send & mask) - COIL_PULSE(RWD_TIME_1) - else - COIL_PULSE(RWD_TIME_0) - } - - // Final pause to mark the end of the frame - COIL_PULSE(0); - - // log - uint8_t cmdbytes[] = {bits, BYTEx(data,0), BYTEx(data,1), BYTEx(data,2), BYTEx(send,0), BYTEx(send,1), BYTEx(send,2)}; - LogTrace(cmdbytes, sizeof(cmdbytes), starttime, GET_TICKS, NULL, true); -} - -/* Receive a frame from the card in reader emulation mode, the FPGA and - * timer must have been set up by LegicRfReader and frame_sendAsReader. - * - * The LEGIC RF protocol from card to reader does not include explicit - * frame start/stop information or length information. The reader must - * know beforehand how many bits it wants to receive. (Notably: a card - * sending a stream of 0-bits is indistinguishable from no card present.) - * - * Receive methodology: There is a fancy correlator in hi_read_rx_xcorr, but - * I'm not smart enough to use it. Instead I have patched hi_read_tx to output - * the ADC signal with hysteresis on SSP_DIN. Bit-bang that signal and look - * for edges. Count the edges in each bit interval. If they are approximately - * 0 this was a 0-bit, if they are approximately equal to the number of edges - * expected for a 212kHz subcarrier, this was a 1-bit. For timing we use the - * timer that's still running from frame_sendAsReader in order to get a synchronization - * with the frame that we just sent. - * - * FIXME: Because we're relying on the hysteresis to just do the right thing - * the range is severely reduced (and you'll probably also need a good antenna). - * So this should be fixed some time in the future for a proper receiver. - */ -static void frame_receiveAsReader(struct legic_frame * const f, uint8_t bits) { - - if ( bits > 32 ) return; - - uint8_t i = bits, edges = 0; - uint32_t the_bit = 1, next_bit_at = 0, data = 0; - uint32_t old_level = 0; - volatile uint32_t level = 0; - - frame_clean(f); - - // calibrate the prng. - legic_prng_forward(2); - data = legic_prng_get_bits(bits); - - //FIXED time between sending frame and now listening frame. 330us - uint32_t starttime = GET_TICKS; - // its about 9+9 ticks delay from end-send to here. - WaitTicks( 477 ); - - next_bit_at = GET_TICKS + TAG_BIT_PERIOD; - - while ( i-- ){ - edges = 0; - while ( GET_TICKS < next_bit_at) { - - level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - - if (level != old_level) - ++edges; - - old_level = level; - } - - next_bit_at += TAG_BIT_PERIOD; - - // We expect 42 edges (ONE) - if ( edges > 20 ) - data ^= the_bit; - - the_bit <<= 1; - } - - // output - f->data = data; - f->bits = bits; - - // log - uint8_t cmdbytes[] = {bits, BYTEx(data, 0), BYTEx(data, 1)}; - LogTrace(cmdbytes, sizeof(cmdbytes), starttime, GET_TICKS, NULL, false); -} - -// Setup pm3 as a Legic Reader -static uint32_t setup_phase_reader(uint8_t iv) { - - // Switch on carrier and let the tag charge for 5ms - HIGH(GPIO_SSC_DOUT); - WaitUS(5000); - - ResetTicks(); - - legic_prng_init(0); - - // send IV handshake - frame_sendAsReader(iv, 7); - - // tag and reader has same IV. - legic_prng_init(iv); - - frame_receiveAsReader(¤t_frame, 6); - - // 292us (438t) - fixed delay before sending ack. - // minus log and stuff 100tick? - WaitTicks(338); - legic_prng_forward(3); - - // Send obsfuscated acknowledgment frame. - // 0x19 = 0x18 MIM22, 0x01 LSB READCMD - // 0x39 = 0x38 MIM256, MIM1024 0x01 LSB READCMD - switch ( current_frame.data ) { - case 0x0D: frame_sendAsReader(0x19, 6); break; - case 0x1D: - case 0x3D: frame_sendAsReader(0x39, 6); break; - default: break; - } - - legic_prng_forward(2); - return current_frame.data; -} - -void LegicCommonInit(bool clear_mem) { - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - /* Bitbang the transmitter */ - SHORT_COIL; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; - - // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. - cardmem = BigBuf_get_EM_addr(); - if ( clear_mem ) - memset(cardmem, 0x00, LEGIC_CARD_MEMSIZE); - - clear_trace(); - set_tracing(true); - crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); - - StartTicks(); -} - -// Switch off carrier, make sure tag is reset -static void switch_off_tag_rwd(void) { - SHORT_COIL; - WaitUS(20); - WDT_HIT(); -} - -// calculate crc4 for a legic READ command -static uint32_t legic4Crc(uint8_t cmd, uint16_t byte_index, uint8_t value, uint8_t cmd_sz) { - crc_clear(&legic_crc); - uint32_t temp = (value << cmd_sz) | (byte_index << 1) | cmd; - crc_update(&legic_crc, temp, cmd_sz + 8 ); - return crc_finish(&legic_crc); -} - -int legic_read_byte( uint16_t index, uint8_t cmd_sz) { - - uint8_t byte, crc, calcCrc = 0; - uint32_t cmd = (index << 1) | LEGIC_READ; - - // 90ticks = 60us (should be 100us but crc calc takes time.) - //WaitTicks(330); // 330ticks prng(4) - works - WaitTicks(240); // 240ticks prng(3) - works - - frame_sendAsReader(cmd, cmd_sz); - frame_receiveAsReader(¤t_frame, 12); - - // CRC check. - byte = BYTEx(current_frame.data, 0); - crc = BYTEx(current_frame.data, 1); - calcCrc = legic4Crc(LEGIC_READ, index, byte, cmd_sz); - - if( calcCrc != crc ) { - Dbprintf("!!! crc mismatch: %x != %x !!!", calcCrc, crc); - return -1; - } - - legic_prng_forward(3); - return byte; -} - -/* - * - assemble a write_cmd_frame with crc and send it - * - wait until the tag sends back an ACK ('1' bit unencrypted) - * - forward the prng based on the timing - */ -bool legic_write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) { - - bool isOK = false; - int8_t i = 40; - uint8_t edges = 0; - uint8_t cmd_sz = addr_sz+1+8+4; //crc+data+cmd; - uint32_t steps = 0, next_bit_at, start, crc, old_level = 0; - - crc = legic4Crc(LEGIC_WRITE, index, byte, addr_sz+1); - - // send write command - uint32_t cmd = LEGIC_WRITE; - cmd |= index << 1; // index - cmd |= byte << (addr_sz+1); // Data - cmd |= (crc & 0xF ) << (addr_sz+1+8); // CRC - - WaitTicks(240); - - frame_sendAsReader(cmd, cmd_sz); - - LINE_IN; - - start = GET_TICKS; - - // ACK, - one single "1" bit after 3.6ms - // 3.6ms = 3600us * 1.5 = 5400ticks. - WaitTicks(5400); - - next_bit_at = GET_TICKS + TAG_BIT_PERIOD; - - while ( i-- ) { - WDT_HIT(); - edges = 0; - while ( GET_TICKS < next_bit_at) { - - volatile uint32_t level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - - if (level != old_level) - ++edges; - - old_level = level; - } - - next_bit_at += TAG_BIT_PERIOD; - - // We expect 42 edges (ONE) - if(edges > 20 ) { - steps = ( (GET_TICKS - start) / TAG_BIT_PERIOD); - legic_prng_forward(steps); - isOK = true; - goto OUT; - } + // wait for byte be become available in rx holding register + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + return AT91C_BASE_SSC->SSC_RHR; } - -OUT: ; - legic_prng_forward(1); - - uint8_t cmdbytes[] = {1, isOK, BYTEx(steps, 0), BYTEx(steps, 1) }; - LogTrace(cmdbytes, sizeof(cmdbytes), start, GET_TICKS, NULL, false); - return isOK; + } } -int LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { - - uint16_t i = 0; - uint8_t isOK = 1; - legic_card_select_t card; - - LegicCommonInit(true); - - if ( legic_select_card_iv(&card, iv) ) { - isOK = 0; - goto OUT; - } +static inline void tx_byte_to_fpga(uint8_t byte) { + for(;;) { + WDT_HIT(); - if (len + offset > card.cardsize) - len = card.cardsize - offset; + // put byte into tx holding register as soon as it is ready + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = byte; + return; + } + } +} - LED_B_ON(); - while (i < len) { - int r = legic_read_byte(offset + i, card.cmdsize); - - if (r == -1 || BUTTON_PRESS()) { - if ( MF_DBGLEVEL >= 2) DbpString("operation aborted"); - isOK = 0; - goto OUT; - } - cardmem[i++] = r; - WDT_HIT(); - } +//----------------------------------------------------------------------------- +// Demodulation +//----------------------------------------------------------------------------- -OUT: - WDT_HIT(); - switch_off_tag_rwd(); - LEDsoff(); - cmd_send(CMD_ACK, isOK, len, 0, cardmem, len); - return 0; +// Returns am aproximated power measurement +// +// The FPGA running on the xcorrelation kernel samples the subcarrier at ~3 MHz. +// The kernel was initialy designed to receive BSPK/2-PSK. Hance, it reports an +// I/Q pair every 18.9us (8 bits i and 8 bits q). +// +// The subcarrier amplitude can be calculated using Pythagoras sqrt(i^2 + q^2). +// To reduce CPU time the amplitude is approximated by using linear functions: +// am = MAX(ABS(i),ABS(q)) + 1/2*MIN(ABS(i),ABSq)) +// +// Note: The SSC receiver is never synchronized the calculation my be performed +// on a i/q pair from two subsequent correlations, but does not matter. +static inline int32_t sample_power() { + int32_t q = (int8_t)rx_byte_from_fpga(); q = ABS(q); + int32_t i = (int8_t)rx_byte_from_fpga(); i = ABS(i); + + return MAX(i, q) + (MIN(i, q) >> 1); +} + +// Returns a demedulated bit +// +// An aproximated power measurement is available every 18.9us. The bit time +// is 100us. The code samples 5 times and uses samples 3 and 4. +// +// Note: The demodulator is drifting (18.9us * 5 = 94.5us), since the longest +// respons is 12 bits, the demodulator will stay in sync with a margin of +// error of 20us left. Sending the next request will resync the card. +static inline bool rx_bit() { + static int32_t p[5]; + for(size_t i = 0; i<5; ++i) { + p[i] = sample_power(); + } + + if((p[2] > input_threshold) && (p[3] > input_threshold)) { + return true; + } + if((p[2] < input_threshold) && (p[3] < input_threshold)) { + return false; + } + + Dbprintf("rx_bit failed %i vs %i (threshold %i)", p[2], p[3], input_threshold); + return false; +} + +//----------------------------------------------------------------------------- +// Modulation +// +// Modulation is a little bit more tricky as demedulation ssp_clk is running at +// 105.4 kHz resulting in a ssc bit periode of 9.4us and ssc frame periode of +// 76us. Legic has a strange pause-puls modulation with a 60us 0-bit and 100us +// 1-bit periode. The following functions use bit stuffing to aproximate the +// modulation. The default state of all bits is 1 the. The code adds pauses: +// - A 1 is aproximated by b1100000000 = 0x0300 +// - A 0 is aproximated by b110000 = 0x0030 +// +// Note: The modulator expect to be run on a little-endian system and the frame +// length to not exeed 11 bits. The frame length is not checked. +//----------------------------------------------------------------------------- + +static void clean_frame(struct legic_frame *f) { + memset(f->data, 0xff, sizeof(f->data)); + + // add end of frame pause + f->data[0] ^= 0x03; + f->bits = 2; +} + +static void append_to_frame(struct legic_frame *f, uint8_t bit) { + uint8_t bit_pos = f->bits % 8; // calculate bits used in partially used byte + uint8_t byte_pos = f->bits / 8; // calculate next free or partially used byte + + static union frame_encoder frame_encoder; + + if(bit) { + frame_encoder.uint32 = 0x0300 << bit_pos; // appended bits at bit_pos + f->bits += 10; // store amount of bits appended + } else { + frame_encoder.uint32 = 0x0030 << bit_pos; // appended bits at bit_pos + f->bits += 6; // store amount of bits appended + } + + // Move data from encoder to frame. This d-tour is necessary bacause the uC + // does not support unaligned access. We use bitwise not and xor to flip bits. + for(uint8_t i = 0; i < sizeof(frame_encoder); ++i) { + f->data[i + byte_pos] ^= frame_encoder.uint8[i]; + } +} + +static uint8_t finalize_frame(struct legic_frame *f) { + // convert bits into full bytes + return (f->bits + 7) / 8; +} + +//----------------------------------------------------------------------------- +// Frame Handling +// +// The LEGIC RF protocol from card to reader does not include explicit frame +// start/stop information or length information. The reader must know beforehand +// how many bits it wants to receive. +// Notably: a card sending a stream of 0-bits is indistinguishable from no card +// present. +//----------------------------------------------------------------------------- + +static void tx_frame(uint32_t frame, uint8_t len) { + static struct legic_frame legic_frame; + clean_frame(&legic_frame); + + // add bit by bit to frame, MSB (last bit on air) first, this reverses the order + // reverse order keeps last pause aligned to byte boundry and in sync with ret + // of last tx_byte_to_fpga call. this in turn syncs our rx phase perfectly. + while(len > 0) { + uint8_t lsb = (frame >> --len) & 0x01; + append_to_frame(&legic_frame, lsb ^ legic_prng_get_bit()); + legic_prng_forward(1); + } + + // finalize frame, returns length in bytes + len = finalize_frame(&legic_frame); + + // start tx with first frame preloaded + tx_byte_to_fpga(legic_frame.data[--len]); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + + // transmit frame, MSB first + while(len > 0) { + tx_byte_to_fpga(legic_frame.data[--len]); + } + + // tx queue has 2 cycles, add 2 empty frames to leave function in sync + tx_byte_to_fpga(0xff); // blocks until last frame is loaded into shift register + tx_byte_to_fpga(0xff); // blocks until last frame is done +} + +static uint32_t rx_frame(uint8_t len) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR + | FPGA_HF_READER_RX_XCORR_848_KHZ + | FPGA_HF_READER_RX_XCORR_QUARTER); + + uint32_t frame = 0; + for(uint8_t i = 0; i < len; i++) { + frame |= (rx_bit() ^ legic_prng_get_bit()) << i; + legic_prng_forward(1); + } + + return frame; +} + +//----------------------------------------------------------------------------- +// Legic Reader +//----------------------------------------------------------------------------- + +int init_card(uint8_t cardtype, legic_card_select_t *p_card) { + p_card->tagtype = cardtype; + + switch(p_card->tagtype) { + case 0x0d: + p_card->cmdsize = 6; + p_card->addrsize = 5; + p_card->cardsize = 22; + break; + case 0x1d: + p_card->cmdsize = 9; + p_card->addrsize = 8; + p_card->cardsize = 256; + break; + case 0x3d: + p_card->cmdsize = 11; + p_card->addrsize = 10; + p_card->cardsize = 1024; + break; + default: + p_card->cmdsize = 0; + p_card->addrsize = 0; + p_card->cardsize = 0; + return 2; + } + return 0; +} + +static void init_reader(bool clear_mem) { + // configure FPGA + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR + | FPGA_HF_READER_RX_XCORR_848_KHZ + | FPGA_HF_READER_RX_XCORR_QUARTER); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // configure SSC with defaults (note: defaults are MSB first - Legic has LSB first, + // the rx stream is bit stuff in reverse to fix this. However, reversing the order + // will align the last pause to a byte boundry and we want that for synchronisation. + FpgaSetupSsc(); + + // and additonaly set Data Default to 1, to prevent glitches when switching to tx. + AT91C_BASE_SSC->SSC_TFMR |= AT91C_SSC_DATDEF; + + // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. + legic_mem = BigBuf_get_EM_addr(); + if(legic_mem) { + memset(legic_mem, 0x00, LEGIC_CARD_MEMSIZE); + } + + // start trace + clear_trace(); + set_tracing(true); + + // init crc calculator + crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0); + + // start us timer + StartCountUS(); +} + +// Setup reader to card connection +// +// The setup consists of a three way handshake: +// - Transmit initialisation vector 7 bits +// - Receive card type 6 bits +// - Acknowledge frame 6 bits +static uint32_t setup_phase_reader(uint8_t iv) { + uint32_t ts = GetCountUS(); + + // Switch on carrier and let the card charge for 5ms. + // Use the time to calibrate the treshhold. + input_threshold = 8; // heuristically determined + do { + int32_t sample = sample_power(); + if(sample > input_threshold) { + input_threshold = sample; + } + } while(GetCountUS() < ts + 5000); + + // Set threshold to noise floor * 2 + input_threshold <<= 1; + + legic_prng_init(0); + tx_frame(iv, 7); + ts = GetCountUS(); + + // configure iv + legic_prng_init(iv); + legic_prng_forward(2); + + // wait until card is expect to respond + while(GetCountUS() < ts + TAG_FRAME_WAIT) { }; + + // receive card type + int32_t card_type = rx_frame(6); + + // send obsfuscated acknowledgment frame + switch (card_type) { + case 0x0D: + tx_frame(0x19, 6); // MIM22 | READCMD = 0x18 | 0x01 + break; + case 0x1D: + case 0x3D: + tx_frame(0x39, 6); // MIM256 | READCMD = 0x38 | 0x01 + break; + } + + return card_type; +} + +static uint8_t calc_crc4(uint16_t cmd, uint8_t cmd_sz, uint8_t value) { + crc_clear(&legic_crc); + crc_update(&legic_crc, (value << cmd_sz) | cmd, 8 + cmd_sz); + return crc_finish(&legic_crc); +} + +static int16_t read_byte(uint16_t index, uint8_t cmd_sz) { + uint16_t cmd = (index << 1) | LEGIC_READ; + + // read one byte + tx_frame(cmd, cmd_sz); + uint32_t frame = rx_frame(12); + + // split frame into data and crc + uint8_t byte = BYTEx(frame, 0); + uint8_t crc = BYTEx(frame, 1); + + // check received against calculated crc + uint8_t calc_crc = calc_crc4(cmd, cmd_sz, byte); + if(calc_crc != crc) { + Dbprintf("!!! crc mismatch: %x != %x !!!", calc_crc, crc); + return -1; + } + + return byte; +} + +//----------------------------------------------------------------------------- +// Command Line Interface +// +// Only this functions are public / called from appmain.c +//----------------------------------------------------------------------------- +void LegicRfInfo(void) { + // configure ARM and FPGA + init_reader(false); + + // establish shared secret and detect card type + uint8_t card_type = setup_phase_reader(0x01); + if(init_card(card_type, &card) != 0) { + cmd_send(CMD_ACK, 0, 0, 0, 0, 0); + goto OUT; + } + + // read UID + for(uint8_t i = 0; i < sizeof(card.uid); ++i) { + int16_t byte = read_byte(i, card.cmdsize); + if(byte == -1) { + cmd_send(CMD_ACK, 0, 0, 0, 0, 0); + goto OUT; + } + card.uid[i] = byte & 0xFF; + } + + // read MCC and check against UID + int16_t mcc = read_byte(4, card.cmdsize); + int16_t calc_mcc = CRC8Legic(card.uid, 4);; + if(mcc != calc_mcc) { + cmd_send(CMD_ACK, 0, 0, 0, 0, 0); + goto OUT; + } + + // OK + cmd_send(CMD_ACK, 1, 0, 0, (uint8_t*)&card, sizeof(legic_card_select_t)); + +OUT: + switch_off(); +} + +void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { + // configure ARM and FPGA + init_reader(false); + + // establish shared secret and detect card type + uint8_t card_type = setup_phase_reader(iv); + if(init_card(card_type, &card) != 0) { + cmd_send(CMD_ACK, 0, 0, 0, 0, 0); + goto OUT; + } + + // do not read beyond card memory + if(len + offset > card.cardsize) { + len = card.cardsize - offset; + } + + for(uint16_t i = 0; i < len; ++i) { + int16_t byte = read_byte(offset + i, card.cmdsize); + if(byte == -1) { + cmd_send(CMD_ACK, 0, 0, 0, 0, 0); + goto OUT; + } + legic_mem[i] = byte; + } + + // OK + cmd_send(CMD_ACK, 1, len, 0, legic_mem, len); + +OUT: + switch_off(); } void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data) { - - #define LOWERLIMIT 4 - uint8_t isOK = 1, msg = 0; - legic_card_select_t card; - - // uid NOT is writeable. - if ( offset <= LOWERLIMIT ) { - isOK = 0; - goto OUT; - } - - LegicCommonInit(false); - - if ( legic_select_card_iv(&card, iv) ) { - isOK = 0; - msg = 1; - goto OUT; - } - - if ( len + offset > card.cardsize) - len = card.cardsize - offset; - - LED_B_ON(); - while( len > 0 ) { - --len; - if ( !legic_write_byte( len + offset, data[len], card.addrsize) ) { - Dbprintf("operation failed | %02X | %02X | %02X", len + offset, len, data[len] ); - isOK = 0; - goto OUT; - } - WDT_HIT(); - } -OUT: - cmd_send(CMD_ACK, isOK, msg,0,0,0); - switch_off_tag_rwd(); - LEDsoff(); -} - -int legic_select_card_iv(legic_card_select_t *p_card, uint8_t iv){ - - if ( p_card == NULL ) return 1; - - p_card->tagtype = setup_phase_reader(iv); - - switch(p_card->tagtype) { - case 0x0d: - p_card->cmdsize = 6; - p_card->addrsize = 5; - p_card->cardsize = 22; - break; - case 0x1d: - p_card->cmdsize = 9; - p_card->addrsize = 8; - p_card->cardsize = 256; - break; - case 0x3d: - p_card->cmdsize = 11; - p_card->addrsize = 10; - p_card->cardsize = 1024; - break; - default: - p_card->cmdsize = 0; - p_card->addrsize = 0; - p_card->cardsize = 0; - return 2; - } - return 0; -} -int legic_select_card(legic_card_select_t *p_card){ - return legic_select_card_iv(p_card, 0x01); -} - -//----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- - -void LegicRfInfo(void){ - - int r; - - uint8_t buf[sizeof(legic_card_select_t)] = {0x00}; - legic_card_select_t *card = (legic_card_select_t*) buf; - - LegicCommonInit(false); - - if ( legic_select_card(card) ) { - cmd_send(CMD_ACK,0,0,0,0,0); - goto OUT; - } - - // read UID bytes - for ( uint8_t i = 0; i < sizeof(card->uid); ++i) { - r = legic_read_byte(i, card->cmdsize); - if ( r == -1 ) { - cmd_send(CMD_ACK,0,0,0,0,0); - goto OUT; - } - card->uid[i] = r & 0xFF; - } - - // MCC byte. - r = legic_read_byte(4, card->cmdsize); - uint32_t calc_mcc = CRC8Legic(card->uid, 4);; - if ( r != calc_mcc) { - cmd_send(CMD_ACK,0,0,0,0,0); - goto OUT; - } - - // OK - cmd_send(CMD_ACK, 1, 0, 0, buf, sizeof(legic_card_select_t)); - -OUT: - switch_off_tag_rwd(); - LEDsoff(); -} - -} - -} - + cmd_send(CMD_ACK, 0, 0, 0, 0, 0); //TODO Implement } void LegicRfSimulate(int phase, int frame, int reqresp) { diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index f86820525980ac5282b8c35eb476d489ad7d7c98..549ae0297b0391dbccebc28e88fa7353bf0d7651 100644 GIT binary patch literal 42175 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 literal 42175 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%0D#K|9 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 Date: Sun, 29 Jul 2018 23:58:43 +0200 Subject: [PATCH 7/8] change: legic reader tx back to bigbang I've tried to modulate the Legic specific pause-puls using ssc and the default ssc clock of 105.4 kHz (bit periode of 9.4us) - previous commit. However, the timing was not precise enough. By increasing the ssc clock this could be circumvented, but the adventage over bitbang would be little. --- armsrc/legicrf.c | 117 ++++++++++++----------------------------------- 1 file changed, 29 insertions(+), 88 deletions(-) diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 7837322e0..e9f05a5a7 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -16,16 +16,6 @@ #include "legic_prng.h" /* legic PRNG impl */ #include "legic.h" /* legic_card_select_t struct */ -struct legic_frame { - uint32_t bits; /* length of frame */ - uint8_t data[24]; /* preprocessed bits */ -}; - -union frame_encoder { - uint32_t uint32; /* SAM7S512 does not support unaligned access as a */ - uint8_t uint8[4]; /* workaround 32bit values are converted to 4x8bit */ -}; - static uint8_t* legic_mem; /* card memory, used for read, write and sim */ static legic_card_select_t card;/* metadata of currently selected card */ static crc_t legic_crc; @@ -41,7 +31,7 @@ static int32_t input_threshold; /* values > threshold are 1 else 0 */ #define LEGIC_CARD_MEMSIZE 1024 /* The largest Legic Prime card is 1k */ //----------------------------------------------------------------------------- -// I/O interface abstraction (ARM <-> FPGA) +// I/O interface abstraction (FPGA -> ARM) //----------------------------------------------------------------------------- static inline uint8_t rx_byte_from_fpga() { @@ -55,18 +45,6 @@ static inline uint8_t rx_byte_from_fpga() { } } -static inline void tx_byte_to_fpga(uint8_t byte) { - for(;;) { - WDT_HIT(); - - // put byte into tx holding register as soon as it is ready - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = byte; - return; - } - } -} - //----------------------------------------------------------------------------- // Demodulation //----------------------------------------------------------------------------- @@ -118,50 +96,26 @@ static inline bool rx_bit() { //----------------------------------------------------------------------------- // Modulation // -// Modulation is a little bit more tricky as demedulation ssp_clk is running at -// 105.4 kHz resulting in a ssc bit periode of 9.4us and ssc frame periode of -// 76us. Legic has a strange pause-puls modulation with a 60us 0-bit and 100us -// 1-bit periode. The following functions use bit stuffing to aproximate the -// modulation. The default state of all bits is 1 the. The code adds pauses: -// - A 1 is aproximated by b1100000000 = 0x0300 -// - A 0 is aproximated by b110000 = 0x0030 -// -// Note: The modulator expect to be run on a little-endian system and the frame -// length to not exeed 11 bits. The frame length is not checked. +// I've tried to modulate the Legic specific pause-puls using ssc and the default +// ssc clock of 105.4 kHz (bit periode of 9.4us) - previous commit. However, +// the timing was not precise enough. By increasing the ssc clock this could +// be circumvented, but the adventage over bitbang would be little. //----------------------------------------------------------------------------- -static void clean_frame(struct legic_frame *f) { - memset(f->data, 0xff, sizeof(f->data)); +static inline void tx_bit(bool bit) { + uint32_t ts = GetCountUS(); - // add end of frame pause - f->data[0] ^= 0x03; - f->bits = 2; -} - -static void append_to_frame(struct legic_frame *f, uint8_t bit) { - uint8_t bit_pos = f->bits % 8; // calculate bits used in partially used byte - uint8_t byte_pos = f->bits / 8; // calculate next free or partially used byte - - static union frame_encoder frame_encoder; + // insert pause + LOW(GPIO_SSC_DOUT); + while(GetCountUS() < ts + RWD_TIME_PAUSE) { }; + HIGH(GPIO_SSC_DOUT); + // return to high, wait for bit periode to end if(bit) { - frame_encoder.uint32 = 0x0300 << bit_pos; // appended bits at bit_pos - f->bits += 10; // store amount of bits appended + while(GetCountUS() < ts + RWD_TIME_1) { }; } else { - frame_encoder.uint32 = 0x0030 << bit_pos; // appended bits at bit_pos - f->bits += 6; // store amount of bits appended + while(GetCountUS() < ts + RWD_TIME_0) { }; } - - // Move data from encoder to frame. This d-tour is necessary bacause the uC - // does not support unaligned access. We use bitwise not and xor to flip bits. - for(uint8_t i = 0; i < sizeof(frame_encoder); ++i) { - f->data[i + byte_pos] ^= frame_encoder.uint8[i]; - } -} - -static uint8_t finalize_frame(struct legic_frame *f) { - // convert bits into full bytes - return (f->bits + 7) / 8; } //----------------------------------------------------------------------------- @@ -175,33 +129,20 @@ static uint8_t finalize_frame(struct legic_frame *f) { //----------------------------------------------------------------------------- static void tx_frame(uint32_t frame, uint8_t len) { - static struct legic_frame legic_frame; - clean_frame(&legic_frame); - - // add bit by bit to frame, MSB (last bit on air) first, this reverses the order - // reverse order keeps last pause aligned to byte boundry and in sync with ret - // of last tx_byte_to_fpga call. this in turn syncs our rx phase perfectly. - while(len > 0) { - uint8_t lsb = (frame >> --len) & 0x01; - append_to_frame(&legic_frame, lsb ^ legic_prng_get_bit()); - legic_prng_forward(1); - } - - // finalize frame, returns length in bytes - len = finalize_frame(&legic_frame); - - // start tx with first frame preloaded - tx_byte_to_fpga(legic_frame.data[--len]); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); // transmit frame, MSB first - while(len > 0) { - tx_byte_to_fpga(legic_frame.data[--len]); - } + for(uint8_t i = 0; i < len; ++i) { + bool bit = (frame >> i) & 0x01; + tx_bit(bit ^ legic_prng_get_bit()); + legic_prng_forward(1); + }; - // tx queue has 2 cycles, add 2 empty frames to leave function in sync - tx_byte_to_fpga(0xff); // blocks until last frame is loaded into shift register - tx_byte_to_fpga(0xff); // blocks until last frame is done + // add pause to mark end of the frame + uint32_t ts = GetCountUS(); + LOW(GPIO_SSC_DOUT); + while(GetCountUS() < ts + RWD_TIME_PAUSE) { }; + HIGH(GPIO_SSC_DOUT); } static uint32_t rx_frame(uint8_t len) { @@ -258,13 +199,13 @@ static void init_reader(bool clear_mem) { | FPGA_HF_READER_RX_XCORR_QUARTER); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - // configure SSC with defaults (note: defaults are MSB first - Legic has LSB first, - // the rx stream is bit stuff in reverse to fix this. However, reversing the order - // will align the last pause to a byte boundry and we want that for synchronisation. + // configure SSC with defaults FpgaSetupSsc(); - // and additonaly set Data Default to 1, to prevent glitches when switching to tx. - AT91C_BASE_SSC->SSC_TFMR |= AT91C_SSC_DATDEF; + // re-claim GPIO_SSC_DOUT as GPIO and enable output + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + HIGH(GPIO_SSC_DOUT); // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. legic_mem = BigBuf_get_EM_addr(); From 058426fa17eacaceb82b04be667785c297329243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Dr=C3=B6scher?= Date: Sat, 4 Aug 2018 23:22:57 +0200 Subject: [PATCH 8/8] change: added rx/tx coordination timestamp --- armsrc/legicrf.c | 57 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index e9f05a5a7..c04f742e0 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -21,10 +21,28 @@ static legic_card_select_t card;/* metadata of currently selected card */ static crc_t legic_crc; static int32_t input_threshold; /* values > threshold are 1 else 0 */ -// LEGIC RF is using the common timer functions: StartCountUS() and GetCountUS() +//----------------------------------------------------------------------------- +// Frame timing and pseudorandom number generator +// +// The Prng is forwarded every 100us (TAG_BIT_PERIOD), except when the reader is +// transmitting. In that case the prng has to be forwarded every bit transmitted: +// - 60us for a 0 (RWD_TIME_0) +// - 100us for a 1 (RWD_TIME_1) +// +// The data dependent timing makes writing comprehensible code significantly +// harder. The current aproach forwards the prng data based if there is data on +// air and time based, using GetCountUS(), during computational and wait periodes. +// +// To not have the necessity to calculate/guess exection time dependend timeouts +// tx_frame and rx_frame use a shared timestamp to coordinate tx and rx timeslots. +//----------------------------------------------------------------------------- + +static int32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ + #define RWD_TIME_PAUSE 20 /* 20us */ #define RWD_TIME_1 100 /* READER_TIME_PAUSE 20us off + 80us on = 100us */ #define RWD_TIME_0 60 /* READER_TIME_PAUSE 20us off + 40us on = 60us */ +#define RWD_FRAME_WAIT 220 /* 220us from TAG frame end to READER frame start */ #define TAG_FRAME_WAIT 330 /* 330us from READER frame end to TAG frame start */ #define TAG_BIT_PERIOD 100 /* 100us */ @@ -73,9 +91,8 @@ static inline int32_t sample_power() { // An aproximated power measurement is available every 18.9us. The bit time // is 100us. The code samples 5 times and uses samples 3 and 4. // -// Note: The demodulator is drifting (18.9us * 5 = 94.5us), since the longest -// respons is 12 bits, the demodulator will stay in sync with a margin of -// error of 20us left. Sending the next request will resync the card. +// Note: The demodulator would be drifting (18.9us * 5 != 100us), rx_frame +// has a delay loop that aligns rx_bit calls to the TAG tx timeslots. static inline bool rx_bit() { static int32_t p[5]; for(size_t i = 0; i<5; ++i) { @@ -131,6 +148,10 @@ static inline void tx_bit(bool bit) { static void tx_frame(uint32_t frame, uint8_t len) { FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + // wait for next tx timeslot + last_frame_end += RWD_FRAME_WAIT; + while(GetCountUS() < last_frame_end) { }; + // transmit frame, MSB first for(uint8_t i = 0; i < len; ++i) { bool bit = (frame >> i) & 0x01; @@ -143,6 +164,9 @@ static void tx_frame(uint32_t frame, uint8_t len) { LOW(GPIO_SSC_DOUT); while(GetCountUS() < ts + RWD_TIME_PAUSE) { }; HIGH(GPIO_SSC_DOUT); + + // update coordination timestamp + last_frame_end = GetCountUS(); } static uint32_t rx_frame(uint8_t len) { @@ -150,12 +174,23 @@ static uint32_t rx_frame(uint8_t len) { | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_QUARTER); + // hold sampling until card is expect to respond + last_frame_end += TAG_FRAME_WAIT; + while(GetCountUS() < last_frame_end) { }; + uint32_t frame = 0; for(uint8_t i = 0; i < len; i++) { frame |= (rx_bit() ^ legic_prng_get_bit()) << i; legic_prng_forward(1); + + // rx_bit runs only 95us, resync to TAG_BIT_PERIOD + last_frame_end += TAG_BIT_PERIOD; + while(GetCountUS() < last_frame_end) { }; } + // update coordination timestamp + last_frame_end = GetCountUS(); + return frame; } @@ -231,7 +266,8 @@ static void init_reader(bool clear_mem) { // - Receive card type 6 bits // - Acknowledge frame 6 bits static uint32_t setup_phase_reader(uint8_t iv) { - uint32_t ts = GetCountUS(); + // init coordination timestamp + last_frame_end = GetCountUS(); // Switch on carrier and let the card charge for 5ms. // Use the time to calibrate the treshhold. @@ -241,24 +277,21 @@ static uint32_t setup_phase_reader(uint8_t iv) { if(sample > input_threshold) { input_threshold = sample; } - } while(GetCountUS() < ts + 5000); + } while(GetCountUS() < last_frame_end + 5000); // Set threshold to noise floor * 2 input_threshold <<= 1; legic_prng_init(0); tx_frame(iv, 7); - ts = GetCountUS(); // configure iv legic_prng_init(iv); legic_prng_forward(2); - // wait until card is expect to respond - while(GetCountUS() < ts + TAG_FRAME_WAIT) { }; - // receive card type int32_t card_type = rx_frame(6); + legic_prng_forward(3); // send obsfuscated acknowledgment frame switch (card_type) { @@ -284,7 +317,9 @@ static int16_t read_byte(uint16_t index, uint8_t cmd_sz) { uint16_t cmd = (index << 1) | LEGIC_READ; // read one byte + legic_prng_forward(2); tx_frame(cmd, cmd_sz); + legic_prng_forward(2); uint32_t frame = rx_frame(12); // split frame into data and crc @@ -298,6 +333,8 @@ static int16_t read_byte(uint16_t index, uint8_t cmd_sz) { return -1; } + legic_prng_forward(1); + return byte; }