From 31abe49fd323411b391dd10335eb4c60cfcecb06 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Fri, 30 Jan 2015 23:03:44 +0100 Subject: [PATCH] Some more fixes to longer lf recordings. Now also supports longer snoops, and an additional command 'lf config' has been defined, instead of having to specify all params for every call --- armsrc/Makefile | 2 +- armsrc/appmain.c | 14 +-- armsrc/apps.h | 4 +- armsrc/lfops.c | 241 +++++------------------------------------- armsrc/lfsampling.c | 251 ++++++++++++++++++++++++++++++++++++++++++++ armsrc/lfsampling.h | 59 +++++++++++ client/cmddata.c | 25 +++-- client/cmdlf.c | 201 +++++++++++++++++++++-------------- client/util.c | 9 +- include/usb_cmd.h | 11 ++ 10 files changed, 506 insertions(+), 311 deletions(-) create mode 100644 armsrc/lfsampling.c create mode 100644 armsrc/lfsampling.h diff --git a/armsrc/Makefile b/armsrc/Makefile index b9019541..4e460623 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -14,7 +14,7 @@ APP_CFLAGS = -DWITH_LF -DWITH_ISO15693 -DWITH_ISO14443a -DWITH_ISO14443b -DWITH_ #-DWITH_LCD #SRC_LCD = fonts.c LCD.c -SRC_LF = lfops.c hitag2.c +SRC_LF = lfops.c hitag2.c lfsampling.c SRC_ISO15693 = iso15693.c iso15693tools.c SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c SRC_ISO14443b = iso14443.c diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 83d9dd1f..32b7e592 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -23,7 +23,7 @@ #include "legicrf.h" #include - +#include "lfsampling.h" #ifdef WITH_LCD #include "LCD.h" #endif @@ -629,16 +629,17 @@ void UsbPacketReceived(uint8_t *packet, int len) switch(c->cmd) { #ifdef WITH_LF + case CMD_SET_LF_SAMPLING_CONFIG: + setSamplingConfig((sample_config *) c->d.asBytes); + break; case CMD_ACQUIRE_RAW_ADC_SAMPLES_125K: - AcquireRawAdcSamples125k(c->arg[0], c->arg[1], c->arg[2]); - cmd_send(CMD_ACK,0,0,0,0,0); + cmd_send(CMD_ACK,SampleLF(),0,0,0,0); break; case CMD_MOD_THEN_ACQUIRE_RAW_ADC_SAMPLES_125K: ModThenAcquireRawAdcSamples125k(c->arg[0],c->arg[1],c->arg[2],c->d.asBytes); break; case CMD_LF_SNOOP_RAW_ADC_SAMPLES: - SnoopLFRawAdcSamples(c->arg[0], c->arg[1]); - cmd_send(CMD_ACK,0,0,0,0,0); + cmd_send(CMD_ACK,SnoopLF(),0,0,0,0); break; case CMD_HID_DEMOD_FSK: CmdHIDdemodFSK(c->arg[0], 0, 0, 1); @@ -910,7 +911,8 @@ void UsbPacketReceived(uint8_t *packet, int len) cmd_send(CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K,i,len,0,((byte_t*)BigBuf)+c->arg[0]+i,len); } // Trigger a finish downloading signal with an ACK frame - cmd_send(CMD_ACK,bits_per_sample,decimation,0,0,0); + // We put a 1 in arg[0] to alert the host we're also sending sample_config + cmd_send(CMD_ACK,1,0,0,getSamplingConfig(),sizeof(sample_config)); LED_B_OFF(); break; diff --git a/armsrc/apps.h b/armsrc/apps.h index 110e03b3..89440bfe 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -81,8 +81,6 @@ int AvgAdc(int ch); void ToSendStuffBit(int b); void ToSendReset(void); void ListenReaderField(int limit); -void SnoopLFRawAdcSamples(int divisor, int trigger_threshold); -void DoAcquisition125k(int trigger_threshold); extern int ToSendMax; extern uint8_t ToSend[]; extern uint32_t BigBuf[]; @@ -147,7 +145,7 @@ extern uint8_t decimation; extern uint8_t bits_per_sample ; extern bool averaging; -void AcquireRawAdcSamples125k(int divisor,int arg1, int arg2); +void AcquireRawAdcSamples125k(int divisor); void ModThenAcquireRawAdcSamples125k(int delay_off,int period_0,int period_1,uint8_t *command); void ReadTItag(void); void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index c1031447..43af4eed 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -15,231 +15,44 @@ #include "crc16.h" #include "string.h" #include "lfdemod.h" +#include "lfsampling.h" -uint8_t decimation = 1; -uint8_t bits_per_sample = 8; -bool averaging = 1; - - -typedef struct { - uint8_t * buffer; - uint32_t numbits; - uint32_t position; -} BitstreamOut; -/** - * @brief Pushes bit onto the stream - * @param stream - * @param bit - */ -void pushBit( BitstreamOut* stream, uint8_t bit) -{ - int bytepos = stream->position >> 3; // divide by 8 - int bitpos = stream->position & 7; - *(stream->buffer+bytepos) |= (bit > 0) << (7 - bitpos); - stream->position++; - stream->numbits++; -} /** - * Does the sample acquisition. If threshold is specified, the actual sampling - * is not commenced until the threshold has been reached. - * This method implements decimation and quantization in order to - * be able to provide longer sample traces. - * Uses the following global settings: - * - decimation - how much should the signal be decimated. A decimation of N means we keep 1 in N samples, etc. - * - bits_per_sample - bits per sample. Max 8, min 1 bit per sample. - * - averaging If set to true, decimation will use averaging, so that if e.g. decimation is 3, the sample - * value that will be used is the average value of the three samples. - * - * @param trigger_threshold - a threshold. The sampling won't commence until this threshold has been reached. Set - * to -1 to ignore threshold. - * @param silent - is true, now outputs are made. If false, dbprints the status - * @return the number of bits occupied by the samples. + * Function to do a modulation and then get samples. + * @param delay_off + * @param period_0 + * @param period_1 + * @param command */ -uint32_t DoAcquisition125k_internal(int trigger_threshold,bool silent) -{ - //. - uint8_t *dest = (uint8_t *)BigBuf; - int bufsize = BIGBUF_SIZE; - memset(dest, 0, bufsize); - - if(bits_per_sample < 1) bits_per_sample = 1; - if(bits_per_sample > 8) bits_per_sample = 8; - - if(decimation < 1) decimation = 1; - - // Use a bit stream to handle the output - BitstreamOut data = { dest , 0, 0}; - int sample_counter = 0; - uint8_t sample = 0; - //If we want to do averaging - uint32_t sample_sum =0 ; - uint32_t sample_total_numbers =0 ; - uint32_t sample_total_saved =0 ; - - while(!BUTTON_PRESS()) { - WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - AT91C_BASE_SSC->SSC_THR = 0x43; - LED_D_ON(); - } - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - LED_D_OFF(); - if (trigger_threshold != -1 && sample < trigger_threshold) - continue; - - trigger_threshold = -1; - sample_total_numbers++; - - if(averaging) - { - sample_sum += sample; - } - //Check decimation - if(decimation > 1) - { - sample_counter++; - if(sample_counter < decimation) continue; - sample_counter = 0; - } - //Averaging - if(averaging && decimation > 1) { - sample = sample_sum / decimation; - sample_sum =0; - } - //Store the sample - sample_total_saved ++; - if(bits_per_sample == 8){ - dest[sample_total_saved-1] = sample; - data.numbits = sample_total_saved << 3;//Get the return value correct - if(sample_total_saved >= bufsize) break; - } - else{ - pushBit(&data, sample & 0x80); - if(bits_per_sample > 1) pushBit(&data, sample & 0x40); - if(bits_per_sample > 2) pushBit(&data, sample & 0x20); - if(bits_per_sample > 3) pushBit(&data, sample & 0x10); - if(bits_per_sample > 4) pushBit(&data, sample & 0x08); - if(bits_per_sample > 5) pushBit(&data, sample & 0x04); - if(bits_per_sample > 6) pushBit(&data, sample & 0x02); - //Not needed, 8bps is covered above - //if(bits_per_sample > 7) pushBit(&data, sample & 0x01); - if((data.numbits >> 3) +1 >= bufsize) break; - } - } - } - - if(!silent) - { - Dbprintf("Done, saved %d out of %d seen samples at %d bits/sample",sample_total_saved, sample_total_numbers,bits_per_sample); - Dbprintf("buffer samples: %02x %02x %02x %02x %02x %02x %02x %02x ...", - dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]); - } - return data.numbits; -} -/** -* Perform sample aquisition. -*/ -void DoAcquisition125k(int trigger_threshold) -{ - DoAcquisition125k_internal(trigger_threshold, false); -} - -/** -* Setup the FPGA to listen for samples. This method downloads the FPGA bitstream -* if not already loaded, sets divisor and starts up the antenna. -* @param divisor : 1, 88> 255 or negative ==> 134.8 KHz -* 0 or 95 ==> 125 KHz -* -**/ -void LFSetupFPGAForADC(int divisor, bool lf_field) -{ - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - if ( (divisor == 1) || (divisor < 0) || (divisor > 255) ) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz - else if (divisor == 0) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - else - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | (lf_field ? FPGA_LF_ADC_READER_FIELD : 0)); - - // Connect the A/D to the peak-detected low-frequency path. - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - // Give it a bit of time for the resonant antenna to settle. - SpinDelay(50); - // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); -} -/** -* Initializes the FPGA, and acquires the samples. -**/ -void AcquireRawAdcSamples125k(int divisor,int arg1, int arg2) -{ - if (arg1 != 0) - { - averaging = (arg1 & 0x80) != 0; - bits_per_sample = (arg1 & 0x0F); - } - if(arg2 != 0) - { - decimation = arg2; - } - - Dbprintf("Sampling config: "); - Dbprintf(" divisor: %d ", divisor); - Dbprintf(" bps: %d ", bits_per_sample); - Dbprintf(" decimation: %d ", decimation); - Dbprintf(" averaging: %d ", averaging); - - LFSetupFPGAForADC(divisor, true); - // Now call the acquisition routine - DoAcquisition125k_internal(-1,false); -} -/** -* Initializes the FPGA for snoop-mode, and acquires the samples. -**/ - -void SnoopLFRawAdcSamples(int divisor, int trigger_threshold) -{ - LFSetupFPGAForADC(divisor, false); - DoAcquisition125k(trigger_threshold); -} - void ModThenAcquireRawAdcSamples125k(int delay_off, int period_0, int period_1, uint8_t *command) { - /* Make sure the tag is reset */ - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(2500); - - int divisor_used = 95; // 125 KHz // see if 'h' was specified if (command[strlen((char *) command) - 1] == 'h') divisor_used = 88; // 134.8 KHz + sample_config sc = { 0,0,1, divisor_used, 0}; + setSamplingConfig(&sc); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor_used); - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - // Give it a bit of time for the resonant antenna to settle. - SpinDelay(50); + /* Make sure the tag is reset */ + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(2500); - // And a little more time for the tag to fully power up - SpinDelay(2000); + LFSetupFPGAForADC(sc.divisor, 1); - // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + // And a little more time for the tag to fully power up + SpinDelay(2000); // now modulate the reader field while(*command != '\0' && *command != ' ') { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelayUs(delay_off); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor_used); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc.divisor); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); LED_D_ON(); @@ -251,14 +64,16 @@ void ModThenAcquireRawAdcSamples125k(int delay_off, int period_0, int period_1, FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelayUs(delay_off); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor_used); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc.divisor); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); // now do the read - DoAcquisition125k(-1); + DoAcquisition_config(false); } + + /* blank r/w tag data stream ...0000000000000000 01111111 1010101010101010101010101010101010101010101010101010101010101010 @@ -745,8 +560,8 @@ void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol) WDT_HIT(); if (ledcontrol) LED_A_ON(); - DoAcquisition125k_internal(-1,true); - // FSK demodulator + DoAcquisition_default(-1,true); + // FSK demodulator size = HIDdemodFSK(dest, sizeof(BigBuf), &hi2, &hi, &lo); WDT_HIT(); @@ -833,8 +648,8 @@ void CmdEM410xdemod(int findone, int *high, int *low, int ledcontrol) WDT_HIT(); if (ledcontrol) LED_A_ON(); - DoAcquisition125k_internal(-1,true); - size = sizeof(BigBuf); + DoAcquisition_default(-1,true); + size = sizeof(BigBuf); //Dbprintf("DEBUG: Buffer got"); //askdemod and manchester decode errCnt = askmandemod(dest, &size, &clk, &invert); @@ -884,8 +699,8 @@ void CmdIOdemodFSK(int findone, int *high, int *low, int ledcontrol) while(!BUTTON_PRESS()) { WDT_HIT(); if (ledcontrol) LED_A_ON(); - DoAcquisition125k_internal(-1,true); - //fskdemod and get start index + DoAcquisition_default(-1,true); + //fskdemod and get start index WDT_HIT(); idx = IOdemodFSK(dest,sizeof(BigBuf)); if (idx>0){ @@ -1489,7 +1304,9 @@ int DemodPCF7931(uint8_t **outBlocks) { int lmin=128, lmax=128; uint8_t dir; - AcquireRawAdcSamples125k(0,0,0); + LFSetupFPGAForADC(95, true); + DoAcquisition_default(0, 0); + lmin = 64; lmax = 192; diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c new file mode 100644 index 00000000..6314ac74 --- /dev/null +++ b/armsrc/lfsampling.c @@ -0,0 +1,251 @@ +//----------------------------------------------------------------------------- +// 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 +// the license. +//----------------------------------------------------------------------------- +// Miscellaneous routines for low frequency sampling. +//----------------------------------------------------------------------------- + +#include "proxmark3.h" +#include "apps.h" +#include "util.h" +#include "string.h" + +#include "lfsampling.h" + +sample_config config = { 1, 8, 1, 88, 0 } ; + +void printConfig() +{ + Dbprintf("Sampling config: "); + Dbprintf(" [q] divisor: %d ", config.divisor); + Dbprintf(" [b] bps: %d ", config.bits_per_sample); + Dbprintf(" [d] decimation: %d ", config.decimation); + Dbprintf(" [a] averaging: %d ", config.averaging); + Dbprintf(" [t] trigger threshold: %d ", config.trigger_threshold); +} + + +/** + * Called from the USB-handler to set the sampling configuration + * The sampling config is used for std reading and snooping. + * + * Other functions may read samples and ignore the sampling config, + * such as functions to read the UID from a prox tag or similar. + * + * Values set to '0' implies no change (except for averaging) + * @brief setSamplingConfig + * @param sc + */ +void setSamplingConfig(sample_config *sc) +{ + if(sc->divisor != 0) config.divisor = sc->divisor; + if(sc->bits_per_sample!= 0) config.bits_per_sample= sc->bits_per_sample; + if(sc->decimation!= 0) config.decimation= sc->decimation; + if(sc->trigger_threshold != -1) config.trigger_threshold= sc->trigger_threshold; + + config.averaging= sc->averaging; + if(config.bits_per_sample > 8) config.bits_per_sample = 8; + if(config.decimation < 1) config.decimation = 1; + + printConfig(); +} + +sample_config* getSamplingConfig() +{ + return &config; +} + +typedef struct { + uint8_t * buffer; + uint32_t numbits; + uint32_t position; +} BitstreamOut; + + +/** + * @brief Pushes bit onto the stream + * @param stream + * @param bit + */ +void pushBit( BitstreamOut* stream, uint8_t bit) +{ + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = stream->position & 7; + *(stream->buffer+bytepos) |= (bit > 0) << (7 - bitpos); + stream->position++; + stream->numbits++; +} + +/** +* Setup the FPGA to listen for samples. This method downloads the FPGA bitstream +* if not already loaded, sets divisor and starts up the antenna. +* @param divisor : 1, 88> 255 or negative ==> 134.8 KHz +* 0 or 95 ==> 125 KHz +* +**/ +void LFSetupFPGAForADC(int divisor, bool lf_field) +{ + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + if ( (divisor == 1) || (divisor < 0) || (divisor > 255) ) + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz + else if (divisor == 0) + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + else + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | (lf_field ? FPGA_LF_ADC_READER_FIELD : 0)); + + // Connect the A/D to the peak-detected low-frequency path. + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + // Give it a bit of time for the resonant antenna to settle. + SpinDelay(50); + // Now set up the SSC to get the ADC samples that are now streaming at us. + FpgaSetupSsc(); +} + + +/** + * Does the sample acquisition. If threshold is specified, the actual sampling + * is not commenced until the threshold has been reached. + * This method implements decimation and quantization in order to + * be able to provide longer sample traces. + * Uses the following global settings: + * @param decimation - how much should the signal be decimated. A decimation of N means we keep 1 in N samples, etc. + * @param bits_per_sample - bits per sample. Max 8, min 1 bit per sample. + * @param averaging If set to true, decimation will use averaging, so that if e.g. decimation is 3, the sample + * value that will be used is the average value of the three samples. + * @param trigger_threshold - a threshold. The sampling won't commence until this threshold has been reached. Set + * to -1 to ignore threshold. + * @param silent - is true, now outputs are made. If false, dbprints the status + * @return the number of bits occupied by the samples. + */ + +uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averaging, int trigger_threshold,bool silent) +{ + //. + uint8_t *dest = (uint8_t *)BigBuf; + int bufsize = BIGBUF_SIZE; + memset(dest, 0, bufsize); + + if(bits_per_sample < 1) bits_per_sample = 1; + if(bits_per_sample > 8) bits_per_sample = 8; + + if(decimation < 1) decimation = 1; + + // Use a bit stream to handle the output + BitstreamOut data = { dest , 0, 0}; + int sample_counter = 0; + uint8_t sample = 0; + //If we want to do averaging + uint32_t sample_sum =0 ; + uint32_t sample_total_numbers =0 ; + uint32_t sample_total_saved =0 ; + + while(!BUTTON_PRESS()) { + WDT_HIT(); + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + AT91C_BASE_SSC->SSC_THR = 0x43; + LED_D_ON(); + } + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + LED_D_OFF(); + if (trigger_threshold > 0 && sample < trigger_threshold) + continue; + + trigger_threshold = 0; + sample_total_numbers++; + + if(averaging) + { + sample_sum += sample; + } + //Check decimation + if(decimation > 1) + { + sample_counter++; + if(sample_counter < decimation) continue; + sample_counter = 0; + } + //Averaging + if(averaging && decimation > 1) { + sample = sample_sum / decimation; + sample_sum =0; + } + //Store the sample + sample_total_saved ++; + if(bits_per_sample == 8){ + dest[sample_total_saved-1] = sample; + data.numbits = sample_total_saved << 3;//Get the return value correct + if(sample_total_saved >= bufsize) break; + } + else{ + pushBit(&data, sample & 0x80); + if(bits_per_sample > 1) pushBit(&data, sample & 0x40); + if(bits_per_sample > 2) pushBit(&data, sample & 0x20); + if(bits_per_sample > 3) pushBit(&data, sample & 0x10); + if(bits_per_sample > 4) pushBit(&data, sample & 0x08); + if(bits_per_sample > 5) pushBit(&data, sample & 0x04); + if(bits_per_sample > 6) pushBit(&data, sample & 0x02); + //Not needed, 8bps is covered above + //if(bits_per_sample > 7) pushBit(&data, sample & 0x01); + if((data.numbits >> 3) +1 >= bufsize) break; + } + } + } + + if(!silent) + { + Dbprintf("Done, saved %d out of %d seen samples at %d bits/sample",sample_total_saved, sample_total_numbers,bits_per_sample); + Dbprintf("buffer samples: %02x %02x %02x %02x %02x %02x %02x %02x ...", + dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]); + } + return data.numbits; +} +/** + * @brief Does sample acquisition, ignoring the config values set in the sample_config. + * This method is typically used by tag-specific readers who just wants to read the samples + * the normal way + * @param trigger_threshold + * @param silent + * @return number of bits sampled + */ +uint32_t DoAcquisition_default(int trigger_threshold, bool silent) +{ + return DoAcquisition(1,8,0,trigger_threshold,silent); +} +uint32_t DoAcquisition_config( bool silent) +{ + return DoAcquisition(config.decimation + ,config.bits_per_sample + ,config.averaging + ,config.trigger_threshold + ,silent); +} + +uint32_t ReadLF(bool activeField) +{ + printConfig(); + LFSetupFPGAForADC(config.divisor, activeField); + // Now call the acquisition routine + return DoAcquisition_config(false); +} + +/** +* Initializes the FPGA for reader-mode (field on), and acquires the samples. +* @return number of bits sampled +**/ +uint32_t SampleLF() +{ + return ReadLF(true); +} +/** +* Initializes the FPGA for snoop-mode (field off), and acquires the samples. +* @return number of bits sampled +**/ + +uint32_t SnoopLF() +{ + return ReadLF(false); +} diff --git a/armsrc/lfsampling.h b/armsrc/lfsampling.h new file mode 100644 index 00000000..9ab458f8 --- /dev/null +++ b/armsrc/lfsampling.h @@ -0,0 +1,59 @@ +#ifndef LFSAMPLING_H +#define LFSAMPLING_H + +/** +* Initializes the FPGA for reader-mode (field on), and acquires the samples. +* @return number of bits sampled +**/ +uint32_t SampleLF(); + +/** +* Initializes the FPGA for snoop-mode (field off), and acquires the samples. +* @return number of bits sampled +**/ + +uint32_t SnoopLF(); + +/** + * @brief Does sample acquisition, ignoring the config values set in the sample_config. + * This method is typically used by tag-specific readers who just wants to read the samples + * the normal way + * @param trigger_threshold + * @param silent + * @return number of bits sampled + */ +uint32_t DoAcquisition_default(int trigger_threshold, bool silent); +/** + * @brief Does sample acquisition, using the config values set in the sample_config. + * @param trigger_threshold + * @param silent + * @return number of bits sampled + */ + +uint32_t DoAcquisition_config( bool silent); + +/** +* Setup the FPGA to listen for samples. This method downloads the FPGA bitstream +* if not already loaded, sets divisor and starts up the antenna. +* @param divisor : 1, 88> 255 or negative ==> 134.8 KHz +* 0 or 95 ==> 125 KHz +* +**/ +void LFSetupFPGAForADC(int divisor, bool lf_field); + + +/** + * Called from the USB-handler to set the sampling configuration + * The sampling config is used for std reading and snooping. + * + * Other functions may read samples and ignore the sampling config, + * such as functions to read the UID from a prox tag or similar. + * + * Values set to '0' implies no change (except for averaging) + * @brief setSamplingConfig + * @param sc + */ +void setSamplingConfig(sample_config *sc); + +sample_config * getSamplingConfig(); +#endif // LFSAMPLING_H diff --git a/client/cmddata.c b/client/cmddata.c index 79384580..fa056b67 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -21,6 +21,8 @@ #include "cmdmain.h" #include "cmddata.h" #include "lfdemod.h" +#include "usb_cmd.h" + uint8_t DemodBuffer[MAX_DEMOD_BUF_LEN]; int DemodBufferLen; static int CmdHelp(const char *Cmd); @@ -1077,11 +1079,15 @@ uint8_t getByte(uint8_t bits_per_sample, BitstreamOut* b) int CmdSamples(const char *Cmd) { - uint8_t got[40000]; + //If we get all but the last byte in bigbuf, + // we don't have to worry about remaining trash + // in the last byte in case the bits-per-sample + // does not line up on byte boundaries + uint8_t got[40000-1]; int n = strtol(Cmd, NULL, 0); if (n == 0) - n = 20000; + n = sizeof(got); if (n > sizeof(got)) n = sizeof(got); @@ -1091,14 +1097,22 @@ int CmdSamples(const char *Cmd) PrintAndLog("Data fetched"); UsbCommand response; WaitForResponse(CMD_ACK, &response); - uint8_t bits_per_sample = response.arg[0]; - PrintAndLog("Samples packed at %d bits per sample", bits_per_sample); + uint8_t bits_per_sample = 8; + + //Old devices without this feature would send 0 at arg[0] + if(response.arg[0] > 0) + { + sample_config *sc = (sample_config *) response.d.asBytes; + PrintAndLog("Samples @ %d bits/smpl, decimation 1:%d ", sc->bits_per_sample + , sc->decimation); + bits_per_sample = sc->bits_per_sample; + } if(bits_per_sample < 8) { PrintAndLog("Unpacking..."); BitstreamOut bout = { got, bits_per_sample * n, 0}; int j =0; - for (j = 0; j * bits_per_sample < n * 8 && j < GraphTraceLen; j++) { + for (j = 0; j * bits_per_sample < n * 8 && j < sizeof(GraphBuffer); j++) { uint8_t sample = getByte(bits_per_sample, &bout); GraphBuffer[j] = ((int) sample )- 128; } @@ -1110,7 +1124,6 @@ int CmdSamples(const char *Cmd) GraphBuffer[j] = ((int)got[j]) - 128; } GraphTraceLen = n; - } RepaintGraphWindow(); diff --git a/client/cmdlf.c b/client/cmdlf.c index 93a9f586..09d93002 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -356,88 +356,152 @@ int CmdIndalaClone(const char *Cmd) return 0; } -int CmdLFReadUsage() +int usage_lf_read() { - PrintAndLog("Usage: lf read [H|] [b ] [d ] [a 0|1]"); + PrintAndLog("Usage: lf read"); PrintAndLog("Options: "); PrintAndLog(" h This help"); - PrintAndLog(" H High frequency (134 KHz). Defaults to 125 KHz"); - PrintAndLog(" Manually set divisor. 88-> 134KHz, 95-> 125 Hz"); - PrintAndLog(" b Sets resolution of bits per sample. Default (max): 8"); - PrintAndLog(" d Sets decimation. A value of N saves only 1 in N samples. Default: 1"); - PrintAndLog(" a [0|1] Averaging - if set, will average the stored sample value when decimating. Default: 1"); + PrintAndLog("This function takes no arguments. "); + PrintAndLog("Use 'lf config' to set parameters."); + return 0; +} +int usage_lf_snoop() +{ + PrintAndLog("Usage: lf snoop"); + PrintAndLog("Options: "); + PrintAndLog(" h This help"); + PrintAndLog("This function takes no arguments. "); + PrintAndLog("Use 'lf config' to set parameters."); + return 0; +} + +int usage_lf_config() +{ + PrintAndLog("Usage: lf config [H|] [b ] [d ] [a 0|1]"); + PrintAndLog("Options: "); + PrintAndLog(" h This help"); + PrintAndLog(" L Low frequency (125 KHz)"); + PrintAndLog(" H High frequency (134 KHz)"); + PrintAndLog(" q Manually set divisor. 88-> 134KHz, 95-> 125 Hz"); + PrintAndLog(" b Sets resolution of bits per sample. Default (max): 8"); + PrintAndLog(" d Sets decimation. A value of N saves only 1 in N samples. Default: 1"); + PrintAndLog(" a [0|1] Averaging - if set, will average the stored sample value when decimating. Default: 1"); + PrintAndLog(" t Sets trigger threshold. 0 means no threshold"); PrintAndLog("Examples:"); - PrintAndLog(" lf read"); + PrintAndLog(" lf config b 8 L"); PrintAndLog(" Samples at 125KHz, 8bps."); - PrintAndLog(" lf read h b 4 d 3"); + PrintAndLog(" lf config H b 4 d 3"); PrintAndLog(" Samples at 134KHz, averages three samples into one, stored with "); PrintAndLog(" a resolution of 4 bits per sample."); + PrintAndLog(" lf read"); + PrintAndLog(" Performs a read (active field)"); + PrintAndLog(" lf snoop"); + PrintAndLog(" Performs a snoop (no active field)"); return 0; } -int CmdLFRead(const char *Cmd) + +int CmdLFSetConfig(const char *Cmd) { - uint8_t divisor = 95;//Frequency divisor - uint8_t bps = 8; // Bits per sample - uint8_t decimation = 1; //How many to keep - bool averaging = 1; // Should we use averaging when discarding samples? + + uint8_t divisor = 0;//Frequency divisor + uint8_t bps = 0; // Bits per sample + uint8_t decimation = 0; //How many to keep + bool averaging = 1; // Defaults to true bool errors = FALSE; + int trigger_threshold =-1;//Means no change + uint8_t unsigned_trigg = 0; uint8_t cmdp =0; - if(param_getchar(Cmd, cmdp) == 'h') - { - return CmdLFReadUsage(); - } - - // Divisor - if(param_getchar(Cmd, cmdp) == 'H') { - divisor = 88; - cmdp++; - }else if(param_isdec(Cmd,cmdp) ) - { - errors |= param_getdec(Cmd,cmdp, &divisor); - } - //BPS - if(param_getchar(Cmd, cmdp) == 'b') { - errors |= param_getdec(Cmd,cmdp+1,&bps); - cmdp+=2; - } - //Decimation - if(param_getchar(Cmd, cmdp) == 'd') + while(param_getchar(Cmd, cmdp) != 0x00) { - errors |= param_getdec(Cmd,cmdp+1,&decimation); - cmdp+=2; + PrintAndLog("working %c", param_getchar(Cmd, cmdp)); + switch(param_getchar(Cmd, cmdp)) + { + case 'h': + return usage_lf_config(); + case 'H': + divisor = 88; + cmdp++; + break; + case 'L': + divisor = 95; + cmdp++; + break; + case 'q': + errors |= param_getdec(Cmd,cmdp+1,&divisor); + cmdp+=2; + break; + case 't': + errors |= param_getdec(Cmd,cmdp+1,&unsigned_trigg); + cmdp+=2; + if(!errors) trigger_threshold = unsigned_trigg; + break; + case 'b': + errors |= param_getdec(Cmd,cmdp+1,&bps); + cmdp+=2; + break; + case 'd': + errors |= param_getdec(Cmd,cmdp+1,&decimation); + cmdp+=2; + break; + case 'a': + averaging = param_getchar(Cmd,cmdp+1) == '1'; + cmdp+=2; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = 1; + break; + } + if(errors) break; } - //Averaging - if(param_getchar(Cmd, cmdp) == 'a') + if(cmdp == 0) { - averaging = param_getchar(Cmd,cmdp+1) == '1'; - cmdp+=2; + errors = 1;// No args } + //Validations if(errors) { - return CmdLFReadUsage(); + return usage_lf_config(); } - //Bps is limited to 8, so fits in lower half of arg1 - if(bps > 8) bps = 8; + if(bps >> 8) bps = 8; - //Feedback - PrintAndLog("Sampling config: "); - PrintAndLog(" divisor: %d ", divisor); - PrintAndLog(" bps: %d ", bps); - PrintAndLog(" decimation: %d ", decimation); - PrintAndLog(" averaging: %d ", averaging); - PrintAndLog("OBS, this is sticky on the device and affects all LF listening operations"); - PrintAndLog("To reset, issue 'lf read'"); + sample_config config = { + decimation,bps,averaging,divisor,trigger_threshold + }; + //Averaging is a flag on high-bit of arg[1] + UsbCommand c = {CMD_SET_LF_SAMPLING_CONFIG}; + memcpy(c.d.asBytes,&config,sizeof(sample_config)); + SendCommand(&c); + return 0; +} +int CmdLFRead(const char *Cmd) +{ + + uint8_t cmdp =0; + if(param_getchar(Cmd, cmdp) == 'h') + { + return usage_lf_read(); + } //And ship it to device - //Averaging is a flag on high-bit of arg[1] UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_125K}; - c.arg[0] = divisor; - c.arg[1] = bps | (averaging << 7) ; - c.arg[2] = decimation; + SendCommand(&c); + WaitForResponse(CMD_ACK,NULL); + return 0; +} +int CmdLFSnoop(const char *Cmd) +{ + uint8_t cmdp =0; + if(param_getchar(Cmd, cmdp) == 'h') + { + return usage_lf_snoop(); + } + + UsbCommand c = {CMD_LF_SNOOP_RAW_ADC_SAMPLES}; SendCommand(&c); WaitForResponse(CMD_ACK,NULL); return 0; @@ -517,30 +581,6 @@ int CmdLFSimManchester(const char *Cmd) return 0; } -int CmdLFSnoop(const char *Cmd) -{ - UsbCommand c = {CMD_LF_SNOOP_RAW_ADC_SAMPLES}; - - // 'h' means higher-low-frequency, 134 kHz - c.arg[0] = 0; - c.arg[1] = -1; - - if (*Cmd == 'l') { - sscanf(Cmd, "l %"lli, &c.arg[1]); - } else if(*Cmd == 'h') { - c.arg[0] = 1; - sscanf(Cmd, "h %"lli, &c.arg[1]); - } else if (sscanf(Cmd, "%"lli" %"lli, &c.arg[0], &c.arg[1]) < 1) { - PrintAndLog("usage 1: snoop"); - PrintAndLog(" 2: snoop {l,h} [trigger threshold]"); - PrintAndLog(" 3: snoop [trigger threshold]"); - return 0; - } - - SendCommand(&c); - WaitForResponse(CMD_ACK,NULL); - return 0; -} int CmdVchDemod(const char *Cmd) { @@ -673,12 +713,13 @@ static command_t CommandTable[] = {"help", CmdHelp, 1, "This help"}, {"cmdread", CmdLFCommandRead, 0, " <'0' period> <'1' period> ['h'] -- Modulate LF reader field to send command before read (all periods in microseconds) (option 'h' for 134)"}, {"em4x", CmdLFEM4X, 1, "{ EM4X RFIDs... }"}, + {"config", CmdLFSetConfig, 0, "Set config for LF sampling, bit/sample, decimation, frequency"}, {"flexdemod", CmdFlexdemod, 1, "Demodulate samples for FlexPass"}, {"hid", CmdLFHID, 1, "{ HID RFIDs... }"}, {"io", CmdLFIO, 1, "{ ioProx tags... }"}, {"indalademod", CmdIndalaDemod, 1, "['224'] -- Demodulate samples for Indala 64 bit UID (option '224' for 224 bit)"}, {"indalaclone", CmdIndalaClone, 0, " ['l']-- Clone Indala to T55x7 (tag must be in antenna)(UID in HEX)(option 'l' for 224 UID"}, - {"read", CmdLFRead, 0, "['h' or ] -- Read 125/134 kHz LF ID-only tag (option 'h' for 134, alternatively: f=12MHz/(divisor+1))"}, + {"read", CmdLFRead, 0, "Read 125/134 kHz LF ID-only tag. Do 'lf read h' for help"}, {"search", CmdLFfind, 1, "Read and Search for valid known tag (in offline mode it you can load first then search)"}, {"sim", CmdLFSim, 0, "[GAP] -- Simulate LF tag from buffer with optional GAP (in microseconds)"}, {"simbidir", CmdLFSimBidir, 0, "Simulate LF tag (with bidirectional data transmission between reader and tag)"}, diff --git a/client/util.c b/client/util.c index bce1c122..6b47eab9 100644 --- a/client/util.c +++ b/client/util.c @@ -213,6 +213,7 @@ int param_getptr(const char *line, int *bg, int *en, int paramnum) return 0; } + char param_getchar(const char *line, int paramnum) { int bg, en; @@ -228,14 +229,16 @@ uint8_t param_get8(const char *line, int paramnum) } /** - * @brief Reads a decimal integer + * @brief Reads a decimal integer (actually, 0-254, not 255) * @param line * @param paramnum - * @return + * @return -1 if error */ uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination) { - uint8_t val = param_get8ex(line, paramnum, 10, 10); + uint8_t val = param_get8ex(line, paramnum, 255, 10); + printf("read %i", (int8_t ) val); + if( (int8_t) val == -1) return 1; (*destination) = val; return 0; } diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 69c3c1b6..6ee6509e 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -33,6 +33,14 @@ typedef struct { uint32_t asDwords[USB_CMD_DATA_SIZE/4]; } d; } PACKED UsbCommand; +// A struct used to send sample-configs over USB +typedef struct{ + uint8_t decimation; + uint8_t bits_per_sample; + bool averaging; + int divisor; + int trigger_threshold; +} sample_config; // For the bootloader #define CMD_DEVICE_INFO 0x0000 @@ -82,6 +90,8 @@ typedef struct { #define CMD_IO_DEMOD_FSK 0x021A #define CMD_IO_CLONE_TAG 0x021B #define CMD_EM410X_DEMOD 0x021c +// Sampling configuration for LF reader/snooper +#define CMD_SET_LF_SAMPLING_CONFIG 0x021d /* CMD_SET_ADC_MUX: ext1 is 0 for lopkd, 1 for loraw, 2 for hipkd, 3 for hiraw */ @@ -187,6 +197,7 @@ typedef struct { #define FLAG_ICLASS_READER_ONLY_ONCE 0x01 #define FLAG_ICLASS_READER_GET_CC 0x02 + // CMD_DEVICE_INFO response packet has flags in arg[0], flag definitions: /* Whether a bootloader that understands the common_area is present */ #define DEVICE_INFO_FLAG_BOOTROM_PRESENT (1<<0) -- 2.39.2