Commit 8fc2b61a authored by Zefir Kurtisi's avatar Zefir Kurtisi Committed by Kalle Valo

ath9k: DFS - add pulse chirp detection for FCC

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: default avatarZefir Kurtisi <zefir.kurtisi@neratec.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 8f010d9c
...@@ -30,6 +30,157 @@ struct ath_radar_data { ...@@ -30,6 +30,157 @@ struct ath_radar_data {
u8 pulse_length_pri; 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 */ /* convert pulse duration to usecs, considering clock mode */
static u32 dur_to_usecs(struct ath_hw *ah, u32 dur) static u32 dur_to_usecs(struct ath_hw *ah, u32 dur)
{ {
...@@ -113,12 +264,6 @@ ath9k_postprocess_radar_event(struct ath_softc *sc, ...@@ -113,12 +264,6 @@ ath9k_postprocess_radar_event(struct ath_softc *sc,
return false; 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 */ /* convert duration to usecs */
pe->width = dur_to_usecs(sc->sc_ah, dur); pe->width = dur_to_usecs(sc->sc_ah, dur);
pe->rssi = rssi; pe->rssi = rssi;
...@@ -190,6 +335,16 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, ...@@ -190,6 +335,16 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
if (!ath9k_postprocess_radar_event(sc, &ard, &pe)) if (!ath9k_postprocess_radar_event(sc, &ard, &pe))
return; 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, ath_dbg(common, DFS,
"ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, " "ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, "
"width=%d, rssi=%d, delta_ts=%llu\n", "width=%d, rssi=%d, delta_ts=%llu\n",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment