Skip to content

Commit

Permalink
ath9k: DFS - add pulse chirp detection for FCC
Browse files Browse the repository at this point in the history
FCC long pulse radar (type 5) requires pulses to be
checked for chirping. This patch implements chirp
detection based on the FFT data provided for long
pulses.

A chirp is detected when a set of criteria defined
by FCC pulse characteristics is met, including
* have at least 4 FFT samples
* max_bin index moves equidistantly between samples
* the gradient is within defined range

The chirp detection has been tested with reference
radar generating devices and proved to work reliably.

Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
  • Loading branch information
Zefir Kurtisi authored and Kalle Valo committed Jul 11, 2015
1 parent 8f010d9 commit 8fc2b61
Showing 1 changed file with 161 additions and 6 deletions.
167 changes: 161 additions & 6 deletions drivers/net/wireless/ath/ath9k/dfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,157 @@ struct ath_radar_data {
u8 pulse_length_pri;
};

/**** begin: CHIRP ************************************************************/

/* min and max gradients for defined FCC chirping pulses, given by
* - 20MHz chirp width over a pulse width of 50us
* - 5MHz chirp width over a pulse width of 100us
*/
static const int BIN_DELTA_MIN = 1;
static const int BIN_DELTA_MAX = 10;

/* we need at least 3 deltas / 4 samples for a reliable chirp detection */
#define NUM_DIFFS 3
static const int FFT_NUM_SAMPLES = (NUM_DIFFS + 1);

/* Threshold for difference of delta peaks */
static const int MAX_DIFF = 2;

/* width range to be checked for chirping */
static const int MIN_CHIRP_PULSE_WIDTH = 20;
static const int MAX_CHIRP_PULSE_WIDTH = 110;

struct ath9k_dfs_fft_20 {
u8 bin[28];
u8 lower_bins[3];
} __packed;
struct ath9k_dfs_fft_40 {
u8 bin[64];
u8 lower_bins[3];
u8 upper_bins[3];
} __packed;

static inline int fft_max_index(u8 *bins)
{
return (bins[2] & 0xfc) >> 2;
}
static inline int fft_max_magnitude(u8 *bins)
{
return (bins[0] & 0xc0) >> 6 | bins[1] << 2 | (bins[2] & 0x03) << 10;
}
static inline u8 fft_bitmap_weight(u8 *bins)
{
return bins[0] & 0x3f;
}

