| /* |
| * Copyright (c) 2002-2013, 2017 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| /*=========================================================================== |
| |
| dfs_fcc_bin5.c |
| |
| OVERVIEW: |
| |
| Source code borrowed from QCA_MAIN DFS module |
| |
| DEPENDENCIES: |
| |
| Are listed for each API below. |
| |
| ===========================================================================*/ |
| |
| /*=========================================================================== |
| |
| EDIT HISTORY FOR FILE |
| |
| |
| This section contains comments describing changes made to the module. |
| Notice that changes are listed in reverse chronological order. |
| |
| |
| |
| when who what, where, why |
| ---------- --- -------------------------------------------------------- |
| |
| ===========================================================================*/ |
| |
| #include "dfs.h" |
| /* TO DO DFS |
| #include <ieee80211_var.h> |
| */ |
| #ifdef ATH_SUPPORT_DFS |
| |
| /* |
| * Reject the pulse if: |
| * + It's outside the RSSI threshold; |
| * + It's outside the pulse duration; |
| * + It's been verified by HW/SW chirp checking |
| * and neither of those found a chirp. |
| */ |
| int |
| dfs_bin5_check_pulse(struct ath_dfs *dfs, struct dfs_event *re, |
| struct dfs_bin5radars *br) |
| { |
| int b5_rssithresh = br->br_pulse.b5_rssithresh; |
| |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_PULSE, |
| "%s: re_dur=%d, rssi=%d, check_chirp=%d, " |
| "hw_chirp=%d, sw_chirp=%d\n", |
| __func__, |
| (int) re->re_dur, |
| (int) re->re_rssi, |
| !! (re->re_flags & DFS_EVENT_CHECKCHIRP), |
| !! (re->re_flags & DFS_EVENT_HW_CHIRP), |
| !! (re->re_flags & DFS_EVENT_SW_CHIRP)); |
| |
| /* |
| * If the sw/hw chirp detection says to fail the pulse, |
| * do so. |
| */ |
| if (DFS_EVENT_NOTCHIRP(re)) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5, |
| "%s: rejecting chirp: ts=%llu, dur=%d, rssi=%d " |
| "checkchirp=%d, hwchirp=%d, swchirp=%d\n", |
| __func__, |
| (unsigned long long) re->re_full_ts, |
| (int) re->re_dur, |
| (int) re->re_rssi, |
| !! (re->re_flags & DFS_EVENT_CHECKCHIRP), |
| !! (re->re_flags & DFS_EVENT_HW_CHIRP), |
| !! (re->re_flags & DFS_EVENT_SW_CHIRP)); |
| return (0); |
| } |
| |
| /* Adjust the filter threshold for rssi in non TURBO mode */ |
| adf_os_spin_lock_bh(&dfs->ic->chan_lock); |
| if( ! (dfs->ic->ic_curchan->ic_flags & CHANNEL_TURBO)) |
| b5_rssithresh += br->br_pulse.b5_rssimargin; |
| |
| adf_os_spin_unlock_bh(&dfs->ic->chan_lock); |
| /* |
| * Check if the pulse is within duration and rssi |
| * thresholds. |
| */ |
| if ((re->re_dur >= br->br_pulse.b5_mindur) && |
| (re->re_dur <= br->br_pulse.b5_maxdur) && |
| (re->re_rssi >= b5_rssithresh)) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5, |
| "%s: dur=%d, rssi=%d - adding!\n", |
| __func__, (int) re->re_dur, (int) re->re_rssi); |
| return (1); |
| } |
| |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5, |
| "%s: too low to be Bin5 pulse tsf=%llu, dur=%d, rssi=%d\n", |
| __func__, |
| (unsigned long long) re->re_full_ts, |
| (int) re->re_dur, |
| (int) re->re_rssi); |
| |
| return (0); |
| } |
| |
| |
| int dfs_bin5_addpulse(struct ath_dfs *dfs, struct dfs_bin5radars *br, |
| struct dfs_event *re, u_int64_t thists) |
| { |
| u_int32_t index,stop; |
| u_int64_t tsDelta; |
| |
| /* Check if this pulse is a valid pulse in terms of repetition, |
| * if not, return without adding it to the queue. |
| * PRI : Pulse Repitetion Interval |
| * BRI : Burst Repitetion Interval */ |
| if( br->br_numelems != 0){ |
| index = br->br_lastelem; |
| tsDelta = thists - br->br_elems[index].be_ts; |
| if( (tsDelta < DFS_BIN5_PRI_LOWER_LIMIT) || |
| ( (tsDelta > DFS_BIN5_PRI_HIGHER_LIMIT) && |
| (tsDelta < DFS_BIN5_BRI_LOWER_LIMIT))) { |
| return 0; |
| } |
| } |
| /* Circular buffer of size 2^n */ |
| index = (br->br_lastelem +1) & DFS_MAX_B5_MASK; |
| br->br_lastelem = index; |
| if (br->br_numelems == DFS_MAX_B5_SIZE) |
| br->br_firstelem = (br->br_firstelem+1)&DFS_MAX_B5_MASK; |
| else |
| br->br_numelems++; |
| br->br_elems[index].be_ts = thists; |
| br->br_elems[index].be_rssi = re->re_rssi; |
| br->br_elems[index].be_dur = re->re_dur; /* please note that this is in u-sec */ |
| stop = 0; |
| index = br->br_firstelem; |
| while ((!stop) && (br->br_numelems-1) > 0) { |
| if ((thists - br->br_elems[index].be_ts) > |
| ((u_int64_t) br->br_pulse.b5_timewindow)) { |
| br->br_numelems--; |
| br->br_firstelem = (br->br_firstelem +1) & DFS_MAX_B5_MASK; |
| index = br->br_firstelem; |
| } else |
| stop = 1; |
| } |
| return 1; |
| } |
| |
| /* |
| * If the dfs structure is NULL (which should be illegal if everyting is working |
| * properly, then signify that a bin5 radar was found |
| */ |
| |
| int |
| dfs_bin5_check(struct ath_dfs *dfs) |
| { |
| struct dfs_bin5radars *br; |
| int index[DFS_MAX_B5_SIZE]; |
| u_int32_t n = 0, i = 0, i1 = 0, this = 0, prev = 0, rssi_diff = 0, width_diff = 0, bursts= 0; |
| u_int32_t total_diff=0, average_diff=0, total_width=0, average_width=0, numevents=0; |
| u_int64_t pri; |
| |
| |
| if (dfs == NULL) { |
| DFS_PRINTK("%s: ic_dfs is NULL\n", __func__); |
| return 1; |
| } |
| for (n=0;n<dfs->dfs_rinfo.rn_numbin5radars; n++) { |
| br = &(dfs->dfs_b5radars[n]); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5, |
| "Num elems = %d\n", br->br_numelems); |
| |
| /* find a valid bin 5 pulse and use it as reference */ |
| for(i1=0;i1 < br->br_numelems; i1++) { |
| this = ((br->br_firstelem +i1) & DFS_MAX_B5_MASK); |
| if ((br->br_elems[this].be_dur >= MIN_BIN5_DUR_MICROSEC) && |
| (br->br_elems[this].be_dur <= MAX_BIN5_DUR_MICROSEC)) { |
| break; |
| } |
| } |
| |
| prev = this; |
| for(i = i1 + 1; i < br->br_numelems; i++){ |
| this = ((br->br_firstelem +i) & DFS_MAX_B5_MASK); |
| |
| /* first make sure it is a bin 5 pulse by checking the duration */ |
| if ((br->br_elems[this].be_dur < MIN_BIN5_DUR_MICROSEC) || (br->br_elems[this].be_dur > MAX_BIN5_DUR_MICROSEC)) { |
| continue; |
| } |
| |
| /* Rule 1: 1000 <= PRI <= 2000 + some margin */ |
| if( br->br_elems[this].be_ts >= br->br_elems[prev].be_ts ) { |
| pri = br->br_elems[this].be_ts - br->br_elems[prev].be_ts; |
| } |
| else {//roll over case |
| //pri = (0xffffffffffffffff - br->br_elems[prev].be_ts) + br->br_elems[this].be_ts; |
| pri = br->br_elems[this].be_ts; |
| } |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5, |
| " pri=%llu this.ts=%llu this.dur=%d this.rssi=%d prev.ts=%llu\n", |
| (unsigned long long)pri, |
| (unsigned long long)br->br_elems[this].be_ts, |
| (int) br->br_elems[this].be_dur, |
| (int) br->br_elems[this].be_rssi, |
| (unsigned long long)br->br_elems[prev].be_ts); |
| if(( (pri >= DFS_BIN5_PRI_LOWER_LIMIT) && (pri <= DFS_BIN5_PRI_HIGHER_LIMIT))) { //pri: pulse repitition interval in us |
| /* Rule 2: pulse width of the pulses in the burst should be same (+/- margin) */ |
| if( br->br_elems[this].be_dur >= br->br_elems[prev].be_dur) { |
| width_diff = br->br_elems[this].be_dur - br->br_elems[prev].be_dur; |
| } |
| else { |
| width_diff = br->br_elems[prev].be_dur - br->br_elems[this].be_dur; |
| } |
| if( width_diff <= DFS_BIN5_WIDTH_MARGIN ) { |
| /* Rule 3: RSSI of the pulses in the burst should be same (+/- margin) */ |
| if( br->br_elems[this].be_rssi >= br->br_elems[prev].be_rssi) { |
| rssi_diff = br->br_elems[this].be_rssi - br->br_elems[prev].be_rssi; |
| } |
| else { |
| rssi_diff = br->br_elems[prev].be_rssi - br->br_elems[this].be_rssi; |
| } |
| if( rssi_diff <= DFS_BIN5_RSSI_MARGIN ) { |
| bursts++; |
| /* Save the indexes of this pair for later width variance check */ |
| if( numevents >= 2 ) { |
| /* make sure the event is not duplicated, |
| * possible in a 3 pulse burst */ |
| if( index[numevents-1] != prev) { |
| index[numevents++] = prev; |
| } |
| } |
| else { |
| index[numevents++] = prev; } |
| index[numevents++] = this; |
| } else { |
| DFS_DPRINTK(dfs, |
| ATH_DEBUG_DFS_BIN5, |
| "%s %d Bin5 rssi_diff=%d\n", |
| __func__, __LINE__, rssi_diff); |
| } |
| } else { |
| DFS_DPRINTK(dfs, |
| ATH_DEBUG_DFS_BIN5, |
| "%s %d Bin5 width_diff=%d\n", |
| __func__, __LINE__, |
| width_diff); |
| } |
| } else if ((pri >= DFS_BIN5_BRI_LOWER_LIMIT) && |
| (pri <= DFS_BIN5_BRI_UPPER_LIMIT)) { |
| // check pulse width to make sure it is in range of bin 5 |
| //if ((br->br_elems[this].be_dur >= MIN_BIN5_DUR_MICROSEC) && (br->br_elems[this].be_dur <= MAX_BIN5_DUR_MICROSEC)) { |
| bursts++; |
| //} |
| } else{ |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5, |
| "%s %d Bin5 PRI check fail pri=%llu\n", |
| __func__, __LINE__, (unsigned long long)pri); |
| } |
| prev = this; |
| } |
| |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5, "bursts=%u numevents=%u\n", bursts, numevents); |
| if ( bursts >= br->br_pulse.b5_threshold) { |
| if( (br->br_elems[br->br_lastelem].be_ts - br->br_elems[br->br_firstelem].be_ts) < 3000000 ) { |
| return 0; |
| } |
| else { |
| /* |
| * don't do this check since not all the cases have this kind of burst width variation. |
| * |
| for (i=0; i<bursts; i++){ |
| total_width += br->br_elems[index[i]].be_dur; |
| } |
| average_width = total_width/bursts; |
| for (i=0; i<bursts; i++){ |
| total_diff += DFS_DIFF(br->br_elems[index[i]].be_dur, average_width); |
| } |
| average_diff = total_diff/bursts; |
| if( average_diff > DFS_BIN5_WIDTH_MARGIN ) { |
| return 1; |
| } else { |
| |
| DFS_DPRINTK(ic, ATH_DEBUG_DFS_BIN5, "bursts=%u numevents=%u total_width=%d average_width=%d total_diff=%d average_diff=%d\n", bursts, numevents, total_width, average_width, total_diff, average_diff); |
| |
| } |
| */ |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5, "bursts=%u numevents=%u total_width=%d average_width=%d total_diff=%d average_diff=%d\n", bursts, numevents, total_width, average_width, total_diff, average_diff); |
| DFS_PRINTK("bin 5 radar detected, bursts=%d\n", bursts); |
| return 1; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| |
| /* Return TRUE if chirping pulse, FALSE if not. |
| Decision is made based on processing the FFT data included with the PHY error. |
| Calculate the slope using the maximum bin index reported in the FFT data. |
| Calculate slope between FFT packet 0 and packet n-1. Also calculate slope between |
| packet 1 and packet n. |
| If a pulse is chirping, a slope of 5 and greater is seen. |
| Non-chirping pulses have slopes of 0, 1, 2 or 3. |
| */ |
| |
| /* |
| * Chirp detection for Sowl/Howl. |
| */ |
| static int |
| dfs_check_chirping_sowl(struct ath_dfs *dfs, void *buf, |
| u_int16_t datalen, int is_ctl, int is_ext, int *slope, int *is_dc) |
| { |
| #define FFT_LEN 70 |
| #define FFT_LOWER_BIN_MAX_INDEX_BYTE 66 |
| #define FFT_UPPER_BIN_MAX_INDEX_BYTE 69 |
| #define MIN_CHIRPING_SLOPE 4 |
| int is_chirp=0; |
| int p, num_fft_packets=0; |
| int ctl_slope=0, ext_slope=0; |
| int ctl_high0, ctl_low0, ctl_slope0=0, ext_high0, ext_low0, ext_slope0=0; |
| int ctl_high1, ctl_low1, ctl_slope1=0, ext_high1, ext_low1, ext_slope1=0; |
| u_int8_t *fft_data_ptr; |
| |
| *slope = 0; |
| *is_dc = 0; |
| |
| num_fft_packets = datalen / FFT_LEN; |
| fft_data_ptr = ((u_int8_t*)buf); |
| |
| /* DEBUG - Print relevant portions of the FFT data*/ |
| for (p=0; p < num_fft_packets; p++) { |
| |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, "fft_data_ptr=0x%pK\t", fft_data_ptr); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, |
| "[66]=%d [69]=%d\n", |
| *(fft_data_ptr+FFT_LOWER_BIN_MAX_INDEX_BYTE) >> 2, |
| *(fft_data_ptr+FFT_UPPER_BIN_MAX_INDEX_BYTE) >> 2); |
| |
| fft_data_ptr += FFT_LEN; |
| } |
| |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, "datalen=%d num_fft_packets=%d\n", datalen, num_fft_packets); |
| |
| /* There is not enough FFT data to figure out whether the pulse is chirping or not*/ |
| if (num_fft_packets < 4) { |
| return 0; |
| } |
| |
| fft_data_ptr = ((u_int8_t*)buf); |
| |
| if (is_ctl) { |
| |
| fft_data_ptr = ((u_int8_t*)buf); |
| ctl_low0 = *(fft_data_ptr+FFT_LOWER_BIN_MAX_INDEX_BYTE) >> 2; |
| fft_data_ptr += FFT_LEN; |
| ctl_low1 = *(fft_data_ptr+FFT_LOWER_BIN_MAX_INDEX_BYTE) >> 2; |
| |
| // last packet with first packet |
| fft_data_ptr = ((u_int8_t*)buf) + (FFT_LEN*(num_fft_packets - 1)); |
| ctl_high1 = *(fft_data_ptr+FFT_LOWER_BIN_MAX_INDEX_BYTE) >> 2; |
| |
| // second last packet with 0th packet |
| fft_data_ptr = ((u_int8_t*)buf) + (FFT_LEN*(num_fft_packets - 2)); |
| ctl_high0 = *(fft_data_ptr+FFT_LOWER_BIN_MAX_INDEX_BYTE) >> 2; |
| |
| ctl_slope0 = ctl_high0 - ctl_low0; |
| if (ctl_slope0 < 0) ctl_slope0 *= (-1); |
| |
| ctl_slope1 = ctl_high1 - ctl_low1; |
| if (ctl_slope1 < 0) ctl_slope1 *= (-1); |
| |
| ctl_slope = ((ctl_slope0 > ctl_slope1) ? ctl_slope0: ctl_slope1); |
| *slope = ctl_slope; |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, "ctl_slope0=%d ctl_slope1=%d ctl_slope=%d\n", |
| ctl_slope0, ctl_slope1, ctl_slope); |
| |
| } else if (is_ext) { |
| |
| fft_data_ptr = ((u_int8_t*)buf); |
| ext_low0 = *(fft_data_ptr+FFT_UPPER_BIN_MAX_INDEX_BYTE) >> 2; |
| |
| fft_data_ptr += FFT_LEN; |
| ext_low1 = *(fft_data_ptr+FFT_UPPER_BIN_MAX_INDEX_BYTE) >> 2; |
| |
| fft_data_ptr = ((u_int8_t*)buf) + (FFT_LEN*(num_fft_packets - 1)); |
| ext_high1 = *(fft_data_ptr+FFT_UPPER_BIN_MAX_INDEX_BYTE) >> 2; |
| fft_data_ptr = ((u_int8_t*)buf) + (FFT_LEN*(num_fft_packets - 2)); |
| ext_high0 = *(fft_data_ptr+FFT_UPPER_BIN_MAX_INDEX_BYTE) >> 2; |
| |
| ext_slope0 = ext_high0 - ext_low0; |
| if (ext_slope0 < 0) ext_slope0 *= (-1); |
| |
| ext_slope1 = ext_high1 - ext_low1; |
| if (ext_slope1 < 0) ext_slope1 *= (-1); |
| |
| ext_slope = ((ext_slope0 > ext_slope1) ? ext_slope0: ext_slope1); |
| *slope = ext_slope; |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT | ATH_DEBUG_DFS_BIN5, |
| "ext_slope0=%d ext_slope1=%d ext_slope=%d\n", |
| ext_slope0, ext_slope1, ext_slope); |
| } else |
| return 0; |
| |
| if ((ctl_slope >= MIN_CHIRPING_SLOPE) || (ext_slope >= MIN_CHIRPING_SLOPE)) { |
| is_chirp = 1; |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5 | ATH_DEBUG_DFS_BIN5_FFT | |
| ATH_DEBUG_DFS_PHYERR_SUM, |
| "is_chirp=%d is_dc=%d\n", is_chirp, *is_dc); |
| } |
| return is_chirp; |
| |
| #undef FFT_LEN |
| #undef FFT_LOWER_BIN_MAX_INDEX_BYTE |
| #undef FFT_UPPER_BIN_MAX_INDEX_BYTE |
| #undef MIN_CHIRPING_SLOPE |
| } |
| |
| /* |
| * Merlin (and Osprey, etc) chirp radar chirp detection. |
| */ |
| static int |
| dfs_check_chirping_merlin(struct ath_dfs *dfs, void *buf, u_int16_t datalen, |
| int is_ctl, int is_ext, int *slope, int *is_dc) |
| { |
| #define ABS_DIFF(_x, _y) ((int)_x > (int)_y ? (int)_x - (int)_y : (int)_y - (int)_x) |
| #define ABS(_x) ((int)_x > 0 ? (int)_x : - (int)_x) |
| |
| #define DELTA_STEP 1 /* This should be between 1 and 3. Default is 1. */ |
| #define NUM_DIFFS 3 /* Number of Diffs to compute. valid range is 2-4 */ |
| #define MAX_DIFF 2 /* Threshold for difference of delta peaks */ |
| #define BIN_COUNT_MAX 6 /* Max. number of strong bins for narrow band */ |
| |
| |
| /* |
| * Dynamic 20/40 mode FFT packet format related definition |
| */ |
| |
| #define NUM_FFT_BYTES_HT40 70 |
| #define NUM_BIN_BYTES_HT40 64 |
| #define NUM_SUBCHAN_BINS_HT40 64 |
| #define LOWER_INDEX_BYTE_HT40 66 |
| #define UPPER_INDEX_BYTE_HT40 69 |
| #define LOWER_WEIGHT_BYTE_HT40 64 |
| #define UPPER_WEIGHT_BYTE_HT40 67 |
| #define LOWER_MAG_BYTE_HT40 65 |
| #define UPPER_MAG_BYTE_HT40 68 |
| |
| /* |
| * Static 20 mode FFT packet format related definition |
| */ |
| |
| #define NUM_FFT_BYTES_HT20 31 |
| #define NUM_BIN_BYTES_HT20 28 |
| #define NUM_SUBCHAN_BINS_HT20 56 |
| #define LOWER_INDEX_BYTE_HT20 30 |
| #define UPPER_INDEX_BYTE_HT20 30 |
| #define LOWER_WEIGHT_BYTE_HT20 28 |
| #define UPPER_WEIGHT_BYTE_HT20 28 |
| #define LOWER_MAG_BYTE_HT20 29 |
| #define UPPER_MAG_BYTE_HT20 29 |
| |
| int num_fft_packets; /* number of FFT packets reported to software */ |
| int num_fft_bytes; |
| int num_bin_bytes; |
| int num_subchan_bins; |
| int lower_index_byte; |
| int upper_index_byte; |
| int lower_weight_byte; |
| int upper_weight_byte; |
| int lower_mag_byte; |
| int upper_mag_byte; |
| |
| int max_index_lower [DELTA_STEP + NUM_DIFFS]; |
| int max_index_upper [DELTA_STEP + NUM_DIFFS]; |
| int max_mag_lower [DELTA_STEP + NUM_DIFFS]; |
| int max_mag_upper [DELTA_STEP + NUM_DIFFS]; |
| int bin_wt_lower [DELTA_STEP + NUM_DIFFS]; |
| int bin_wt_upper [DELTA_STEP + NUM_DIFFS]; |
| int max_mag_sel [DELTA_STEP + NUM_DIFFS]; |
| int max_mag [DELTA_STEP + NUM_DIFFS]; |
| int max_index [DELTA_STEP + NUM_DIFFS]; |
| |
| |
| |
| int max_d[] = {10, 19, 28}; |
| int min_d[] = {1, 2, 3}; |
| |
| u_int8_t *ptr; /* pointer to FFT data */ |
| int i; |
| int fft_start; |
| int chirp_found; |
| int delta_peak[NUM_DIFFS]; |
| int j; |
| int bin_count; |
| int bw_mask; |
| int delta_diff; |
| int same_sign; |
| int temp; |
| |
| adf_os_spin_lock_bh(&dfs->ic->chan_lock); |
| if (IS_CHAN_HT40(dfs->ic->ic_curchan)) { |
| num_fft_bytes = NUM_FFT_BYTES_HT40; |
| num_bin_bytes = NUM_BIN_BYTES_HT40; |
| num_subchan_bins = NUM_SUBCHAN_BINS_HT40; |
| lower_index_byte = LOWER_INDEX_BYTE_HT40; |
| upper_index_byte = UPPER_INDEX_BYTE_HT40; |
| lower_weight_byte = LOWER_WEIGHT_BYTE_HT40; |
| upper_weight_byte = UPPER_WEIGHT_BYTE_HT40; |
| lower_mag_byte = LOWER_MAG_BYTE_HT40; |
| upper_mag_byte = UPPER_MAG_BYTE_HT40; |
| |
| /* if we are in HT40MINUS then swap primary and extension */ |
| if (IS_CHAN_HT40_MINUS(dfs->ic->ic_curchan)) { |
| temp = is_ctl; |
| is_ctl = is_ext; |
| is_ext = temp; |
| } |
| |
| } else { |
| num_fft_bytes = NUM_FFT_BYTES_HT20; |
| num_bin_bytes = NUM_BIN_BYTES_HT20; |
| num_subchan_bins = NUM_SUBCHAN_BINS_HT20; |
| lower_index_byte = LOWER_INDEX_BYTE_HT20; |
| upper_index_byte = UPPER_INDEX_BYTE_HT20; |
| lower_weight_byte = LOWER_WEIGHT_BYTE_HT20; |
| upper_weight_byte = UPPER_WEIGHT_BYTE_HT20; |
| lower_mag_byte = LOWER_MAG_BYTE_HT20; |
| upper_mag_byte = UPPER_MAG_BYTE_HT20; |
| } |
| |
| adf_os_spin_unlock_bh(&dfs->ic->chan_lock); |
| |
| ptr = (u_int8_t*)buf; |
| /* |
| * sanity check for FFT buffer |
| */ |
| |
| if ((ptr == NULL) || (datalen == 0)) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, "%s: FFT buffer pointer is null or size is 0\n", __func__); |
| return 0; |
| } |
| |
| num_fft_packets = (datalen - 3) / num_fft_bytes; |
| if (num_fft_packets < (NUM_DIFFS + DELTA_STEP)) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, "datalen = %d, num_fft_packets = %d, too few packets... (exiting)\n", datalen, num_fft_packets); |
| return 0; |
| } |
| |
| if ((((datalen - 3) % num_fft_bytes) == 2) && (datalen > num_fft_bytes)) { |
| // FIXME !!! |
| ptr += 2; |
| datalen -= 2; |
| } |
| |
| for (i = 0; i < (NUM_DIFFS + DELTA_STEP); i++) { |
| fft_start = i * num_fft_bytes; |
| bin_wt_lower[i] = ptr[fft_start + lower_weight_byte] & 0x3f; |
| bin_wt_upper[i] = ptr[fft_start + upper_weight_byte] & 0x3f; |
| |
| max_index_lower[i] = ptr[fft_start + lower_index_byte] >> 2; |
| max_index_upper[i] = (ptr[fft_start + upper_index_byte] >> 2) + num_subchan_bins; |
| |
| adf_os_spin_lock_bh(&dfs->ic->chan_lock); |
| if (!IS_CHAN_HT40(dfs->ic->ic_curchan)) { |
| /* |
| * for HT20 mode indices are 6 bit signed number |
| */ |
| max_index_lower[i] ^= 0x20; |
| max_index_upper[i] = 0; |
| } |
| |
| adf_os_spin_unlock_bh(&dfs->ic->chan_lock); |
| /* |
| * Reconstruct the maximum magnitude for each sub-channel. Also select |
| * and flag the max overall magnitude between the two sub-channels. |
| */ |
| |
| max_mag_lower[i] = ((ptr[fft_start + lower_index_byte] & 0x03) << 8) + |
| ptr[fft_start + lower_mag_byte]; |
| max_mag_upper[i] = ((ptr[fft_start + upper_index_byte] & 0x03) << 8) + |
| ptr[fft_start + upper_mag_byte]; |
| bw_mask = ((bin_wt_lower[i] == 0) ? 0 : is_ctl) + |
| (((bin_wt_upper[i] == 0) ? 0 : is_ext) << 1); |
| |
| /* |
| * Limit the max bin based on channel bandwidth |
| * If the upper sub-channel max index is stuck at '1', the signal is dominated |
| * by residual DC (or carrier leak) and should be ignored. |
| */ |
| |
| if (bw_mask == 1) { |
| max_mag_sel[i] = 0; |
| max_mag[i] = max_mag_lower[i]; |
| max_index[i] = max_index_lower[i]; |
| } else if(bw_mask == 2) { |
| max_mag_sel[i] = 1; |
| max_mag[i] = max_mag_upper[i]; |
| max_index[i] = max_index_upper[i]; |
| } else if(max_index_upper[i] == num_subchan_bins) { |
| max_mag_sel[i] = 0; /* Ignore DC bin. */ |
| max_mag[i] = max_mag_lower[i]; |
| max_index[i] = max_index_lower[i]; |
| } else { |
| if (max_mag_upper[i] > max_mag_lower[i]) { |
| max_mag_sel[i] = 1; |
| max_mag[i] = max_mag_upper[i]; |
| max_index[i] = max_index_upper[i]; |
| } else { |
| max_mag_sel[i] = 0; |
| max_mag[i] = max_mag_lower[i]; |
| max_index[i] = max_index_lower[i]; |
| } |
| } |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, |
| "i=%d, max_index[i]=%d, max_index_lower[i]=%d, " |
| "max_index_upper[i]=%d\n", |
| i, max_index[i], max_index_lower[i], max_index_upper[i]); |
| } |
| |
| |
| |
| chirp_found = 1; |
| delta_diff = 0; |
| same_sign = 1; |
| |
| /* |
| delta_diff computation -- look for movement in peak. |
| make sure that the chirp direction (i.e. sign) is always the same, |
| i.e. sign of the two peaks should be same. |
| */ |
| for (i = 0; i < NUM_DIFFS; i++) { |
| delta_peak[i] = max_index[i + DELTA_STEP] - max_index[i]; |
| if (i > 0) { |
| delta_diff = delta_peak[i] - delta_peak[i-1]; |
| same_sign = !((delta_peak[i] & 0x80) ^ (delta_peak[i-1] & 0x80)); |
| } |
| chirp_found &= (ABS(delta_peak[i]) >= min_d[DELTA_STEP - 1]) && |
| (ABS(delta_peak[i]) <= max_d[DELTA_STEP - 1]) && |
| same_sign && |
| (ABS(delta_diff) <= MAX_DIFF); |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, |
| "i=%d, delta_peak[i]=%d, delta_diff=%d\n", |
| i, delta_peak[i], delta_diff); |
| } |
| |
| |
| if (chirp_found) |
| { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, |
| "%s: CHIRPING_BEFORE_STRONGBIN_YES\n", __func__); |
| } else { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, |
| "%s: CHIRPING_BEFORE_STRONGBIN_NO\n", __func__); |
| } |
| |
| /* |
| Work around for potential hardware data corruption bug. Check for |
| wide band signal by counting strong bins indicated by bitmap flags. |
| This check is done if chirp_found is true. We do this as a final check |
| to weed out corrupt FFTs bytes. This looks expensive but in most cases it |
| will exit early. |
| */ |
| |
| for (i = 0; (i < (NUM_DIFFS + DELTA_STEP)) && (chirp_found == 1); i++) { |
| bin_count = 0; |
| /* point to the start of the 1st byte of the selected sub-channel. */ |
| fft_start = (i * num_fft_bytes) + (max_mag_sel[i] ? (num_subchan_bins >> 1) : 0); |
| for (j = 0; j < (num_subchan_bins >> 1); j++) |
| { |
| /* |
| * If either bin is flagged "strong", accumulate the bin_count. |
| * It's not accurate, but good enough... |
| */ |
| bin_count += (ptr[fft_start + j] & 0x88) ? 1 : 0; |
| } |
| chirp_found &= (bin_count > BIN_COUNT_MAX) ? 0 : 1; |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT, |
| "i=%d, computed bin_count=%d\n", i, bin_count); |
| } |
| |
| if (chirp_found) |
| { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT | ATH_DEBUG_DFS_PHYERR_SUM, |
| "%s: CHIRPING_YES\n", __func__); |
| } else { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5_FFT | ATH_DEBUG_DFS_PHYERR_SUM, |
| "%s: CHIRPING_NO\n", __func__); |
| } |
| return chirp_found; |
| |
| #undef ABS_DIFF |
| #undef ABS |
| #undef DELTA_STEP |
| #undef NUM_DIFFS |
| #undef MAX_DIFF |
| #undef BIN_COUNT_MAX |
| |
| #undef NUM_FFT_BYTES_HT40 |
| #undef NUM_BIN_BYTES_HT40 |
| #undef NUM_SUBCHAN_BINS_HT40 |
| #undef LOWER_INDEX_BYTE_HT40 |
| #undef UPPER_INDEX_BYTE_HT40 |
| #undef LOWER_WEIGHT_BYTE_HT40 |
| #undef UPPER_WEIGHT_BYTE_HT40 |
| #undef LOWER_MAG_BYTE_HT40 |
| #undef UPPER_MAG_BYTE_HT40 |
| |
| #undef NUM_FFT_BYTES_HT40 |
| #undef NUM_BIN_BYTES_HT40 |
| #undef NUM_SUBCHAN_BINS_HT40 |
| #undef LOWER_INDEX_BYTE_HT40 |
| #undef UPPER_INDEX_BYTE_HT40 |
| #undef LOWER_WEIGHT_BYTE_HT40 |
| #undef UPPER_WEIGHT_BYTE_HT40 |
| #undef LOWER_MAG_BYTE_HT40 |
| #undef UPPER_MAG_BYTE_HT40 |
| } |
| |
| int |
| dfs_check_chirping(struct ath_dfs *dfs, void *buf, |
| u_int16_t datalen, int is_ctl, int is_ext, int *slope, int *is_dc) |
| { |
| |
| if (dfs->dfs_caps.ath_dfs_use_enhancement) |
| return dfs_check_chirping_merlin(dfs, buf, datalen, is_ctl, |
| is_ext, slope, is_dc); |
| else |
| return dfs_check_chirping_sowl(dfs, buf, datalen, is_ctl, |
| is_ext, slope, is_dc); |
| } |
| |
| u_int8_t |
| dfs_retain_bin5_burst_pattern(struct ath_dfs *dfs, u_int32_t diff_ts, |
| u_int8_t old_dur) |
| { |
| |
| /* |
| * Pulses may get split into 2 during chirping, this print is only |
| * to show that it happened, we do not handle this condition if we |
| * cannot detect the chirping. |
| */ |
| /* |
| * SPLIT pulses will have a time stamp difference of < 50 |
| */ |
| if (diff_ts < 50) { |
| DFS_DPRINTK(dfs, ATH_DEBUG_DFS_BIN5, |
| "%s SPLIT pulse diffTs=%u dur=%d (old_dur=%d)\n", |
| __func__, diff_ts, |
| dfs->dfs_rinfo.dfs_last_bin5_dur, old_dur); |
| } |
| /* |
| * Check if this is the 2nd or 3rd pulse in the same burst, |
| * PRI will be between 1000 and 2000 us |
| */ |
| if (((diff_ts >= DFS_BIN5_PRI_LOWER_LIMIT) && |
| (diff_ts <= DFS_BIN5_PRI_HIGHER_LIMIT))) { |
| /* |
| * This pulse belongs to the same burst as the pulse before, |
| * so return the same random duration for it |
| */ |
| DFS_DPRINTK(dfs,ATH_DEBUG_DFS_BIN5, |
| "%s this pulse belongs to the same burst as before, give " |
| "it same dur=%d (old_dur=%d)\n", |
| __func__, dfs->dfs_rinfo.dfs_last_bin5_dur, old_dur); |
| |
| return (dfs->dfs_rinfo.dfs_last_bin5_dur); |
| } |
| /* |
| * This pulse does not belong to this burst, return unchanged |
| * duration. |
| */ |
| return old_dur; |
| } |
| |
| /* |
| * Chirping pulses may get cut off at DC and report lower durations. |
| * |
| * This function will compute a suitable random duration for each pulse. |
| * Duration must be between 50 and 100 us, but remember that in |
| * ath_process_phyerr() which calls this function, we are dealing with the |
| * HW reported duration (unconverted). dfs_process_radarevent() will |
| * actually convert the duration into the correct value. |
| * |
| * XXX This function doesn't take into account whether the hardware |
| * is operating in 5GHz fast clock mode or not. |
| * |
| * XXX And this function doesn't take into account whether the hardware |
| * is peregrine or not. Grr. |
| */ |
| int |
| dfs_get_random_bin5_dur(struct ath_dfs *dfs, u_int64_t tstamp) |
| { |
| u_int8_t new_dur=MIN_BIN5_DUR; |
| int range; |
| |
| get_random_bytes(&new_dur, sizeof(u_int8_t)); |
| |
| range = (MAX_BIN5_DUR - MIN_BIN5_DUR + 1); |
| |
| new_dur %= range; |
| |
| new_dur += MIN_BIN5_DUR; |
| |
| return new_dur; |
| } |
| |
| #endif /* ATH_SUPPORT_DFS */ |