| 1 | //----------------------------------------------------------------------------- |
| 2 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, |
| 3 | // at your option, any later version. See the LICENSE.txt file for the text of |
| 4 | // the license. |
| 5 | //----------------------------------------------------------------------------- |
| 6 | // Miscellaneous routines for low frequency sampling. |
| 7 | //----------------------------------------------------------------------------- |
| 8 | |
| 9 | #include "proxmark3.h" |
| 10 | #include "apps.h" |
| 11 | #include "util.h" |
| 12 | #include "string.h" |
| 13 | #include "usb_cdc.h" // for usb_poll_validate_length |
| 14 | #include "lfsampling.h" |
| 15 | |
| 16 | sample_config config = { 1, 8, 1, 95, 0 } ; |
| 17 | |
| 18 | void printConfig() |
| 19 | { |
| 20 | Dbprintf("LF Sampling config: "); |
| 21 | Dbprintf(" [q] divisor: %d ", config.divisor); |
| 22 | Dbprintf(" [b] bps: %d ", config.bits_per_sample); |
| 23 | Dbprintf(" [d] decimation: %d ", config.decimation); |
| 24 | Dbprintf(" [a] averaging: %d ", config.averaging); |
| 25 | Dbprintf(" [t] trigger threshold: %d ", config.trigger_threshold); |
| 26 | } |
| 27 | |
| 28 | |
| 29 | /** |
| 30 | * Called from the USB-handler to set the sampling configuration |
| 31 | * The sampling config is used for std reading and snooping. |
| 32 | * |
| 33 | * Other functions may read samples and ignore the sampling config, |
| 34 | * such as functions to read the UID from a prox tag or similar. |
| 35 | * |
| 36 | * Values set to '0' implies no change (except for averaging) |
| 37 | * @brief setSamplingConfig |
| 38 | * @param sc |
| 39 | */ |
| 40 | void setSamplingConfig(sample_config *sc) |
| 41 | { |
| 42 | if(sc->divisor != 0) config.divisor = sc->divisor; |
| 43 | if(sc->bits_per_sample != 0) config.bits_per_sample = sc->bits_per_sample; |
| 44 | if(sc->decimation != 0) config.decimation = sc->decimation; |
| 45 | if(sc->trigger_threshold != -1) config.trigger_threshold = sc->trigger_threshold; |
| 46 | |
| 47 | config.averaging = sc->averaging; |
| 48 | if(config.bits_per_sample > 8) config.bits_per_sample = 8; |
| 49 | if(config.decimation < 1) config.decimation = 1; |
| 50 | |
| 51 | printConfig(); |
| 52 | } |
| 53 | |
| 54 | sample_config* getSamplingConfig() |
| 55 | { |
| 56 | return &config; |
| 57 | } |
| 58 | |
| 59 | typedef struct { |
| 60 | uint8_t * buffer; |
| 61 | uint32_t numbits; |
| 62 | uint32_t position; |
| 63 | } BitstreamOut; |
| 64 | |
| 65 | /** |
| 66 | * @brief Pushes bit onto the stream |
| 67 | * @param stream |
| 68 | * @param bit |
| 69 | */ |
| 70 | void pushBit( BitstreamOut* stream, uint8_t bit) |
| 71 | { |
| 72 | int bytepos = stream->position >> 3; // divide by 8 |
| 73 | int bitpos = stream->position & 7; |
| 74 | *(stream->buffer+bytepos) |= (bit > 0) << (7 - bitpos); |
| 75 | stream->position++; |
| 76 | stream->numbits++; |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Setup the FPGA to listen for samples. This method downloads the FPGA bitstream |
| 81 | * if not already loaded, sets divisor and starts up the antenna. |
| 82 | * @param divisor : 1, 88> 255 or negative ==> 134.8 KHz |
| 83 | * 0 or 95 ==> 125 KHz |
| 84 | * |
| 85 | **/ |
| 86 | void LFSetupFPGAForADC(int divisor, bool lf_field) |
| 87 | { |
| 88 | FpgaDownloadAndGo(FPGA_BITSTREAM_LF); |
| 89 | if ( (divisor == 1) || (divisor < 0) || (divisor > 255) ) |
| 90 | FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz |
| 91 | else if (divisor == 0) |
| 92 | FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz |
| 93 | else |
| 94 | FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor); |
| 95 | |
| 96 | FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | (lf_field ? FPGA_LF_ADC_READER_FIELD : 0)); |
| 97 | |
| 98 | // Connect the A/D to the peak-detected low-frequency path. |
| 99 | SetAdcMuxFor(GPIO_MUXSEL_LOPKD); |
| 100 | // Give it a bit of time for the resonant antenna to settle. |
| 101 | SpinDelay(50); |
| 102 | // Now set up the SSC to get the ADC samples that are now streaming at us. |
| 103 | FpgaSetupSsc(); |
| 104 | } |
| 105 | |
| 106 | /** |
| 107 | * Does the sample acquisition. If threshold is specified, the actual sampling |
| 108 | * is not commenced until the threshold has been reached. |
| 109 | * This method implements decimation and quantization in order to |
| 110 | * be able to provide longer sample traces. |
| 111 | * Uses the following global settings: |
| 112 | * @param decimation - how much should the signal be decimated. A decimation of N means we keep 1 in N samples, etc. |
| 113 | * @param bits_per_sample - bits per sample. Max 8, min 1 bit per sample. |
| 114 | * @param averaging If set to true, decimation will use averaging, so that if e.g. decimation is 3, the sample |
| 115 | * value that will be used is the average value of the three samples. |
| 116 | * @param trigger_threshold - a threshold. The sampling won't commence until this threshold has been reached. Set |
| 117 | * to -1 to ignore threshold. |
| 118 | * @param silent - is true, now outputs are made. If false, dbprints the status |
| 119 | * @return the number of bits occupied by the samples. |
| 120 | */ |
| 121 | uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averaging, int trigger_threshold,bool silent) |
| 122 | { |
| 123 | //bigbuf, to hold the aquired raw data signal |
| 124 | uint8_t *dest = BigBuf_get_addr(); |
| 125 | uint16_t bufsize = BigBuf_max_traceLen(); |
| 126 | |
| 127 | //BigBuf_Clear_ext(false); //creates issues with cmdread (marshmellow) |
| 128 | |
| 129 | if(bits_per_sample < 1) bits_per_sample = 1; |
| 130 | if(bits_per_sample > 8) bits_per_sample = 8; |
| 131 | |
| 132 | if(decimation < 1) decimation = 1; |
| 133 | |
| 134 | // Use a bit stream to handle the output |
| 135 | BitstreamOut data = { dest , 0, 0}; |
| 136 | int sample_counter = 0; |
| 137 | uint8_t sample = 0; |
| 138 | //If we want to do averaging |
| 139 | uint32_t sample_sum =0 ; |
| 140 | uint32_t sample_total_numbers =0 ; |
| 141 | uint32_t sample_total_saved =0 ; |
| 142 | |
| 143 | while(!BUTTON_PRESS() && !usb_poll_validate_length() ) { |
| 144 | WDT_HIT(); |
| 145 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { |
| 146 | AT91C_BASE_SSC->SSC_THR = 0x43; |
| 147 | LED_D_ON(); |
| 148 | } |
| 149 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { |
| 150 | sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; |
| 151 | LED_D_OFF(); |
| 152 | // threshold either high or low values 128 = center 0. if trigger = 178 |
| 153 | if ((trigger_threshold > 0) && (sample < (trigger_threshold+128)) && (sample > (128-trigger_threshold))) // |
| 154 | continue; |
| 155 | |
| 156 | trigger_threshold = 0; |
| 157 | sample_total_numbers++; |
| 158 | |
| 159 | if(averaging) |
| 160 | { |
| 161 | sample_sum += sample; |
| 162 | } |
| 163 | //Check decimation |
| 164 | if(decimation > 1) |
| 165 | { |
| 166 | sample_counter++; |
| 167 | if(sample_counter < decimation) continue; |
| 168 | sample_counter = 0; |
| 169 | } |
| 170 | //Averaging |
| 171 | if(averaging && decimation > 1) { |
| 172 | sample = sample_sum / decimation; |
| 173 | sample_sum =0; |
| 174 | } |
| 175 | //Store the sample |
| 176 | sample_total_saved ++; |
| 177 | if(bits_per_sample == 8){ |
| 178 | dest[sample_total_saved-1] = sample; |
| 179 | data.numbits = sample_total_saved << 3;//Get the return value correct |
| 180 | if(sample_total_saved >= bufsize) break; |
| 181 | } |
| 182 | else{ |
| 183 | pushBit(&data, sample & 0x80); |
| 184 | if(bits_per_sample > 1) pushBit(&data, sample & 0x40); |
| 185 | if(bits_per_sample > 2) pushBit(&data, sample & 0x20); |
| 186 | if(bits_per_sample > 3) pushBit(&data, sample & 0x10); |
| 187 | if(bits_per_sample > 4) pushBit(&data, sample & 0x08); |
| 188 | if(bits_per_sample > 5) pushBit(&data, sample & 0x04); |
| 189 | if(bits_per_sample > 6) pushBit(&data, sample & 0x02); |
| 190 | //Not needed, 8bps is covered above |
| 191 | //if(bits_per_sample > 7) pushBit(&data, sample & 0x01); |
| 192 | if((data.numbits >> 3) +1 >= bufsize) break; |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | if(!silent) |
| 198 | { |
| 199 | Dbprintf("Done, saved %d out of %d seen samples at %d bits/sample",sample_total_saved, sample_total_numbers,bits_per_sample); |
| 200 | Dbprintf("buffer samples: %02x %02x %02x %02x %02x %02x %02x %02x ...", |
| 201 | dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]); |
| 202 | } |
| 203 | return data.numbits; |
| 204 | } |
| 205 | /** |
| 206 | * @brief Does sample acquisition, ignoring the config values set in the sample_config. |
| 207 | * This method is typically used by tag-specific readers who just wants to read the samples |
| 208 | * the normal way |
| 209 | * @param trigger_threshold |
| 210 | * @param silent |
| 211 | * @return number of bits sampled |
| 212 | */ |
| 213 | uint32_t DoAcquisition_default(int trigger_threshold, bool silent) |
| 214 | { |
| 215 | return DoAcquisition(1,8,0,trigger_threshold,silent); |
| 216 | } |
| 217 | uint32_t DoAcquisition_config( bool silent) |
| 218 | { |
| 219 | return DoAcquisition(config.decimation |
| 220 | ,config.bits_per_sample |
| 221 | ,config.averaging |
| 222 | ,config.trigger_threshold |
| 223 | ,silent); |
| 224 | } |
| 225 | |
| 226 | uint32_t ReadLF(bool activeField, bool silent) |
| 227 | { |
| 228 | if (!silent) printConfig(); |
| 229 | LFSetupFPGAForADC(config.divisor, activeField); |
| 230 | // Now call the acquisition routine |
| 231 | return DoAcquisition_config(silent); |
| 232 | } |
| 233 | |
| 234 | /** |
| 235 | * Initializes the FPGA for reader-mode (field on), and acquires the samples. |
| 236 | * @return number of bits sampled |
| 237 | **/ |
| 238 | uint32_t SampleLF(bool printCfg) |
| 239 | { |
| 240 | return ReadLF(true, printCfg); |
| 241 | } |
| 242 | /** |
| 243 | * Initializes the FPGA for snoop-mode (field off), and acquires the samples. |
| 244 | * @return number of bits sampled |
| 245 | **/ |
| 246 | uint32_t SnoopLF() { |
| 247 | return ReadLF(false, true); |
| 248 | } |
| 249 | |
| 250 | /** |
| 251 | * acquisition of T55x7 LF signal. Similart to other LF, but adjusted with @marshmellows thresholds |
| 252 | * the data is collected in BigBuf. |
| 253 | **/ |
| 254 | void doT55x7Acquisition(size_t sample_size) { |
| 255 | |
| 256 | #define T55xx_READ_UPPER_THRESHOLD 128+60 // 60 grph |
| 257 | #define T55xx_READ_LOWER_THRESHOLD 128-60 // -60 grph |
| 258 | #define T55xx_READ_TOL 5 |
| 259 | |
| 260 | uint8_t *dest = BigBuf_get_addr(); |
| 261 | uint16_t bufsize = BigBuf_max_traceLen(); |
| 262 | |
| 263 | if ( bufsize > sample_size ) |
| 264 | bufsize = sample_size; |
| 265 | |
| 266 | uint16_t i = 0; |
| 267 | bool startFound = false; |
| 268 | bool highFound = false; |
| 269 | bool lowFound = false; |
| 270 | uint8_t curSample = 0; |
| 271 | uint8_t lastSample = 0; |
| 272 | uint16_t skipCnt = 0; |
| 273 | while(!BUTTON_PRESS() && !usb_poll_validate_length() && skipCnt < 1000 && (i < bufsize) ) { |
| 274 | WDT_HIT(); |
| 275 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { |
| 276 | AT91C_BASE_SSC->SSC_THR = 0x43; |
| 277 | LED_D_ON(); |
| 278 | } |
| 279 | if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { |
| 280 | curSample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; |
| 281 | LED_D_OFF(); |
| 282 | |
| 283 | // skip until the first high sample above threshold |
| 284 | if (!startFound && curSample > T55xx_READ_UPPER_THRESHOLD) { |
| 285 | //if (curSample > lastSample) |
| 286 | // lastSample = curSample; |
| 287 | highFound = true; |
| 288 | } else if (!highFound) { |
| 289 | skipCnt++; |
| 290 | continue; |
| 291 | } |
| 292 | // skip until the first Low sample below threshold |
| 293 | if (!startFound && curSample < T55xx_READ_LOWER_THRESHOLD) { |
| 294 | //if (curSample > lastSample) |
| 295 | lastSample = curSample; |
| 296 | lowFound = true; |
| 297 | } else if (!lowFound) { |
| 298 | skipCnt++; |
| 299 | continue; |
| 300 | } |
| 301 | |
| 302 | |
| 303 | // skip until first high samples begin to change |
| 304 | if (startFound || curSample > T55xx_READ_LOWER_THRESHOLD+T55xx_READ_TOL){ |
| 305 | // if just found start - recover last sample |
| 306 | if (!startFound) { |
| 307 | dest[i++] = lastSample; |
| 308 | startFound = true; |
| 309 | } |
| 310 | // collect samples |
| 311 | dest[i++] = curSample; |
| 312 | } |
| 313 | } |
| 314 | } |
| 315 | } |
| 316 | |