+ // 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 */
+ 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();
+ 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;
+ }
+ }
+
+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();
+
+ if ( legic_select_card_iv(&card, iv) ) {
+ isOK = 0;
+ goto OUT;
+ }
+
+ if (len + offset > card.cardsize)
+ len = card.cardsize - offset;
+
+ 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;