static int ath9k_get_max_index_ht40(struct ath9k_dfs_fft_40 *fft,
bool is_ctl, bool is_ext)
{
const int DFS_UPPER_BIN_OFFSET = 64;
/* if detected radar on both channels, select the significant one */
if (is_ctl && is_ext) {
/* first check wether channels have 'strong' bins */
is_ctl = fft_bitmap_weight(fft->lower_bins) != 0;
is_ext = fft_bitmap_weight(fft->upper_bins) != 0;

/* if still unclear, take higher magnitude */
if (is_ctl && is_ext) {
int mag_lower = fft_max_magnitude(fft->lower_bins);
int mag_upper = fft_max_magnitude(fft->upper_bins);
if (mag_upper > mag_lower)
is_ctl = false;
else
is_ext = false;
}
}
if (is_ctl)
return fft_max_index(fft->lower_bins);
return fft_max_index(fft->upper_bins) + DFS_UPPER_BIN_OFFSET;
}
static bool ath9k_check_chirping(struct ath_softc *sc, u8 *data,
int datalen, bool is_ctl, bool is_ext)
{
int i;
int max_bin[FFT_NUM_SAMPLES];
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
int prev_delta;

if (IS_CHAN_HT40(ah->curchan)) {
struct ath9k_dfs_fft_40 *fft = (struct ath9k_dfs_fft_40 *) data;
int num_fft_packets = datalen / sizeof(*fft);
if (num_fft_packets == 0)
return false;

ath_dbg(common, DFS, "HT40: datalen=%d, num_fft_packets=%d\n",
datalen, num_fft_packets);
if (num_fft_packets < (FFT_NUM_SAMPLES)) {
ath_dbg(common, DFS, "not enough packets for chirp\n");
return false;
}
/* HW sometimes adds 2 garbage bytes in front of FFT samples */
if ((datalen % sizeof(*fft)) == 2) {
fft = (struct ath9k_dfs_fft_40 *) (data + 2);
ath_dbg(common, DFS, "fixing datalen by 2\n");
}
if (IS_CHAN_HT40MINUS(ah->curchan)) {
int temp = is_ctl;
is_ctl = is_ext;
is_ext = temp;
}
for (i = 0; i < FFT_NUM_SAMPLES; i++)
max_bin[i] = ath9k_get_max_index_ht40(fft + i, is_ctl,
is_ext);
} else {
struct ath9k_dfs_fft_20 *fft = (struct ath9k_dfs_fft_20 *) data;
int num_fft_packets = datalen / sizeof(*fft);
if (num_fft_packets == 0)
return false;
ath_dbg(common, DFS, "HT20: datalen=%d, num_fft_packets=%d\n",
datalen, num_fft_packets);
if (num_fft_packets < (FFT_NUM_SAMPLES)) {
ath_dbg(common, DFS, "not enough packets for chirp\n");
return false;
}
/* in ht20, this is a 6-bit signed number => shift it to 0 */
for (i = 0; i < FFT_NUM_SAMPLES; i++)
max_bin[i] = fft_max_index(fft[i].lower_bins) ^ 0x20;
}
ath_dbg(common, DFS, "bin_max = [%d, %d, %d, %d]\n",
max_bin[0], max_bin[1], max_bin[2], max_bin[3]);

/* Check for chirp attributes within specs
* a) delta of adjacent max_bins is within range
* b) delta of adjacent deltas are within tolerance
*/
prev_delta = 0;
for (i = 0; i < NUM_DIFFS; i++) {
int ddelta = -1;
int delta = max_bin[i + 1] - max_bin[i];

/* ensure gradient is within valid range */
if (abs(delta) < BIN_DELTA_MIN || abs(delta) > BIN_DELTA_MAX) {
ath_dbg(common, DFS, "CHIRP: invalid delta %d "
"in sample %d\n", delta, i);
return false;
}
if (i == 0)
goto done;
ddelta = delta - prev_delta;
if (abs(ddelta) > MAX_DIFF) {
ath_dbg(common, DFS, "CHIRP: ddelta %d too high\n",
ddelta);
return false;
}
done:
ath_dbg(common, DFS, "CHIRP - %d: delta=%d, ddelta=%d\n",
i, delta, ddelta);
prev_delta = delta;
}
return true;
}
/**** end: CHIRP **************************************************************/

/* convert pulse duration to usecs, considering clock mode */
static u32 dur_to_usecs(struct ath_hw *ah, u32 dur)
{
Expand Down Expand Up @@ -113,12 +264,6 @@ ath9k_postprocess_radar_event(struct ath_softc *sc,
return false;
}

/*
* TODO: check chirping pulses
* checks for chirping are dependent on the DFS regulatory domain
* used, which is yet TBD
*/

/* convert duration to usecs */
pe->width = dur_to_usecs(sc->sc_ah, dur);
pe->rssi = rssi;
Expand Down Expand Up @@ -190,6 +335,16 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
if (!ath9k_postprocess_radar_event(sc, &ard, &pe))
return;

if (pe.width > MIN_CHIRP_PULSE_WIDTH &&
pe.width < MAX_CHIRP_PULSE_WIDTH) {
bool is_ctl = !!(ard.pulse_bw_info & PRI_CH_RADAR_FOUND);
bool is_ext = !!(ard.pulse_bw_info & EXT_CH_RADAR_FOUND);
int clen = datalen - 3;
pe.chirp = ath9k_check_chirping(sc, data, clen, is_ctl, is_ext);
} else {
pe.chirp = false;
}

ath_dbg(common, DFS,
"ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, "
"width=%d, rssi=%d, delta_ts=%llu\n",
Expand Down

0 comments on commit 8fc2b61

Please sign in to comment.