//----------------------------------------------------------------------------- // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // See LICENSE.txt for the text of the license. //----------------------------------------------------------------------------- // LF ADC read/write implementation //----------------------------------------------------------------------------- #include "lfadc.h" #include "lfsampling.h" #include "fpgaloader.h" #include "ticks.h" #include "dbprint.h" #include "appmain.h" // Sam7s has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS_TIMER_DIV1_CLOCK) // TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz // Carrier periods (T0) have duration of 8 microseconds (us), which is 1/125000 per second // T0 = TIMER_CLOCK1 / 125000 = 192 //#define T0 192 // Sam7s has three counters, we will use the first TIMER_COUNTER_0 (aka TC0) // using TIMER_CLOCK3 (aka AT91C_TC_CLKS_TIMER_DIV3_CLOCK) // as a counting signal. TIMER_CLOCK3 = MCK/32, MCK is running at 48 MHz, so the timer is running at 48/32 = 1500 kHz // Carrier period (T0) have duration of 8 microseconds (us), which is 1/125000 per second (125 kHz frequency) // T0 = timer/carrier = 1500kHz/125kHz = 1500000/125000 = 6 //#define HITAG_T0 3 ////////////////////////////////////////////////////////////////////////////// // Exported global variables ////////////////////////////////////////////////////////////////////////////// bool g_logging = true; ////////////////////////////////////////////////////////////////////////////// // Global variables ////////////////////////////////////////////////////////////////////////////// static bool rising_edge = false; static bool reader_mode = false; ////////////////////////////////////////////////////////////////////////////// // Auxiliary functions ////////////////////////////////////////////////////////////////////////////// bool lf_test_periods(size_t expected, size_t count) { // Compute 10% deviation (integer operation, so rounded down) size_t diviation = expected / 10; return ((count > (expected - diviation)) && (count < (expected + diviation))); } ////////////////////////////////////////////////////////////////////////////// // Low frequency (LF) adc passthrough functionality ////////////////////////////////////////////////////////////////////////////// static uint8_t previous_adc_val = 0; //0xFF; static uint8_t adc_avg = 0; uint8_t get_adc_avg(void) { return adc_avg; } void lf_sample_mean(void) { uint8_t periods = 0; uint32_t adc_sum = 0; while (periods < 32) { if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { adc_sum += AT91C_BASE_SSC->SSC_RHR; periods++; } } // division by 32 adc_avg = adc_sum >> 5; previous_adc_val = adc_avg; if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("LF ADC average %u", adc_avg); } } static size_t lf_count_edge_periods_ex(size_t max, bool wait, bool detect_gap) { #define LIMIT_DEV 20 // timeout limit to 100 000 w/o uint32_t timeout = 100000; size_t periods = 0; uint8_t avg_peak = adc_avg + LIMIT_DEV; uint8_t avg_through = adc_avg - LIMIT_DEV; while (BUTTON_PRESS() == false) { WDT_HIT(); timeout--; if (timeout == 0) { return 0; } if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x00; continue; } if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { periods++; // reset timeout timeout = 100000; volatile uint8_t adc_val = AT91C_BASE_SSC->SSC_RHR; if (g_logging) { logSampleSimple(adc_val); } // Only test field changes if state of adc values matter if (wait == false) { // Test if we are locating a field modulation (100% ASK = complete field drop) if (detect_gap) { // Only return when the field completely disappeared if (adc_val == 0) { return periods; } } else { // Trigger on a modulation swap by observing an edge change if (rising_edge) { if ((previous_adc_val > avg_peak) && (adc_val <= previous_adc_val)) { rising_edge = false; return periods; } } else { if ((previous_adc_val < avg_through) && (adc_val >= previous_adc_val)) { rising_edge = true; return periods; } } } } previous_adc_val = adc_val; if (periods >= max) { return 0; } } } if (g_logging) { logSampleSimple(0xFF); } return 0; } size_t lf_count_edge_periods(size_t max) { return lf_count_edge_periods_ex(max, false, false); } size_t lf_detect_gap(size_t max) { return lf_count_edge_periods_ex(max, false, true); } void lf_reset_counter(void) { // TODO: find out the correct reset settings for tag and reader mode // if (reader_mode) { // Reset values for reader mode rising_edge = false; previous_adc_val = 0xFF; // } else { // Reset values for tag/transponder mode // rising_edge = false; // previous_adc_val = 0xFF; // } } bool lf_get_tag_modulation(void) { return (rising_edge == false); } bool lf_get_reader_modulation(void) { return rising_edge; } void lf_wait_periods(size_t periods) { // wait detect gap lf_count_edge_periods_ex(periods, true, false); } void lf_init(bool reader, bool simulate, bool ledcontrol) { StopTicks(); reader_mode = reader; FpgaDownloadAndGo(FPGA_BITSTREAM_LF); sample_config *sc = getSamplingConfig(); sc->decimation = 1; sc->averaging = 0; FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); if (reader) { FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); } else { if (simulate) { FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC); } else { // Sniff FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC); // FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE); } } // Connect the A/D to the peak-detected low-frequency path. SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Now set up the SSC to get the ADC samples that are now streaming at us. FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); // When in reader mode, give the field a bit of time to settle. // 313T0 = 313 * 8us = 2504us = 2.5ms Hitag2 tags needs to be fully powered. SpinDelay(10); // Steal this pin from the SSP (SPI communication channel with fpga) and use it to control the modulation AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; LOW(GPIO_SSC_DOUT); // Enable peripheral Clock for TIMER_CLOCK 0 AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV4_CLOCK; // Enable peripheral Clock for TIMER_CLOCK 1 AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV4_CLOCK; // Clear all leds if (ledcontrol) LEDsoff(); // Reset and enable timers AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // Assert a sync signal. This sets all timers to 0 on next active clock edge AT91C_BASE_TCB->TCB_BCR = 1; // Prepare data trace uint32_t bufsize = 10000; // use malloc if (g_logging) { initSampleBufferEx(&bufsize, true); } lf_sample_mean(); } void lf_finalize(bool ledcontrol) { // Disable timers AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // return stolen pin to SSP AT91C_BASE_PIOA->PIO_PDR = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_ASR = GPIO_SSC_DIN | GPIO_SSC_DOUT; FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); if (ledcontrol) LEDsoff(); StartTicks(); } size_t lf_detect_field_drop(size_t max) { /* size_t periods = 0; // int16_t checked = 0; while (BUTTON_PRESS() == false) { // // only every 1000th times, in order to save time when collecting samples. // if (checked == 4000) { // if (data_available()) { // checked = -1; // break; // } else { // checked = 0; // } // } // ++checked; WDT_HIT(); if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { periods++; volatile uint8_t adc_val = AT91C_BASE_SSC->SSC_RHR; if (g_logging) logSampleSimple(adc_val); if (adc_val == 0) { rising_edge = false; return periods; } if (periods == max) return 0; } } */ return 0; } void lf_modulation(bool modulation) { if (modulation) { HIGH(GPIO_SSC_DOUT); } else { LOW(GPIO_SSC_DOUT); } } // simulation static void lf_manchester_send_bit(uint8_t bit) { lf_modulation(bit != 0); lf_wait_periods(16); lf_modulation(bit == 0); lf_wait_periods(32); } // simulation bool lf_manchester_send_bytes(const uint8_t *frame, size_t frame_len, bool ledcontrol) { if (ledcontrol) LED_B_ON(); lf_manchester_send_bit(1); lf_manchester_send_bit(1); lf_manchester_send_bit(1); lf_manchester_send_bit(1); lf_manchester_send_bit(1); // Send the content of the frame for (size_t i = 0; i < frame_len; i++) { lf_manchester_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); } if (ledcontrol) LED_B_OFF(); return true; }