18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc.
38c2ecf20Sopenharmony_ci * Copyright (c) 2011 Neratec Solutions AG
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
68c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
78c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
108c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
118c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
128c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
138c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
148c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
158c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "hw.h"
198c2ecf20Sopenharmony_ci#include "hw-ops.h"
208c2ecf20Sopenharmony_ci#include "ath9k.h"
218c2ecf20Sopenharmony_ci#include "dfs.h"
228c2ecf20Sopenharmony_ci#include "dfs_debug.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* internal struct to pass radar data */
258c2ecf20Sopenharmony_cistruct ath_radar_data {
268c2ecf20Sopenharmony_ci	u8 pulse_bw_info;
278c2ecf20Sopenharmony_ci	u8 rssi;
288c2ecf20Sopenharmony_ci	u8 ext_rssi;
298c2ecf20Sopenharmony_ci	u8 pulse_length_ext;
308c2ecf20Sopenharmony_ci	u8 pulse_length_pri;
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/**** begin: CHIRP ************************************************************/
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* min and max gradients for defined FCC chirping pulses, given by
368c2ecf20Sopenharmony_ci * - 20MHz chirp width over a pulse width of  50us
378c2ecf20Sopenharmony_ci * -  5MHz chirp width over a pulse width of 100us
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cistatic const int BIN_DELTA_MIN		= 1;
408c2ecf20Sopenharmony_cistatic const int BIN_DELTA_MAX		= 10;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* we need at least 3 deltas / 4 samples for a reliable chirp detection */
438c2ecf20Sopenharmony_ci#define NUM_DIFFS 3
448c2ecf20Sopenharmony_ci#define FFT_NUM_SAMPLES		(NUM_DIFFS + 1)
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* Threshold for difference of delta peaks */
478c2ecf20Sopenharmony_cistatic const int MAX_DIFF		= 2;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* width range to be checked for chirping */
508c2ecf20Sopenharmony_cistatic const int MIN_CHIRP_PULSE_WIDTH	= 20;
518c2ecf20Sopenharmony_cistatic const int MAX_CHIRP_PULSE_WIDTH	= 110;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistruct ath9k_dfs_fft_20 {
548c2ecf20Sopenharmony_ci	u8 bin[28];
558c2ecf20Sopenharmony_ci	u8 lower_bins[3];
568c2ecf20Sopenharmony_ci} __packed;
578c2ecf20Sopenharmony_cistruct ath9k_dfs_fft_40 {
588c2ecf20Sopenharmony_ci	u8 bin[64];
598c2ecf20Sopenharmony_ci	u8 lower_bins[3];
608c2ecf20Sopenharmony_ci	u8 upper_bins[3];
618c2ecf20Sopenharmony_ci} __packed;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic inline int fft_max_index(u8 *bins)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	return (bins[2] & 0xfc) >> 2;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_cistatic inline int fft_max_magnitude(u8 *bins)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	return (bins[0] & 0xc0) >> 6 | bins[1] << 2 | (bins[2] & 0x03) << 10;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_cistatic inline u8 fft_bitmap_weight(u8 *bins)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	return bins[0] & 0x3f;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int ath9k_get_max_index_ht40(struct ath9k_dfs_fft_40 *fft,
778c2ecf20Sopenharmony_ci				    bool is_ctl, bool is_ext)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	const int DFS_UPPER_BIN_OFFSET = 64;
808c2ecf20Sopenharmony_ci	/* if detected radar on both channels, select the significant one */
818c2ecf20Sopenharmony_ci	if (is_ctl && is_ext) {
828c2ecf20Sopenharmony_ci		/* first check wether channels have 'strong' bins */
838c2ecf20Sopenharmony_ci		is_ctl = fft_bitmap_weight(fft->lower_bins) != 0;
848c2ecf20Sopenharmony_ci		is_ext = fft_bitmap_weight(fft->upper_bins) != 0;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		/* if still unclear, take higher magnitude */
878c2ecf20Sopenharmony_ci		if (is_ctl && is_ext) {
888c2ecf20Sopenharmony_ci			int mag_lower = fft_max_magnitude(fft->lower_bins);
898c2ecf20Sopenharmony_ci			int mag_upper = fft_max_magnitude(fft->upper_bins);
908c2ecf20Sopenharmony_ci			if (mag_upper > mag_lower)
918c2ecf20Sopenharmony_ci				is_ctl = false;
928c2ecf20Sopenharmony_ci			else
938c2ecf20Sopenharmony_ci				is_ext = false;
948c2ecf20Sopenharmony_ci		}
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci	if (is_ctl)
978c2ecf20Sopenharmony_ci		return fft_max_index(fft->lower_bins);
988c2ecf20Sopenharmony_ci	return fft_max_index(fft->upper_bins) + DFS_UPPER_BIN_OFFSET;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_cistatic bool ath9k_check_chirping(struct ath_softc *sc, u8 *data,
1018c2ecf20Sopenharmony_ci				 int datalen, bool is_ctl, bool is_ext)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	int i;
1048c2ecf20Sopenharmony_ci	int max_bin[FFT_NUM_SAMPLES];
1058c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
1068c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
1078c2ecf20Sopenharmony_ci	int prev_delta;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (IS_CHAN_HT40(ah->curchan)) {
1108c2ecf20Sopenharmony_ci		struct ath9k_dfs_fft_40 *fft = (struct ath9k_dfs_fft_40 *) data;
1118c2ecf20Sopenharmony_ci		int num_fft_packets = datalen / sizeof(*fft);
1128c2ecf20Sopenharmony_ci		if (num_fft_packets == 0)
1138c2ecf20Sopenharmony_ci			return false;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci		ath_dbg(common, DFS, "HT40: datalen=%d, num_fft_packets=%d\n",
1168c2ecf20Sopenharmony_ci			datalen, num_fft_packets);
1178c2ecf20Sopenharmony_ci		if (num_fft_packets < FFT_NUM_SAMPLES) {
1188c2ecf20Sopenharmony_ci			ath_dbg(common, DFS, "not enough packets for chirp\n");
1198c2ecf20Sopenharmony_ci			return false;
1208c2ecf20Sopenharmony_ci		}
1218c2ecf20Sopenharmony_ci		/* HW sometimes adds 2 garbage bytes in front of FFT samples */
1228c2ecf20Sopenharmony_ci		if ((datalen % sizeof(*fft)) == 2) {
1238c2ecf20Sopenharmony_ci			fft = (struct ath9k_dfs_fft_40 *) (data + 2);
1248c2ecf20Sopenharmony_ci			ath_dbg(common, DFS, "fixing datalen by 2\n");
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci		if (IS_CHAN_HT40MINUS(ah->curchan))
1278c2ecf20Sopenharmony_ci			swap(is_ctl, is_ext);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		for (i = 0; i < FFT_NUM_SAMPLES; i++)
1308c2ecf20Sopenharmony_ci			max_bin[i] = ath9k_get_max_index_ht40(fft + i, is_ctl,
1318c2ecf20Sopenharmony_ci							      is_ext);
1328c2ecf20Sopenharmony_ci	} else {
1338c2ecf20Sopenharmony_ci		struct ath9k_dfs_fft_20 *fft = (struct ath9k_dfs_fft_20 *) data;
1348c2ecf20Sopenharmony_ci		int num_fft_packets = datalen / sizeof(*fft);
1358c2ecf20Sopenharmony_ci		if (num_fft_packets == 0)
1368c2ecf20Sopenharmony_ci			return false;
1378c2ecf20Sopenharmony_ci		ath_dbg(common, DFS, "HT20: datalen=%d, num_fft_packets=%d\n",
1388c2ecf20Sopenharmony_ci			datalen, num_fft_packets);
1398c2ecf20Sopenharmony_ci		if (num_fft_packets < FFT_NUM_SAMPLES) {
1408c2ecf20Sopenharmony_ci			ath_dbg(common, DFS, "not enough packets for chirp\n");
1418c2ecf20Sopenharmony_ci			return false;
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci		/* in ht20, this is a 6-bit signed number => shift it to 0 */
1448c2ecf20Sopenharmony_ci		for (i = 0; i < FFT_NUM_SAMPLES; i++)
1458c2ecf20Sopenharmony_ci			max_bin[i] = fft_max_index(fft[i].lower_bins) ^ 0x20;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci	ath_dbg(common, DFS, "bin_max = [%d, %d, %d, %d]\n",
1488c2ecf20Sopenharmony_ci		max_bin[0], max_bin[1], max_bin[2], max_bin[3]);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	/* Check for chirp attributes within specs
1518c2ecf20Sopenharmony_ci	 * a) delta of adjacent max_bins is within range
1528c2ecf20Sopenharmony_ci	 * b) delta of adjacent deltas are within tolerance
1538c2ecf20Sopenharmony_ci	 */
1548c2ecf20Sopenharmony_ci	prev_delta = 0;
1558c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_DIFFS; i++) {
1568c2ecf20Sopenharmony_ci		int ddelta = -1;
1578c2ecf20Sopenharmony_ci		int delta = max_bin[i + 1] - max_bin[i];
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		/* ensure gradient is within valid range */
1608c2ecf20Sopenharmony_ci		if (abs(delta) < BIN_DELTA_MIN || abs(delta) > BIN_DELTA_MAX) {
1618c2ecf20Sopenharmony_ci			ath_dbg(common, DFS, "CHIRP: invalid delta %d "
1628c2ecf20Sopenharmony_ci				"in sample %d\n", delta, i);
1638c2ecf20Sopenharmony_ci			return false;
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci		if (i == 0)
1668c2ecf20Sopenharmony_ci			goto done;
1678c2ecf20Sopenharmony_ci		ddelta = delta - prev_delta;
1688c2ecf20Sopenharmony_ci		if (abs(ddelta) > MAX_DIFF) {
1698c2ecf20Sopenharmony_ci			ath_dbg(common, DFS, "CHIRP: ddelta %d too high\n",
1708c2ecf20Sopenharmony_ci				ddelta);
1718c2ecf20Sopenharmony_ci			return false;
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_cidone:
1748c2ecf20Sopenharmony_ci		ath_dbg(common, DFS, "CHIRP - %d: delta=%d, ddelta=%d\n",
1758c2ecf20Sopenharmony_ci			i, delta, ddelta);
1768c2ecf20Sopenharmony_ci		prev_delta = delta;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci	return true;
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci/**** end: CHIRP **************************************************************/
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* convert pulse duration to usecs, considering clock mode */
1838c2ecf20Sopenharmony_cistatic u32 dur_to_usecs(struct ath_hw *ah, u32 dur)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	const u32 AR93X_NSECS_PER_DUR = 800;
1868c2ecf20Sopenharmony_ci	const u32 AR93X_NSECS_PER_DUR_FAST = (8000 / 11);
1878c2ecf20Sopenharmony_ci	u32 nsecs;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (IS_CHAN_A_FAST_CLOCK(ah, ah->curchan))
1908c2ecf20Sopenharmony_ci		nsecs = dur * AR93X_NSECS_PER_DUR_FAST;
1918c2ecf20Sopenharmony_ci	else
1928c2ecf20Sopenharmony_ci		nsecs = dur * AR93X_NSECS_PER_DUR;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return (nsecs + 500) / 1000;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci#define PRI_CH_RADAR_FOUND 0x01
1988c2ecf20Sopenharmony_ci#define EXT_CH_RADAR_FOUND 0x02
1998c2ecf20Sopenharmony_cistatic bool
2008c2ecf20Sopenharmony_ciath9k_postprocess_radar_event(struct ath_softc *sc,
2018c2ecf20Sopenharmony_ci			      struct ath_radar_data *ard,
2028c2ecf20Sopenharmony_ci			      struct pulse_event *pe)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	u8 rssi;
2058c2ecf20Sopenharmony_ci	u16 dur;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/*
2088c2ecf20Sopenharmony_ci	 * Only the last 2 bits of the BW info are relevant, they indicate
2098c2ecf20Sopenharmony_ci	 * which channel the radar was detected in.
2108c2ecf20Sopenharmony_ci	 */
2118c2ecf20Sopenharmony_ci	ard->pulse_bw_info &= 0x03;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	switch (ard->pulse_bw_info) {
2148c2ecf20Sopenharmony_ci	case PRI_CH_RADAR_FOUND:
2158c2ecf20Sopenharmony_ci		/* radar in ctrl channel */
2168c2ecf20Sopenharmony_ci		dur = ard->pulse_length_pri;
2178c2ecf20Sopenharmony_ci		DFS_STAT_INC(sc, pri_phy_errors);
2188c2ecf20Sopenharmony_ci		/*
2198c2ecf20Sopenharmony_ci		 * cannot use ctrl channel RSSI
2208c2ecf20Sopenharmony_ci		 * if extension channel is stronger
2218c2ecf20Sopenharmony_ci		 */
2228c2ecf20Sopenharmony_ci		rssi = (ard->ext_rssi >= (ard->rssi + 3)) ? 0 : ard->rssi;
2238c2ecf20Sopenharmony_ci		break;
2248c2ecf20Sopenharmony_ci	case EXT_CH_RADAR_FOUND:
2258c2ecf20Sopenharmony_ci		/* radar in extension channel */
2268c2ecf20Sopenharmony_ci		dur = ard->pulse_length_ext;
2278c2ecf20Sopenharmony_ci		DFS_STAT_INC(sc, ext_phy_errors);
2288c2ecf20Sopenharmony_ci		/*
2298c2ecf20Sopenharmony_ci		 * cannot use extension channel RSSI
2308c2ecf20Sopenharmony_ci		 * if control channel is stronger
2318c2ecf20Sopenharmony_ci		 */
2328c2ecf20Sopenharmony_ci		rssi = (ard->rssi >= (ard->ext_rssi + 12)) ? 0 : ard->ext_rssi;
2338c2ecf20Sopenharmony_ci		break;
2348c2ecf20Sopenharmony_ci	case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND):
2358c2ecf20Sopenharmony_ci		/*
2368c2ecf20Sopenharmony_ci		 * Conducted testing, when pulse is on DC, both pri and ext
2378c2ecf20Sopenharmony_ci		 * durations are reported to be same
2388c2ecf20Sopenharmony_ci		 *
2398c2ecf20Sopenharmony_ci		 * Radiated testing, when pulse is on DC, different pri and
2408c2ecf20Sopenharmony_ci		 * ext durations are reported, so take the larger of the two
2418c2ecf20Sopenharmony_ci		 */
2428c2ecf20Sopenharmony_ci		if (ard->pulse_length_ext >= ard->pulse_length_pri)
2438c2ecf20Sopenharmony_ci			dur = ard->pulse_length_ext;
2448c2ecf20Sopenharmony_ci		else
2458c2ecf20Sopenharmony_ci			dur = ard->pulse_length_pri;
2468c2ecf20Sopenharmony_ci		DFS_STAT_INC(sc, dc_phy_errors);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		/* when both are present use stronger one */
2498c2ecf20Sopenharmony_ci		rssi = (ard->rssi < ard->ext_rssi) ? ard->ext_rssi : ard->rssi;
2508c2ecf20Sopenharmony_ci		break;
2518c2ecf20Sopenharmony_ci	default:
2528c2ecf20Sopenharmony_ci		/*
2538c2ecf20Sopenharmony_ci		 * Bogus bandwidth info was received in descriptor,
2548c2ecf20Sopenharmony_ci		 * so ignore this PHY error
2558c2ecf20Sopenharmony_ci		 */
2568c2ecf20Sopenharmony_ci		DFS_STAT_INC(sc, bwinfo_discards);
2578c2ecf20Sopenharmony_ci		return false;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (rssi == 0) {
2618c2ecf20Sopenharmony_ci		DFS_STAT_INC(sc, rssi_discards);
2628c2ecf20Sopenharmony_ci		return false;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	/* convert duration to usecs */
2668c2ecf20Sopenharmony_ci	pe->width = dur_to_usecs(sc->sc_ah, dur);
2678c2ecf20Sopenharmony_ci	pe->rssi = rssi;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	DFS_STAT_INC(sc, pulses_detected);
2708c2ecf20Sopenharmony_ci	return true;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic void
2748c2ecf20Sopenharmony_ciath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	struct dfs_pattern_detector *pd = sc->dfs_detector;
2778c2ecf20Sopenharmony_ci	DFS_STAT_INC(sc, pulses_processed);
2788c2ecf20Sopenharmony_ci	if (pd == NULL)
2798c2ecf20Sopenharmony_ci		return;
2808c2ecf20Sopenharmony_ci	if (!pd->add_pulse(pd, pe, NULL))
2818c2ecf20Sopenharmony_ci		return;
2828c2ecf20Sopenharmony_ci	DFS_STAT_INC(sc, radar_detected);
2838c2ecf20Sopenharmony_ci	ieee80211_radar_detected(sc->hw);
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci/*
2878c2ecf20Sopenharmony_ci * DFS: check PHY-error for radar pulse and feed the detector
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_civoid ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
2908c2ecf20Sopenharmony_ci			      struct ath_rx_status *rs, u64 mactime)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	struct ath_radar_data ard;
2938c2ecf20Sopenharmony_ci	u16 datalen;
2948c2ecf20Sopenharmony_ci	char *vdata_end;
2958c2ecf20Sopenharmony_ci	struct pulse_event pe;
2968c2ecf20Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
2978c2ecf20Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	DFS_STAT_INC(sc, pulses_total);
3008c2ecf20Sopenharmony_ci	if ((rs->rs_phyerr != ATH9K_PHYERR_RADAR) &&
3018c2ecf20Sopenharmony_ci	    (rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT)) {
3028c2ecf20Sopenharmony_ci		ath_dbg(common, DFS,
3038c2ecf20Sopenharmony_ci			"Error: rs_phyer=0x%x not a radar error\n",
3048c2ecf20Sopenharmony_ci			rs->rs_phyerr);
3058c2ecf20Sopenharmony_ci		DFS_STAT_INC(sc, pulses_no_dfs);
3068c2ecf20Sopenharmony_ci		return;
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	datalen = rs->rs_datalen;
3108c2ecf20Sopenharmony_ci	if (datalen == 0) {
3118c2ecf20Sopenharmony_ci		DFS_STAT_INC(sc, datalen_discards);
3128c2ecf20Sopenharmony_ci		return;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	ard.rssi = rs->rs_rssi_ctl[0];
3168c2ecf20Sopenharmony_ci	ard.ext_rssi = rs->rs_rssi_ext[0];
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/*
3198c2ecf20Sopenharmony_ci	 * hardware stores this as 8 bit signed value.
3208c2ecf20Sopenharmony_ci	 * we will cap it at 0 if it is a negative number
3218c2ecf20Sopenharmony_ci	 */
3228c2ecf20Sopenharmony_ci	if (ard.rssi & 0x80)
3238c2ecf20Sopenharmony_ci		ard.rssi = 0;
3248c2ecf20Sopenharmony_ci	if (ard.ext_rssi & 0x80)
3258c2ecf20Sopenharmony_ci		ard.ext_rssi = 0;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	vdata_end = data + datalen;
3288c2ecf20Sopenharmony_ci	ard.pulse_bw_info = vdata_end[-1];
3298c2ecf20Sopenharmony_ci	ard.pulse_length_ext = vdata_end[-2];
3308c2ecf20Sopenharmony_ci	ard.pulse_length_pri = vdata_end[-3];
3318c2ecf20Sopenharmony_ci	pe.freq = ah->curchan->channel;
3328c2ecf20Sopenharmony_ci	pe.ts = mactime;
3338c2ecf20Sopenharmony_ci	if (!ath9k_postprocess_radar_event(sc, &ard, &pe))
3348c2ecf20Sopenharmony_ci		return;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	if (pe.width > MIN_CHIRP_PULSE_WIDTH &&
3378c2ecf20Sopenharmony_ci	    pe.width < MAX_CHIRP_PULSE_WIDTH) {
3388c2ecf20Sopenharmony_ci		bool is_ctl = !!(ard.pulse_bw_info & PRI_CH_RADAR_FOUND);
3398c2ecf20Sopenharmony_ci		bool is_ext = !!(ard.pulse_bw_info & EXT_CH_RADAR_FOUND);
3408c2ecf20Sopenharmony_ci		int clen = datalen - 3;
3418c2ecf20Sopenharmony_ci		pe.chirp = ath9k_check_chirping(sc, data, clen, is_ctl, is_ext);
3428c2ecf20Sopenharmony_ci	} else {
3438c2ecf20Sopenharmony_ci		pe.chirp = false;
3448c2ecf20Sopenharmony_ci	}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	ath_dbg(common, DFS,
3478c2ecf20Sopenharmony_ci		"ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, "
3488c2ecf20Sopenharmony_ci		"width=%d, rssi=%d, delta_ts=%llu\n",
3498c2ecf20Sopenharmony_ci		ard.pulse_bw_info, pe.freq, pe.ts, pe.width, pe.rssi,
3508c2ecf20Sopenharmony_ci		pe.ts - sc->dfs_prev_pulse_ts);
3518c2ecf20Sopenharmony_ci	sc->dfs_prev_pulse_ts = pe.ts;
3528c2ecf20Sopenharmony_ci	if (ard.pulse_bw_info & PRI_CH_RADAR_FOUND)
3538c2ecf20Sopenharmony_ci		ath9k_dfs_process_radar_pulse(sc, &pe);
3548c2ecf20Sopenharmony_ci	if (IS_CHAN_HT40(ah->curchan) &&
3558c2ecf20Sopenharmony_ci	    ard.pulse_bw_info & EXT_CH_RADAR_FOUND) {
3568c2ecf20Sopenharmony_ci		pe.freq += IS_CHAN_HT40PLUS(ah->curchan) ? 20 : -20;
3578c2ecf20Sopenharmony_ci		ath9k_dfs_process_radar_pulse(sc, &pe);
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci#undef PRI_CH_RADAR_FOUND
3618c2ecf20Sopenharmony_ci#undef EXT_CH_RADAR_FOUND
362