+ // Switch on carrier and let the tag charge for 1ms
+ HIGH(GPIO_SSC_DOUT);
+ WaitUS(5000);
+
+ ResetTicks();
+
+ // no keystream yet
+ legic_prng_init(0);
+
+ // send IV handshake
+ frame_sendAsReader(iv, 7);
+
+ // Now both tag and reader has same IV. Prng can start.
+ 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;
+}
+
+static void LegicCommonInit(void) {
+
+ FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);
+ SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
+
+ /* Bitbang the transmitter */
+ LOW(GPIO_SSC_DOUT);
+ AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
+ AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT;
+
+ // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier.
+ cardmem = BigBuf_get_EM_addr();
+ 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) {
+ LOW(GPIO_SSC_DOUT);
+ 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: expected %x but got %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
+ */
+int legic_write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) {
+
+ // crc
+ crc_clear(&legic_crc);
+ crc_update(&legic_crc, 0, 1); /* CMD_WRITE */
+ crc_update(&legic_crc, index, addr_sz);
+ crc_update(&legic_crc, byte, 8);
+ uint32_t crc = crc_finish(&legic_crc);
+ uint32_t crc2 = legic4Crc(LEGIC_WRITE, index, byte, addr_sz+1);
+ if ( crc != crc2 ) {
+ Dbprintf("crc is missmatch");
+ return 1;
+ }
+ // send write command
+ uint32_t cmd = ((crc <<(addr_sz+1+8)) //CRC
+ |(byte <<(addr_sz+1)) //Data
+ |(index <<1) //index
+ | LEGIC_WRITE); //CMD = Write
+
+ uint32_t cmd_sz = addr_sz+1+8+4; //crc+data+cmd
+
+ legic_prng_forward(2);
+
+ WaitTicks(330);
+
+ frame_sendAsReader(cmd, cmd_sz);
+
+ // wait for ack
+ AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN;
+ AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN;
+
+ int t, old_level = 0, edges = 0;
+ int next_bit_at = 0;
+
+ // ACK 3.6ms = 3600us * 1.5 = 5400ticks.
+ WaitTicks(5360);
+
+ for( t = 0; t < 80; ++t) {
+ edges = 0;
+ next_bit_at += TAG_BIT_PERIOD;
+ while(timer->TC_CV < next_bit_at) {
+ volatile uint32_t level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN);
+ if(level != old_level)
+ edges++;
+
+ old_level = level;
+ }
+ /* expected are 42 edges (ONE) */
+ if(edges > 20 ) {
+ int t = timer->TC_CV;
+ int c = t / TAG_BIT_PERIOD;
+
+ ResetTimer(timer);
+ legic_prng_forward(c);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+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();
+
+ if ( legic_select_card_iv(&card, iv) ) {
+ isOK = 0;
+ goto OUT;
+ }
+
+ switch_off_tag_rwd();
+
+ if (len + offset >= card.cardsize)
+ len = card.cardsize - offset;
+
+ setup_phase_reader(iv);
+
+ 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;