diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 0e56918a5..6efe5808d 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -1566,8 +1566,8 @@ void ReaderHitag(hitag_function htf, hitag_data *htd) { LogTrace(rx, nbytes(rxlen), response_start, response_start + response_duration, NULL, false); // TODO when using cumulative time for command_start, pm3 doesn't reply anymore, e.g. on lf hitag read 23 4F4E4D494B52 -// command_start = response_start + response_duration; - command_start = 0; + command_start = response_start + response_duration; +// command_start = 0; // Dbhexdump(nbytes(rxlen), rx, false); } } @@ -1590,25 +1590,35 @@ void WriterHitag(hitag_function htf, hitag_data *htd, int page) { StopTicks(); - // int frame_count = 0; - int response = 0; + int frame_count = 0; + uint32_t command_start = 0; + uint32_t command_duration = 0; + uint32_t response_start = 0; + uint32_t response_duration = 0; uint8_t rx[HITAG_FRAME_LEN]; size_t rxlen = 0; uint8_t txbuf[HITAG_FRAME_LEN]; uint8_t *tx = txbuf; size_t txlen = 0; - int lastbit; - int reset_sof; - int t_wait = HITAG_T_WAIT_MAX; - bool bStop; + int t_wait_1; + int t_wait_1_guard = 8; + int t_wait_2; + size_t tag_size; + bool bStop = false; - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - set_tracing(true); - clear_trace(); + // Raw demodulation/decoding by sampling edge periods + size_t periods = 0; // Reset the return status bSuccessful = false; + bCrypto = false; + // Clean up trace and prepare it for storing frames + set_tracing(true); + clear_trace(); + + DbpString("Starting Hitag writer family"); + // Check configuration switch (htf) { case WHT2F_CRYPTO: { @@ -1642,221 +1652,227 @@ void WriterHitag(hitag_function htf, hitag_data *htd, int page) { } LED_D_ON(); + hitag2_init(); - // Configure output and enable pin that is connected to the FPGA (for modulating) - AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT; - - // Set fpga in edge detect with reader field, we can modulate as reader now - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - - // Disable modulation at default, which means enable the field - LOW(GPIO_SSC_DOUT); - - // Enable Peripheral Clock for - // TIMER_CLOCK0, used to measure exact timing before answering - // TIMER_CLOCK1, used to capture edges of the tag frames - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); - - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // TC0: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; - - // TC1: Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, - // external trigger rising edge, load RA on falling edge of TIOA. - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK - | AT91C_TC_ETRGEDG_FALLING - | AT91C_TC_ABETRG - | AT91C_TC_LDRA_FALLING; - - // Enable and reset counters - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - while (AT91C_BASE_TC0->TC_CV > 0) {}; - - // Reset the received frame, frame count and timing info - lastbit = 1; - bStop = false; - + // init as reader + lf_init(true); + // Tag specific configuration settings (sof, timings, etc.) if (htf < 10) { // hitagS settings - reset_sof = 1; - t_wait = 200; + t_wait_1 = 204; + t_wait_2 = 128; + /*tag_size = 256;*/ + flipped_bit = 0; + tag_size = 8; + DbpString("Configured for hitagS writer"); } else if (htf < 20) { - // hitag1 settings - reset_sof = 1; - t_wait = 200; + // hitag1 settings + t_wait_1 = 204; + t_wait_2 = 128; + tag_size = 256; + flipped_bit = 0; + DbpString("Configured for hitag1 writer"); } else if (htf < 30) { - // hitag2 settings - reset_sof = 4; - t_wait = HITAG_T_WAIT_2_MIN; + // hitag2 settings + t_wait_1 = HITAG_T_WAIT_1_MIN; + t_wait_2 = HITAG_T_WAIT_2_MIN; + tag_size = 48; + DbpString("Configured for hitag2 writer"); } else { - Dbprintf("Error, unknown hitag reader type: %d", htf); + Dbprintf("Error, unknown hitag writer type: %d", htf); return; } + uint8_t tag_modulation; + size_t max_nrzs = (8 * HITAG_FRAME_LEN + 5) * 2; // up to 2 nrzs per bit + uint8_t nrz_samples[max_nrzs]; + size_t nrzs = 0; + while (!bStop && !BUTTON_PRESS() && !data_available()) { WDT_HIT(); - // Check if frame was captured and store it - if (rxlen > 0) { - // frame_count++; - LogTrace(rx, nbytes(rxlen), response, response, NULL, false); - } - // By default reset the transmission buffer tx = txbuf; switch (htf) { case WHT2F_CRYPTO: { bStop = !hitag2_crypto(rx, rxlen, tx, &txlen, true); + break; } - break; case WHT2F_PASSWORD: { bStop = !hitag2_password(rx, rxlen, tx, &txlen, true); + break; } - break; default: { Dbprintf("Error, unknown function: %d", htf); - return; + goto out; } - break; } - // Send and store the reader command - // Disable timer 1 with external trigger to avoid triggers during our own modulation - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // Wait for HITAG_T_WAIT_2 carrier periods after the last tag bit before transmitting, - // Since the clock counts since the last falling edge, a 'one' means that the - // falling edge occurred halfway the period. with respect to this falling edge, - // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. - // All timer values are in terms of T0 units - while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * (t_wait + (HITAG_T_TAG_HALF_PERIOD * lastbit))) {}; + // Wait for t_wait_2 carrier periods after the last tag bit before transmitting, + lf_wait_periods(t_wait_2); + command_start += t_wait_2; // Transmit the reader frame - hitag_reader_send_frame(tx, txlen); + command_duration = hitag_reader_send_frame(tx, txlen); + + response_start = command_start + command_duration; - // Enable and reset external trigger in timer for capturing future frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + // Let the antenna and ADC values settle + // And find the position where edge sampling should start + lf_wait_periods(t_wait_1 - t_wait_1_guard); + response_start += t_wait_1 - t_wait_1_guard; - // Add transmitted frame to total count + // Keep administration of the first edge detection + bool waiting_for_first_edge = true; + + // Did we detected any modulaiton at all + bool detected_tag_modulation = false; + + // Use the current modulation state as starting point + tag_modulation = lf_get_tag_modulation(); + + // Reset the number of NRZ samples and use edge detection to detect them + nrzs = 0; + while (nrzs < max_nrzs) { + // Get the timing of the next edge in number of wave periods + periods = lf_count_edge_periods(128); + + // Are we dealing with the first incoming edge + if (waiting_for_first_edge) { + // Just break out of loop after an initial time-out (tag is probably not available) + if (periods == 0) break; + if (tag_modulation == 0) { + // hitag replies always start with 11111 == 1010101010, if we see 0 + // it means we missed the first period, e.g. if the signal never crossed 0 since reader signal + // so let's add it: + nrz_samples[nrzs++] = tag_modulation ^ 1; + // Register the number of periods that have passed + // we missed the begin of response but we know it happened one period of 16 earlier + response_start += periods - 16; + response_duration = response_start; + } else { + // Register the number of periods that have passed + response_start += periods; + response_duration = response_start; + } + // Indicate that we have dealt with the first edge + waiting_for_first_edge = false; + // The first edge is always a single NRZ bit, force periods on 16 + periods = 16; + // We have received more than 0 periods, so we have detected a tag response + detected_tag_modulation = true; + } else { + // The function lf_count_edge_periods() returns 0 when a time-out occurs + if (periods == 0) { + //Dbprintf("Detected timeout after [%d] nrz samples", nrzs); + break; + } + } + // Evaluate the number of periods before the next edge + if (periods > 24 && periods <= 64) { + // Detected two sequential equal bits and a modulation switch + // NRZ modulation: (11 => --|) or (11 __|) + nrz_samples[nrzs++] = tag_modulation; + nrz_samples[nrzs++] = tag_modulation; + response_duration += periods; + // Invert tag modulation state + tag_modulation ^= 1; + } else if (periods > 0 && periods <= 24) { + // Detected one bit and a modulation switch + // NRZ modulation: (1 => -|) or (0 _|) + nrz_samples[nrzs++] = tag_modulation; + response_duration += periods; + tag_modulation ^= 1; + } else { + // The function lf_count_edge_periods() returns > 64 periods, this is not a valid number periods + //Dbprintf("Detected unexpected period count: %d", periods); + break; + } + } + + // Wait some extra time for flash to be programmed + // + + // Store the TX frame, we do this now at this point, to avoid delay in processing + // and to be able to overwrite the first samples with the trace (since they currently + // still use the same memory space) if (txlen > 0) { - // frame_count++; - LogTrace(tx, nbytes(txlen), HITAG_T_WAIT_2_MIN, HITAG_T_WAIT_2_MIN, NULL, true); + frame_count++; + LogTrace(tx, nbytes(txlen), command_start, command_start + command_duration, NULL, true); } // Reset values for receiving frames memset(rx, 0x00, sizeof(rx)); rxlen = 0; - lastbit = 1; - bool bSkip = true; - int tag_sof = reset_sof; - response = 0; - uint32_t errorCount = 0; - // Receive frame, watch for at most T0*EOF periods - while (AT91C_BASE_TC1->TC_CV < HITAG_T0 * HITAG_T_WAIT_MAX) { - // Check if falling edge in tag modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / HITAG_T0); + // If there is no response, just repeat the loop + if (!detected_tag_modulation) continue; - // Reset timer every frame, we have to capture the last edge for timing - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - - LED_B_ON(); - - // Capture tag frame (manchester decoding using only falling edges) - if (ra >= HITAG_T_EOF) { - if (rxlen != 0) { - //Dbprintf("DEBUG: Wierd1"); - } - // Capture the T0 periods that have passed since last communication or field drop (reset) - // We always recieve a 'one' first, which has the falling edge after a half period |-_| - response = ra - HITAG_T_TAG_HALF_PERIOD; - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { - // Manchester coding example |-_|_-|-_| (101) - - // need to test to verify we don't exceed memory... - // if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) { - // break; - // } - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { - // Manchester coding example |_-|...|_-|-_| (0...01) - - // need to test to verify we don't exceed memory... - // if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) { - // break; - // } - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - // We have to skip this half period at start and add the 'one' the second time - if (!bSkip) { - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } - lastbit = !lastbit; - bSkip = !bSkip; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { - // Manchester coding example |_-|_-| (00) or |-_|-_| (11) - - // need to test to verify we don't exceed memory... - // if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) { - // break; - // } - if (tag_sof) { - // Ignore bits that are transmitted during SOF - tag_sof--; - } else { - // bit is same as last bit - rx[rxlen / 8] |= lastbit << (7 - (rxlen % 8)); - rxlen++; - } - } else { - // Dbprintf("DEBUG: Wierd2"); - errorCount++; - // Ignore wierd value, is to small to mean anything - } - } - // if we saw over 100 wierd values break it probably isn't hitag... - if (errorCount > 100) break; - - // We can break this loop if we received the last bit from a frame - if (AT91C_BASE_TC1->TC_CV > HITAG_T0 * HITAG_T_EOF) { - if (rxlen > 0) break; - } + // Make sure we always have an even number of samples. This fixes the problem + // of ending the manchester decoding with a zero. See the example below where + // the '|' character is end of modulation + // One at the end: ..._-|_____... + // Zero at the end: ...-_|_____... + // The last modulation change of a zero is not detected, but we should take + // the half period in account, otherwise the demodulator will fail. + if ((nrzs % 2) != 0) { + nrz_samples[nrzs++] = tag_modulation; } - // Wait some extra time for flash to be programmed - if ((rxlen == 0) && (writestate == WRITE_STATE_PROG)) { - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * (HITAG_T_PROG - HITAG_T_WAIT_MAX)); + LED_B_ON(); + + // decode bitstream + manrawdecode((uint8_t *)nrz_samples, &nrzs, true, 0); + + // decode frame + + // Verify if the header consists of five consecutive ones + if (nrzs < 5) { + break; + } else { + size_t i; + for (i = 0; i < 5; i++) { + if (nrz_samples[i] != 1) { + Dbprintf("Detected incorrect header, the bit [%d] is zero instead of one, abort", i); + break; + } + } + if (i < 5) break; + } + + // Pack the response into a byte array + for (size_t i = 5; i < nrzs; i++) { + uint8_t bit = nrz_samples[i]; + if (bit > 1) { // When Manchester detects impossible symbol it writes "7" + break; + } + rx[rxlen / 8] |= bit << (7 - (rxlen % 8)); + rxlen++; + } + + if (rxlen % 8 == 1) // skip spurious bit + rxlen--; + + // Check if frame was captured and store it + if (rxlen > 0) { + frame_count++; + + LogTrace(rx, nbytes(rxlen), response_start, response_start + response_duration, NULL, false); + command_start = 0; } } - LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - set_tracing(false); - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; +out: + lf_finalize(); + Dbprintf("TX/RX frames recorded: %u", frame_count); + // release allocated memory from BigBuff. + BigBuf_free(); StartTicks(); - - reply_mix(CMD_ACK, bSuccessful, 0, 0, (uint8_t *)tag.sectors, 48); + + reply_mix(CMD_ACK, bSuccessful, 0, 0, (uint8_t *)tag.sectors, tag_size); }