162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2008-2011 Atheros Communications Inc.
362306a36Sopenharmony_ci * Copyright (c) 2011 Neratec Solutions AG
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any
662306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
762306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1062306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1162306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1262306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1362306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1462306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1562306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "hw.h"
1962306a36Sopenharmony_ci#include "hw-ops.h"
2062306a36Sopenharmony_ci#include "ath9k.h"
2162306a36Sopenharmony_ci#include "dfs.h"
2262306a36Sopenharmony_ci#include "dfs_debug.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/* internal struct to pass radar data */
2562306a36Sopenharmony_cistruct ath_radar_data {
2662306a36Sopenharmony_ci	u8 pulse_bw_info;
2762306a36Sopenharmony_ci	u8 rssi;
2862306a36Sopenharmony_ci	u8 ext_rssi;
2962306a36Sopenharmony_ci	u8 pulse_length_ext;
3062306a36Sopenharmony_ci	u8 pulse_length_pri;
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/**** begin: CHIRP ************************************************************/
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* min and max gradients for defined FCC chirping pulses, given by
3662306a36Sopenharmony_ci * - 20MHz chirp width over a pulse width of  50us
3762306a36Sopenharmony_ci * -  5MHz chirp width over a pulse width of 100us
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_cistatic const int BIN_DELTA_MIN		= 1;
4062306a36Sopenharmony_cistatic const int BIN_DELTA_MAX		= 10;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* we need at least 3 deltas / 4 samples for a reliable chirp detection */
4362306a36Sopenharmony_ci#define NUM_DIFFS 3
4462306a36Sopenharmony_ci#define FFT_NUM_SAMPLES		(NUM_DIFFS + 1)
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* Threshold for difference of delta peaks */
4762306a36Sopenharmony_cistatic const int MAX_DIFF		= 2;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* width range to be checked for chirping */
5062306a36Sopenharmony_cistatic const int MIN_CHIRP_PULSE_WIDTH	= 20;
5162306a36Sopenharmony_cistatic const int MAX_CHIRP_PULSE_WIDTH	= 110;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistruct ath9k_dfs_fft_20 {
5462306a36Sopenharmony_ci	u8 bin[28];
5562306a36Sopenharmony_ci	u8 lower_bins[3];
5662306a36Sopenharmony_ci} __packed;
5762306a36Sopenharmony_cistruct ath9k_dfs_fft_40 {
5862306a36Sopenharmony_ci	u8 bin[64];
5962306a36Sopenharmony_ci	u8 lower_bins[3];
6062306a36Sopenharmony_ci	u8 upper_bins[3];
6162306a36Sopenharmony_ci} __packed;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic inline int fft_max_index(u8 *bins)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	return (bins[2] & 0xfc) >> 2;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_cistatic inline int fft_max_magnitude(u8 *bins)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	return (bins[0] & 0xc0) >> 6 | bins[1] << 2 | (bins[2] & 0x03) << 10;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_cistatic inline u8 fft_bitmap_weight(u8 *bins)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	return bins[0] & 0x3f;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int ath9k_get_max_index_ht40(struct ath9k_dfs_fft_40 *fft,
7762306a36Sopenharmony_ci				    bool is_ctl, bool is_ext)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	const int DFS_UPPER_BIN_OFFSET = 64;
8062306a36Sopenharmony_ci	/* if detected radar on both channels, select the significant one */
8162306a36Sopenharmony_ci	if (is_ctl && is_ext) {
8262306a36Sopenharmony_ci		/* first check wether channels have 'strong' bins */
8362306a36Sopenharmony_ci		is_ctl = fft_bitmap_weight(fft->lower_bins) != 0;
8462306a36Sopenharmony_ci		is_ext = fft_bitmap_weight(fft->upper_bins) != 0;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		/* if still unclear, take higher magnitude */
8762306a36Sopenharmony_ci		if (is_ctl && is_ext) {
8862306a36Sopenharmony_ci			int mag_lower = fft_max_magnitude(fft->lower_bins);
8962306a36Sopenharmony_ci			int mag_upper = fft_max_magnitude(fft->upper_bins);
9062306a36Sopenharmony_ci			if (mag_upper > mag_lower)
9162306a36Sopenharmony_ci				is_ctl = false;
9262306a36Sopenharmony_ci			else
9362306a36Sopenharmony_ci				is_ext = false;
9462306a36Sopenharmony_ci		}
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci	if (is_ctl)
9762306a36Sopenharmony_ci		return fft_max_index(fft->lower_bins);
9862306a36Sopenharmony_ci	return fft_max_index(fft->upper_bins) + DFS_UPPER_BIN_OFFSET;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_cistatic bool ath9k_check_chirping(struct ath_softc *sc, u8 *data,
10162306a36Sopenharmony_ci				 int datalen, bool is_ctl, bool is_ext)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	int i;
10462306a36Sopenharmony_ci	int max_bin[FFT_NUM_SAMPLES];
10562306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
10662306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
10762306a36Sopenharmony_ci	int prev_delta;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (IS_CHAN_HT40(ah->curchan)) {
11062306a36Sopenharmony_ci		struct ath9k_dfs_fft_40 *fft = (struct ath9k_dfs_fft_40 *) data;
11162306a36Sopenharmony_ci		int num_fft_packets = datalen / sizeof(*fft);
11262306a36Sopenharmony_ci		if (num_fft_packets == 0)
11362306a36Sopenharmony_ci			return false;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci		ath_dbg(common, DFS, "HT40: datalen=%d, num_fft_packets=%d\n",
11662306a36Sopenharmony_ci			datalen, num_fft_packets);
11762306a36Sopenharmony_ci		if (num_fft_packets < FFT_NUM_SAMPLES) {
11862306a36Sopenharmony_ci			ath_dbg(common, DFS, "not enough packets for chirp\n");
11962306a36Sopenharmony_ci			return false;
12062306a36Sopenharmony_ci		}
12162306a36Sopenharmony_ci		/* HW sometimes adds 2 garbage bytes in front of FFT samples */
12262306a36Sopenharmony_ci		if ((datalen % sizeof(*fft)) == 2) {
12362306a36Sopenharmony_ci			fft = (struct ath9k_dfs_fft_40 *) (data + 2);
12462306a36Sopenharmony_ci			ath_dbg(common, DFS, "fixing datalen by 2\n");
12562306a36Sopenharmony_ci		}
12662306a36Sopenharmony_ci		if (IS_CHAN_HT40MINUS(ah->curchan))
12762306a36Sopenharmony_ci			swap(is_ctl, is_ext);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		for (i = 0; i < FFT_NUM_SAMPLES; i++)
13062306a36Sopenharmony_ci			max_bin[i] = ath9k_get_max_index_ht40(fft + i, is_ctl,
13162306a36Sopenharmony_ci							      is_ext);
13262306a36Sopenharmony_ci	} else {
13362306a36Sopenharmony_ci		struct ath9k_dfs_fft_20 *fft = (struct ath9k_dfs_fft_20 *) data;
13462306a36Sopenharmony_ci		int num_fft_packets = datalen / sizeof(*fft);
13562306a36Sopenharmony_ci		if (num_fft_packets == 0)
13662306a36Sopenharmony_ci			return false;
13762306a36Sopenharmony_ci		ath_dbg(common, DFS, "HT20: datalen=%d, num_fft_packets=%d\n",
13862306a36Sopenharmony_ci			datalen, num_fft_packets);
13962306a36Sopenharmony_ci		if (num_fft_packets < FFT_NUM_SAMPLES) {
14062306a36Sopenharmony_ci			ath_dbg(common, DFS, "not enough packets for chirp\n");
14162306a36Sopenharmony_ci			return false;
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci		/* in ht20, this is a 6-bit signed number => shift it to 0 */
14462306a36Sopenharmony_ci		for (i = 0; i < FFT_NUM_SAMPLES; i++)
14562306a36Sopenharmony_ci			max_bin[i] = fft_max_index(fft[i].lower_bins) ^ 0x20;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci	ath_dbg(common, DFS, "bin_max = [%d, %d, %d, %d]\n",
14862306a36Sopenharmony_ci		max_bin[0], max_bin[1], max_bin[2], max_bin[3]);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* Check for chirp attributes within specs
15162306a36Sopenharmony_ci	 * a) delta of adjacent max_bins is within range
15262306a36Sopenharmony_ci	 * b) delta of adjacent deltas are within tolerance
15362306a36Sopenharmony_ci	 */
15462306a36Sopenharmony_ci	prev_delta = 0;
15562306a36Sopenharmony_ci	for (i = 0; i < NUM_DIFFS; i++) {
15662306a36Sopenharmony_ci		int ddelta = -1;
15762306a36Sopenharmony_ci		int delta = max_bin[i + 1] - max_bin[i];
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		/* ensure gradient is within valid range */
16062306a36Sopenharmony_ci		if (abs(delta) < BIN_DELTA_MIN || abs(delta) > BIN_DELTA_MAX) {
16162306a36Sopenharmony_ci			ath_dbg(common, DFS, "CHIRP: invalid delta %d "
16262306a36Sopenharmony_ci				"in sample %d\n", delta, i);
16362306a36Sopenharmony_ci			return false;
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci		if (i == 0)
16662306a36Sopenharmony_ci			goto done;
16762306a36Sopenharmony_ci		ddelta = delta - prev_delta;
16862306a36Sopenharmony_ci		if (abs(ddelta) > MAX_DIFF) {
16962306a36Sopenharmony_ci			ath_dbg(common, DFS, "CHIRP: ddelta %d too high\n",
17062306a36Sopenharmony_ci				ddelta);
17162306a36Sopenharmony_ci			return false;
17262306a36Sopenharmony_ci		}
17362306a36Sopenharmony_cidone:
17462306a36Sopenharmony_ci		ath_dbg(common, DFS, "CHIRP - %d: delta=%d, ddelta=%d\n",
17562306a36Sopenharmony_ci			i, delta, ddelta);
17662306a36Sopenharmony_ci		prev_delta = delta;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci	return true;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci/**** end: CHIRP **************************************************************/
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/* convert pulse duration to usecs, considering clock mode */
18362306a36Sopenharmony_cistatic u32 dur_to_usecs(struct ath_hw *ah, u32 dur)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	const u32 AR93X_NSECS_PER_DUR = 800;
18662306a36Sopenharmony_ci	const u32 AR93X_NSECS_PER_DUR_FAST = (8000 / 11);
18762306a36Sopenharmony_ci	u32 nsecs;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	if (IS_CHAN_A_FAST_CLOCK(ah, ah->curchan))
19062306a36Sopenharmony_ci		nsecs = dur * AR93X_NSECS_PER_DUR_FAST;
19162306a36Sopenharmony_ci	else
19262306a36Sopenharmony_ci		nsecs = dur * AR93X_NSECS_PER_DUR;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return (nsecs + 500) / 1000;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci#define PRI_CH_RADAR_FOUND 0x01
19862306a36Sopenharmony_ci#define EXT_CH_RADAR_FOUND 0x02
19962306a36Sopenharmony_cistatic bool
20062306a36Sopenharmony_ciath9k_postprocess_radar_event(struct ath_softc *sc,
20162306a36Sopenharmony_ci			      struct ath_radar_data *ard,
20262306a36Sopenharmony_ci			      struct pulse_event *pe)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	u8 rssi;
20562306a36Sopenharmony_ci	u16 dur;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/*
20862306a36Sopenharmony_ci	 * Only the last 2 bits of the BW info are relevant, they indicate
20962306a36Sopenharmony_ci	 * which channel the radar was detected in.
21062306a36Sopenharmony_ci	 */
21162306a36Sopenharmony_ci	ard->pulse_bw_info &= 0x03;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	switch (ard->pulse_bw_info) {
21462306a36Sopenharmony_ci	case PRI_CH_RADAR_FOUND:
21562306a36Sopenharmony_ci		/* radar in ctrl channel */
21662306a36Sopenharmony_ci		dur = ard->pulse_length_pri;
21762306a36Sopenharmony_ci		DFS_STAT_INC(sc, pri_phy_errors);
21862306a36Sopenharmony_ci		/*
21962306a36Sopenharmony_ci		 * cannot use ctrl channel RSSI
22062306a36Sopenharmony_ci		 * if extension channel is stronger
22162306a36Sopenharmony_ci		 */
22262306a36Sopenharmony_ci		rssi = (ard->ext_rssi >= (ard->rssi + 3)) ? 0 : ard->rssi;
22362306a36Sopenharmony_ci		break;
22462306a36Sopenharmony_ci	case EXT_CH_RADAR_FOUND:
22562306a36Sopenharmony_ci		/* radar in extension channel */
22662306a36Sopenharmony_ci		dur = ard->pulse_length_ext;
22762306a36Sopenharmony_ci		DFS_STAT_INC(sc, ext_phy_errors);
22862306a36Sopenharmony_ci		/*
22962306a36Sopenharmony_ci		 * cannot use extension channel RSSI
23062306a36Sopenharmony_ci		 * if control channel is stronger
23162306a36Sopenharmony_ci		 */
23262306a36Sopenharmony_ci		rssi = (ard->rssi >= (ard->ext_rssi + 12)) ? 0 : ard->ext_rssi;
23362306a36Sopenharmony_ci		break;
23462306a36Sopenharmony_ci	case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND):
23562306a36Sopenharmony_ci		/*
23662306a36Sopenharmony_ci		 * Conducted testing, when pulse is on DC, both pri and ext
23762306a36Sopenharmony_ci		 * durations are reported to be same
23862306a36Sopenharmony_ci		 *
23962306a36Sopenharmony_ci		 * Radiated testing, when pulse is on DC, different pri and
24062306a36Sopenharmony_ci		 * ext durations are reported, so take the larger of the two
24162306a36Sopenharmony_ci		 */
24262306a36Sopenharmony_ci		if (ard->pulse_length_ext >= ard->pulse_length_pri)
24362306a36Sopenharmony_ci			dur = ard->pulse_length_ext;
24462306a36Sopenharmony_ci		else
24562306a36Sopenharmony_ci			dur = ard->pulse_length_pri;
24662306a36Sopenharmony_ci		DFS_STAT_INC(sc, dc_phy_errors);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		/* when both are present use stronger one */
24962306a36Sopenharmony_ci		rssi = max(ard->rssi, ard->ext_rssi);
25062306a36Sopenharmony_ci		break;
25162306a36Sopenharmony_ci	default:
25262306a36Sopenharmony_ci		/*
25362306a36Sopenharmony_ci		 * Bogus bandwidth info was received in descriptor,
25462306a36Sopenharmony_ci		 * so ignore this PHY error
25562306a36Sopenharmony_ci		 */
25662306a36Sopenharmony_ci		DFS_STAT_INC(sc, bwinfo_discards);
25762306a36Sopenharmony_ci		return false;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (rssi == 0) {
26162306a36Sopenharmony_ci		DFS_STAT_INC(sc, rssi_discards);
26262306a36Sopenharmony_ci		return false;
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* convert duration to usecs */
26662306a36Sopenharmony_ci	pe->width = dur_to_usecs(sc->sc_ah, dur);
26762306a36Sopenharmony_ci	pe->rssi = rssi;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	DFS_STAT_INC(sc, pulses_detected);
27062306a36Sopenharmony_ci	return true;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void
27462306a36Sopenharmony_ciath9k_dfs_process_radar_pulse(struct ath_softc *sc, struct pulse_event *pe)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	struct dfs_pattern_detector *pd = sc->dfs_detector;
27762306a36Sopenharmony_ci	DFS_STAT_INC(sc, pulses_processed);
27862306a36Sopenharmony_ci	if (pd == NULL)
27962306a36Sopenharmony_ci		return;
28062306a36Sopenharmony_ci	if (!pd->add_pulse(pd, pe, NULL))
28162306a36Sopenharmony_ci		return;
28262306a36Sopenharmony_ci	DFS_STAT_INC(sc, radar_detected);
28362306a36Sopenharmony_ci	ieee80211_radar_detected(sc->hw);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci/*
28762306a36Sopenharmony_ci * DFS: check PHY-error for radar pulse and feed the detector
28862306a36Sopenharmony_ci */
28962306a36Sopenharmony_civoid ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data,
29062306a36Sopenharmony_ci			      struct ath_rx_status *rs, u64 mactime)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	struct ath_radar_data ard;
29362306a36Sopenharmony_ci	u16 datalen;
29462306a36Sopenharmony_ci	char *vdata_end;
29562306a36Sopenharmony_ci	struct pulse_event pe;
29662306a36Sopenharmony_ci	struct ath_hw *ah = sc->sc_ah;
29762306a36Sopenharmony_ci	struct ath_common *common = ath9k_hw_common(ah);
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	DFS_STAT_INC(sc, pulses_total);
30062306a36Sopenharmony_ci	if ((rs->rs_phyerr != ATH9K_PHYERR_RADAR) &&
30162306a36Sopenharmony_ci	    (rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT)) {
30262306a36Sopenharmony_ci		ath_dbg(common, DFS,
30362306a36Sopenharmony_ci			"Error: rs_phyer=0x%x not a radar error\n",
30462306a36Sopenharmony_ci			rs->rs_phyerr);
30562306a36Sopenharmony_ci		DFS_STAT_INC(sc, pulses_no_dfs);
30662306a36Sopenharmony_ci		return;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	datalen = rs->rs_datalen;
31062306a36Sopenharmony_ci	if (datalen == 0) {
31162306a36Sopenharmony_ci		DFS_STAT_INC(sc, datalen_discards);
31262306a36Sopenharmony_ci		return;
31362306a36Sopenharmony_ci	}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	ard.rssi = rs->rs_rssi_ctl[0];
31662306a36Sopenharmony_ci	ard.ext_rssi = rs->rs_rssi_ext[0];
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	/*
31962306a36Sopenharmony_ci	 * hardware stores this as 8 bit signed value.
32062306a36Sopenharmony_ci	 * we will cap it at 0 if it is a negative number
32162306a36Sopenharmony_ci	 */
32262306a36Sopenharmony_ci	if (ard.rssi & 0x80)
32362306a36Sopenharmony_ci		ard.rssi = 0;
32462306a36Sopenharmony_ci	if (ard.ext_rssi & 0x80)
32562306a36Sopenharmony_ci		ard.ext_rssi = 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	vdata_end = data + datalen;
32862306a36Sopenharmony_ci	ard.pulse_bw_info = vdata_end[-1];
32962306a36Sopenharmony_ci	ard.pulse_length_ext = vdata_end[-2];
33062306a36Sopenharmony_ci	ard.pulse_length_pri = vdata_end[-3];
33162306a36Sopenharmony_ci	pe.freq = ah->curchan->channel;
33262306a36Sopenharmony_ci	pe.ts = mactime;
33362306a36Sopenharmony_ci	if (!ath9k_postprocess_radar_event(sc, &ard, &pe))
33462306a36Sopenharmony_ci		return;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (pe.width > MIN_CHIRP_PULSE_WIDTH &&
33762306a36Sopenharmony_ci	    pe.width < MAX_CHIRP_PULSE_WIDTH) {
33862306a36Sopenharmony_ci		bool is_ctl = !!(ard.pulse_bw_info & PRI_CH_RADAR_FOUND);
33962306a36Sopenharmony_ci		bool is_ext = !!(ard.pulse_bw_info & EXT_CH_RADAR_FOUND);
34062306a36Sopenharmony_ci		int clen = datalen - 3;
34162306a36Sopenharmony_ci		pe.chirp = ath9k_check_chirping(sc, data, clen, is_ctl, is_ext);
34262306a36Sopenharmony_ci	} else {
34362306a36Sopenharmony_ci		pe.chirp = false;
34462306a36Sopenharmony_ci	}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	ath_dbg(common, DFS,
34762306a36Sopenharmony_ci		"ath9k_dfs_process_phyerr: type=%d, freq=%d, ts=%llu, "
34862306a36Sopenharmony_ci		"width=%d, rssi=%d, delta_ts=%llu\n",
34962306a36Sopenharmony_ci		ard.pulse_bw_info, pe.freq, pe.ts, pe.width, pe.rssi,
35062306a36Sopenharmony_ci		pe.ts - sc->dfs_prev_pulse_ts);
35162306a36Sopenharmony_ci	sc->dfs_prev_pulse_ts = pe.ts;
35262306a36Sopenharmony_ci	if (ard.pulse_bw_info & PRI_CH_RADAR_FOUND)
35362306a36Sopenharmony_ci		ath9k_dfs_process_radar_pulse(sc, &pe);
35462306a36Sopenharmony_ci	if (IS_CHAN_HT40(ah->curchan) &&
35562306a36Sopenharmony_ci	    ard.pulse_bw_info & EXT_CH_RADAR_FOUND) {
35662306a36Sopenharmony_ci		pe.freq += IS_CHAN_HT40PLUS(ah->curchan) ? 20 : -20;
35762306a36Sopenharmony_ci		ath9k_dfs_process_radar_pulse(sc, &pe);
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci#undef PRI_CH_RADAR_FOUND
36162306a36Sopenharmony_ci#undef EXT_CH_RADAR_FOUND
362