aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ath/ath9k/dfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ath/ath9k/dfs.c')
-rw-r--r--drivers/net/wireless/ath/ath9k/dfs.c170
1 files changed, 163 insertions, 7 deletions
diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c
index e98a9eaba7ff..1ece42c2443d 100644
--- a/drivers/net/wireless/ath/ath9k/dfs.c
+++ b/drivers/net/wireless/ath/ath9k/dfs.c
@@ -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)
{
@@ -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;
@@ -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",
@@ -198,7 +353,8 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
sc->dfs_prev_pulse_ts = pe.ts;
if (ard.pulse_bw_info & PRI_CH_RADAR_FOUND)
ath9k_dfs_process_radar_pulse(sc, &pe);
- if (ard.pulse_bw_info & EXT_CH_RADAR_FOUND) {
+ if (IS_CHAN_HT40(ah->curchan) &&
+ ard.pulse_bw_info & EXT_CH_RADAR_FOUND) {
pe.freq += IS_CHAN_HT40PLUS(ah->curchan) ? 20 : -20;
ath9k_dfs_process_radar_pulse(sc, &pe);
}