brintos

brintos / linux-shallow public Read only

0
0
Text · 29.4 KiB · 4b27445 Raw
1085 lines · c
1/*2 * Copyright (c) 2013 Qualcomm Atheros, Inc.3 *4 * Permission to use, copy, modify, and/or distribute this software for any5 * purpose with or without fee is hereby granted, provided that the above6 * copyright notice and this permission notice appear in all copies.7 *8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.15 */16 17#include <linux/relay.h>18#include <linux/random.h>19#include "ath9k.h"20 21static s8 fix_rssi_inv_only(u8 rssi_val)22{23	if (rssi_val == 128)24		rssi_val = 0;25	return (s8) rssi_val;26}27 28static void ath_debug_send_fft_sample(struct ath_spec_scan_priv *spec_priv,29				      struct fft_sample_tlv *fft_sample_tlv)30{31	int length;32	if (!spec_priv->rfs_chan_spec_scan)33		return;34 35	length = __be16_to_cpu(fft_sample_tlv->length) +36		 sizeof(*fft_sample_tlv);37	relay_write(spec_priv->rfs_chan_spec_scan, fft_sample_tlv, length);38}39 40typedef int (ath_cmn_fft_idx_validator) (u8 *sample_end, int bytes_read);41 42static int43ath_cmn_max_idx_verify_ht20_fft(u8 *sample_end, int bytes_read)44{45	struct ath_ht20_mag_info *mag_info;46	u8 *sample;47	u16 max_magnitude;48	u8 max_index;49	u8 max_exp;50 51	/* Sanity check so that we don't read outside the read52	 * buffer53	 */54	if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN - 1)55		return -1;56 57	mag_info = (struct ath_ht20_mag_info *) (sample_end -58				sizeof(struct ath_ht20_mag_info) + 1);59 60	sample = sample_end - SPECTRAL_HT20_SAMPLE_LEN + 1;61 62	max_index = spectral_max_index_ht20(mag_info->all_bins);63	max_magnitude = spectral_max_magnitude(mag_info->all_bins);64 65	max_exp = mag_info->max_exp & 0xf;66 67	/* Don't try to read something outside the read buffer68	 * in case of a missing byte (so bins[0] will be outside69	 * the read buffer)70	 */71	if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN && max_index < 1)72		return -1;73 74	if ((sample[max_index] & 0xf8) != ((max_magnitude >> max_exp) & 0xf8))75		return -1;76	else77		return 0;78}79 80static int81ath_cmn_max_idx_verify_ht20_40_fft(u8 *sample_end, int bytes_read)82{83	struct ath_ht20_40_mag_info *mag_info;84	u8 *sample;85	u16 lower_mag, upper_mag;86	u8 lower_max_index, upper_max_index;87	u8 max_exp;88	int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2;89 90	/* Sanity check so that we don't read outside the read91	 * buffer92	 */93	if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN - 1)94		return -1;95 96	mag_info = (struct ath_ht20_40_mag_info *) (sample_end -97				sizeof(struct ath_ht20_40_mag_info) + 1);98 99	sample = sample_end - SPECTRAL_HT20_40_SAMPLE_LEN + 1;100 101	lower_mag = spectral_max_magnitude(mag_info->lower_bins);102	lower_max_index = spectral_max_index_ht40(mag_info->lower_bins);103 104	upper_mag = spectral_max_magnitude(mag_info->upper_bins);105	upper_max_index = spectral_max_index_ht40(mag_info->upper_bins);106 107	max_exp = mag_info->max_exp & 0xf;108 109	/* Don't try to read something outside the read buffer110	 * in case of a missing byte (so bins[0] will be outside111	 * the read buffer)112	 */113	if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN &&114	   ((upper_max_index < 1) || (lower_max_index < 1)))115		return -1;116 117	if (((sample[upper_max_index + dc_pos] & 0xf8) !=118	     ((upper_mag >> max_exp) & 0xf8)) ||119	    ((sample[lower_max_index] & 0xf8) !=120	     ((lower_mag >> max_exp) & 0xf8)))121		return -1;122	else123		return 0;124}125 126typedef int (ath_cmn_fft_sample_handler) (struct ath_rx_status *rs,127			struct ath_spec_scan_priv *spec_priv,128			u8 *sample_buf, u64 tsf, u16 freq, int chan_type);129 130static int131ath_cmn_process_ht20_fft(struct ath_rx_status *rs,132			struct ath_spec_scan_priv *spec_priv,133			u8 *sample_buf,134			u64 tsf, u16 freq, int chan_type)135{136	struct fft_sample_ht20 fft_sample_20;137	struct ath_common *common = ath9k_hw_common(spec_priv->ah);138	struct ath_hw *ah = spec_priv->ah;139	struct ath_ht20_mag_info *mag_info;140	struct fft_sample_tlv *tlv;141	int i = 0;142	int ret = 0;143	int dc_pos = SPECTRAL_HT20_NUM_BINS / 2;144	u16 magnitude, tmp_mag, length;145	u8 max_index, bitmap_w, max_exp;146 147	length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);148	fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;149	fft_sample_20.tlv.length = __cpu_to_be16(length);150	fft_sample_20.freq = __cpu_to_be16(freq);151	fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);152	fft_sample_20.noise = ah->noise;153 154	mag_info = (struct ath_ht20_mag_info *) (sample_buf +155					SPECTRAL_HT20_NUM_BINS);156 157	magnitude = spectral_max_magnitude(mag_info->all_bins);158	fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);159 160	max_index = spectral_max_index_ht20(mag_info->all_bins);161	fft_sample_20.max_index = max_index;162 163	bitmap_w = spectral_bitmap_weight(mag_info->all_bins);164	fft_sample_20.bitmap_weight = bitmap_w;165 166	max_exp = mag_info->max_exp & 0xf;167	fft_sample_20.max_exp = max_exp;168 169	fft_sample_20.tsf = __cpu_to_be64(tsf);170 171	memcpy(fft_sample_20.data, sample_buf, SPECTRAL_HT20_NUM_BINS);172 173	ath_dbg(common, SPECTRAL_SCAN, "FFT HT20 frame: max mag 0x%X,"174					"max_mag_idx %i\n",175					magnitude >> max_exp,176					max_index);177 178	if ((fft_sample_20.data[max_index] & 0xf8) !=179	    ((magnitude >> max_exp) & 0xf8)) {180		ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n");181		ret = -1;182	}183 184	/* DC value (value in the middle) is the blind spot of the spectral185	 * sample and invalid, interpolate it.186	 */187	fft_sample_20.data[dc_pos] = (fft_sample_20.data[dc_pos + 1] +188					fft_sample_20.data[dc_pos - 1]) / 2;189 190	/* Check if the maximum magnitude is indeed maximum,191	 * also if the maximum value was at dc_pos, calculate192	 * a new one (since value at dc_pos is invalid).193	 */194	if (max_index == dc_pos) {195		tmp_mag = 0;196		for (i = 0; i < dc_pos; i++) {197			if (fft_sample_20.data[i] > tmp_mag) {198				tmp_mag = fft_sample_20.data[i];199				fft_sample_20.max_index = i;200			}201		}202 203		magnitude = tmp_mag << max_exp;204		fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);205 206		ath_dbg(common, SPECTRAL_SCAN,207			"Calculated new lower max 0x%X at %i\n",208			tmp_mag, fft_sample_20.max_index);209	} else210	for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++) {211		if (fft_sample_20.data[i] == (magnitude >> max_exp))212			ath_dbg(common, SPECTRAL_SCAN,213				"Got max: 0x%X at index %i\n",214				fft_sample_20.data[i], i);215 216		if (fft_sample_20.data[i] > (magnitude >> max_exp)) {217			ath_dbg(common, SPECTRAL_SCAN,218				"Got bin %i greater than max: 0x%X\n",219				i, fft_sample_20.data[i]);220			ret = -1;221		}222	}223 224	if (ret < 0)225		return ret;226 227	tlv = (struct fft_sample_tlv *)&fft_sample_20;228 229	ath_debug_send_fft_sample(spec_priv, tlv);230 231	return 0;232}233 234static int235ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs,236			struct ath_spec_scan_priv *spec_priv,237			u8 *sample_buf,238			u64 tsf, u16 freq, int chan_type)239{240	struct fft_sample_ht20_40 fft_sample_40;241	struct ath_common *common = ath9k_hw_common(spec_priv->ah);242	struct ath_hw *ah = spec_priv->ah;243	struct ath9k_hw_cal_data *caldata = ah->caldata;244	struct ath_ht20_40_mag_info *mag_info;245	struct fft_sample_tlv *tlv;246	int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2;247	int i = 0;248	int ret = 0;249	s16 ext_nf;250	u16 lower_mag, upper_mag, tmp_mag, length;251	s8 lower_rssi, upper_rssi;252	u8 lower_max_index, upper_max_index;253	u8 lower_bitmap_w, upper_bitmap_w, max_exp;254 255	if (caldata)256		ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,257				caldata->nfCalHist[3].privNF);258	else259		ext_nf = ATH_DEFAULT_NOISE_FLOOR;260 261	length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);262	fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;263	fft_sample_40.tlv.length = __cpu_to_be16(length);264	fft_sample_40.freq = __cpu_to_be16(freq);265	fft_sample_40.channel_type = chan_type;266 267	if (chan_type == NL80211_CHAN_HT40PLUS) {268		lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);269		upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);270 271		fft_sample_40.lower_noise = ah->noise;272		fft_sample_40.upper_noise = ext_nf;273	} else {274		lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);275		upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);276 277		fft_sample_40.lower_noise = ext_nf;278		fft_sample_40.upper_noise = ah->noise;279	}280 281	fft_sample_40.lower_rssi = lower_rssi;282	fft_sample_40.upper_rssi = upper_rssi;283 284	mag_info = (struct ath_ht20_40_mag_info *) (sample_buf +285					SPECTRAL_HT20_40_NUM_BINS);286 287	lower_mag = spectral_max_magnitude(mag_info->lower_bins);288	fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);289 290	upper_mag = spectral_max_magnitude(mag_info->upper_bins);291	fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);292 293	lower_max_index = spectral_max_index_ht40(mag_info->lower_bins);294	fft_sample_40.lower_max_index = lower_max_index;295 296	upper_max_index = spectral_max_index_ht40(mag_info->upper_bins);297	fft_sample_40.upper_max_index = upper_max_index;298 299	lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);300	fft_sample_40.lower_bitmap_weight = lower_bitmap_w;301 302	upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);303	fft_sample_40.upper_bitmap_weight = upper_bitmap_w;304 305	max_exp = mag_info->max_exp & 0xf;306	fft_sample_40.max_exp = max_exp;307 308	fft_sample_40.tsf = __cpu_to_be64(tsf);309 310	memcpy(fft_sample_40.data, sample_buf, SPECTRAL_HT20_40_NUM_BINS);311 312	ath_dbg(common, SPECTRAL_SCAN, "FFT HT20/40 frame: lower mag 0x%X,"313					"lower_mag_idx %i, upper mag 0x%X,"314					"upper_mag_idx %i\n",315					lower_mag >> max_exp,316					lower_max_index,317					upper_mag >> max_exp,318					upper_max_index);319 320	/* Check if we got the expected magnitude values at321	 * the expected bins322	 */323	if (((fft_sample_40.data[upper_max_index + dc_pos] & 0xf8)324	    != ((upper_mag >> max_exp) & 0xf8)) ||325	   ((fft_sample_40.data[lower_max_index] & 0xf8)326	    != ((lower_mag >> max_exp) & 0xf8))) {327		ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n");328		ret = -1;329	}330 331	/* DC value (value in the middle) is the blind spot of the spectral332	 * sample and invalid, interpolate it.333	 */334	fft_sample_40.data[dc_pos] = (fft_sample_40.data[dc_pos + 1] +335					fft_sample_40.data[dc_pos - 1]) / 2;336 337	/* Check if the maximum magnitudes are indeed maximum,338	 * also if the maximum value was at dc_pos, calculate339	 * a new one (since value at dc_pos is invalid).340	 */341	if (lower_max_index == dc_pos) {342		tmp_mag = 0;343		for (i = 0; i < dc_pos; i++) {344			if (fft_sample_40.data[i] > tmp_mag) {345				tmp_mag = fft_sample_40.data[i];346				fft_sample_40.lower_max_index = i;347			}348		}349 350		lower_mag = tmp_mag << max_exp;351		fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);352 353		ath_dbg(common, SPECTRAL_SCAN,354			"Calculated new lower max 0x%X at %i\n",355			tmp_mag, fft_sample_40.lower_max_index);356	} else357	for (i = 0; i < dc_pos; i++) {358		if (fft_sample_40.data[i] == (lower_mag >> max_exp))359			ath_dbg(common, SPECTRAL_SCAN,360				"Got lower mag: 0x%X at index %i\n",361				fft_sample_40.data[i], i);362 363		if (fft_sample_40.data[i] > (lower_mag >> max_exp)) {364			ath_dbg(common, SPECTRAL_SCAN,365				"Got lower bin %i higher than max: 0x%X\n",366				i, fft_sample_40.data[i]);367			ret = -1;368		}369	}370 371	if (upper_max_index == dc_pos) {372		tmp_mag = 0;373		for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) {374			if (fft_sample_40.data[i] > tmp_mag) {375				tmp_mag = fft_sample_40.data[i];376				fft_sample_40.upper_max_index = i;377			}378		}379		upper_mag = tmp_mag << max_exp;380		fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);381 382		ath_dbg(common, SPECTRAL_SCAN,383			"Calculated new upper max 0x%X at %i\n",384			tmp_mag, fft_sample_40.upper_max_index);385	} else386	for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) {387		if (fft_sample_40.data[i] == (upper_mag >> max_exp))388			ath_dbg(common, SPECTRAL_SCAN,389				"Got upper mag: 0x%X at index %i\n",390				fft_sample_40.data[i], i);391 392		if (fft_sample_40.data[i] > (upper_mag >> max_exp)) {393			ath_dbg(common, SPECTRAL_SCAN,394				"Got upper bin %i higher than max: 0x%X\n",395				i, fft_sample_40.data[i]);396 397			ret = -1;398		}399	}400 401	if (ret < 0)402		return ret;403 404	tlv = (struct fft_sample_tlv *)&fft_sample_40;405 406	ath_debug_send_fft_sample(spec_priv, tlv);407 408	return 0;409}410 411static inline void412ath_cmn_copy_fft_frame(u8 *in, u8 *out, int sample_len, int sample_bytes)413{414	switch (sample_bytes - sample_len) {415	case -1:416		/* First byte missing */417		memcpy(&out[1], in,418		       sample_len - 1);419		break;420	case 0:421		/* Length correct, nothing to do. */422		memcpy(out, in, sample_len);423		break;424	case 1:425		/* MAC added 2 extra bytes AND first byte426		 * is missing.427		 */428		memcpy(&out[1], in, 30);429		out[31] = in[31];430		memcpy(&out[32], &in[33],431		       sample_len - 32);432		break;433	case 2:434		/* MAC added 2 extra bytes at bin 30 and 32,435		 * remove them.436		 */437		memcpy(out, in, 30);438		out[30] = in[31];439		memcpy(&out[31], &in[33],440		       sample_len - 31);441		break;442	default:443		break;444	}445}446 447static int448ath_cmn_is_fft_buf_full(struct ath_spec_scan_priv *spec_priv)449{450	int i = 0;451	int ret = 0;452	struct rchan_buf *buf;453	struct rchan *rc = spec_priv->rfs_chan_spec_scan;454 455	for_each_possible_cpu(i) {456		if ((buf = *per_cpu_ptr(rc->buf, i))) {457			ret += relay_buf_full(buf);458		}459	}460 461	if (ret)462		return 1;463	else464		return 0;465}466 467/* returns 1 if this was a spectral frame, even if not handled. */468int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr,469		    struct ath_rx_status *rs, u64 tsf)470{471	u8 sample_buf[SPECTRAL_SAMPLE_MAX_LEN] = {0};472	struct ath_hw *ah = spec_priv->ah;473	struct ath_common *common = ath9k_hw_common(spec_priv->ah);474	struct ath_softc *sc = common->priv;475	u8 num_bins, *vdata = (u8 *)hdr;476	struct ath_radar_info *radar_info;477	int len = rs->rs_datalen;478	int i;479	int got_slen = 0;480	u8  *sample_start;481	int sample_bytes = 0;482	int ret = 0;483	u16 fft_len, sample_len, freq = ah->curchan->chan->center_freq;484	enum nl80211_channel_type chan_type;485	ath_cmn_fft_idx_validator *fft_idx_validator;486	ath_cmn_fft_sample_handler *fft_handler;487 488	/* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer489	 * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT490	 * yet, but this is supposed to be possible as well.491	 */492	if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&493	    rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&494	    rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)495		return 0;496 497	/* check if spectral scan bit is set. This does not have to be checked498	 * if received through a SPECTRAL phy error, but shouldn't hurt.499	 */500	radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;501	if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))502		return 0;503 504	if (!spec_priv->rfs_chan_spec_scan)505		return 1;506 507	/* Output buffers are full, no need to process anything508	 * since there is no space to put the result anyway509	 */510	ret = ath_cmn_is_fft_buf_full(spec_priv);511	if (ret == 1) {512		ath_dbg(common, SPECTRAL_SCAN, "FFT report ignored, no space "513						"left on output buffers\n");514		return 1;515	}516 517	chan_type = cfg80211_get_chandef_type(&common->hw->conf.chandef);518	if ((chan_type == NL80211_CHAN_HT40MINUS) ||519	    (chan_type == NL80211_CHAN_HT40PLUS)) {520		fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;521		sample_len = SPECTRAL_HT20_40_SAMPLE_LEN;522		num_bins = SPECTRAL_HT20_40_NUM_BINS;523		fft_idx_validator = &ath_cmn_max_idx_verify_ht20_40_fft;524		fft_handler = &ath_cmn_process_ht20_40_fft;525	} else {526		fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;527		sample_len = SPECTRAL_HT20_SAMPLE_LEN;528		num_bins = SPECTRAL_HT20_NUM_BINS;529		fft_idx_validator = ath_cmn_max_idx_verify_ht20_fft;530		fft_handler = &ath_cmn_process_ht20_fft;531	}532 533	ath_dbg(common, SPECTRAL_SCAN, "Got radar dump bw_info: 0x%X,"534					"len: %i fft_len: %i\n",535					radar_info->pulse_bw_info,536					len,537					fft_len);538	sample_start = vdata;539	for (i = 0; i < len - 2; i++) {540		sample_bytes++;541 542		/* Only a single sample received, no need to look543		 * for the sample's end, do the correction based544		 * on the packet's length instead. Note that hw545		 * will always put the radar_info structure on546		 * the end.547		 */548		if (len <= fft_len + 2) {549			sample_bytes = len - sizeof(struct ath_radar_info);550			got_slen = 1;551		}552 553		/* Search for the end of the FFT frame between554		 * sample_len - 1 and sample_len + 2. exp_max is 3555		 * bits long and it's the only value on the last556		 * byte of the frame so since it'll be smaller than557		 * the next byte (the first bin of the next sample)558		 * 90% of the time, we can use it as a separator.559		 */560		if (vdata[i] <= 0x7 && sample_bytes >= sample_len - 1) {561 562			/* Got a frame length within boundaries, there are563			 * four scenarios here:564			 *565			 * a) sample_len -> We got the correct length566			 * b) sample_len + 2 -> 2 bytes added around bin[31]567			 * c) sample_len - 1 -> The first byte is missing568			 * d) sample_len + 1 -> b + c at the same time569			 *570			 * When MAC adds 2 extra bytes, bin[31] and bin[32]571			 * have the same value, so we can use that for further572			 * verification in cases b and d.573			 */574 575			/* Did we go too far ? If so we couldn't determine576			 * this sample's boundaries, discard any further577			 * data578			 */579			if ((sample_bytes > sample_len + 2) ||580			   ((sample_bytes > sample_len) &&581			   (sample_start[31] != sample_start[32])))582				break;583 584			/* See if we got a valid frame by checking the585			 * consistency of mag_info fields. This is to586			 * prevent from "fixing" a correct frame.587			 * Failure is non-fatal, later frames may588			 * be valid.589			 */590			if (!fft_idx_validator(&vdata[i], i)) {591				ath_dbg(common, SPECTRAL_SCAN,592					"Found valid fft frame at %i\n", i);593				got_slen = 1;594			}595 596			/* We expect 1 - 2 more bytes */597			else if ((sample_start[31] == sample_start[32]) &&598				(sample_bytes >= sample_len) &&599				(sample_bytes < sample_len + 2) &&600				(vdata[i + 1] <= 0x7))601				continue;602 603			/* Try to distinguish cases a and c */604			else if ((sample_bytes == sample_len - 1) &&605				(vdata[i + 1] <= 0x7))606				continue;607 608			got_slen = 1;609		}610 611		if (got_slen) {612			ath_dbg(common, SPECTRAL_SCAN, "FFT frame len: %i\n",613				sample_bytes);614 615			/* Only try to fix a frame if it's the only one616			 * on the report, else just skip it.617			 */618			if (sample_bytes != sample_len && len <= fft_len + 2) {619				ath_cmn_copy_fft_frame(sample_start,620						       sample_buf, sample_len,621						       sample_bytes);622 623				ret = fft_handler(rs, spec_priv, sample_buf,624						  tsf, freq, chan_type);625 626				if (ret == 0)627					RX_STAT_INC(sc, rx_spectral_sample_good);628				else629					RX_STAT_INC(sc, rx_spectral_sample_err);630 631				memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN);632 633				/* Mix the received bins to the /dev/random634				 * pool635				 */636				add_device_randomness(sample_buf, num_bins);637			}638 639			/* Process a normal frame */640			if (sample_bytes == sample_len) {641				ret = fft_handler(rs, spec_priv, sample_start,642						  tsf, freq, chan_type);643 644				if (ret == 0)645					RX_STAT_INC(sc, rx_spectral_sample_good);646				else647					RX_STAT_INC(sc, rx_spectral_sample_err);648 649				/* Mix the received bins to the /dev/random650				 * pool651				 */652				add_device_randomness(sample_start, num_bins);653			}654 655			/* Short report processed, break out of the656			 * loop.657			 */658			if (len <= fft_len + 2)659				return 1;660 661			sample_start = &vdata[i + 1];662 663			/* -1 to grab sample_len -1, -2 since664			 * they 'll get increased by one. In case665			 * of failure try to recover by going byte666			 * by byte instead.667			 */668			if (ret == 0) {669				i += num_bins - 2;670				sample_bytes = num_bins - 2;671			}672			got_slen = 0;673		}674	}675 676	i -= num_bins - 2;677	if (len - i != sizeof(struct ath_radar_info))678		ath_dbg(common, SPECTRAL_SCAN, "FFT report truncated"679						"(bytes left: %i)\n",680						len - i);681	return 1;682}683EXPORT_SYMBOL(ath_cmn_process_fft);684 685/*********************/686/* spectral_scan_ctl */687/*********************/688 689static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,690				       size_t count, loff_t *ppos)691{692	struct ath_spec_scan_priv *spec_priv = file->private_data;693	char *mode = "";694	unsigned int len;695 696	switch (spec_priv->spectral_mode) {697	case SPECTRAL_DISABLED:698		mode = "disable";699		break;700	case SPECTRAL_BACKGROUND:701		mode = "background";702		break;703	case SPECTRAL_CHANSCAN:704		mode = "chanscan";705		break;706	case SPECTRAL_MANUAL:707		mode = "manual";708		break;709	}710	len = strlen(mode);711	return simple_read_from_buffer(user_buf, count, ppos, mode, len);712}713 714void ath9k_cmn_spectral_scan_trigger(struct ath_common *common,715				 struct ath_spec_scan_priv *spec_priv)716{717	struct ath_hw *ah = spec_priv->ah;718	u32 rxfilter;719 720	if (IS_ENABLED(CONFIG_ATH9K_TX99))721		return;722 723	if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {724		ath_err(common, "spectrum analyzer not implemented on this hardware\n");725		return;726	}727 728	if (!spec_priv->spec_config.enabled)729		return;730 731	ath_ps_ops(common)->wakeup(common);732	rxfilter = ath9k_hw_getrxfilter(ah);733	ath9k_hw_setrxfilter(ah, rxfilter |734				 ATH9K_RX_FILTER_PHYRADAR |735				 ATH9K_RX_FILTER_PHYERR);736 737	/* TODO: usually this should not be neccesary, but for some reason738	 * (or in some mode?) the trigger must be called after the739	 * configuration, otherwise the register will have its values reset740	 * (on my ar9220 to value 0x01002310)741	 */742	ath9k_cmn_spectral_scan_config(common, spec_priv, spec_priv->spectral_mode);743	ath9k_hw_ops(ah)->spectral_scan_trigger(ah);744	ath_ps_ops(common)->restore(common);745}746EXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger);747 748int ath9k_cmn_spectral_scan_config(struct ath_common *common,749			       struct ath_spec_scan_priv *spec_priv,750			       enum spectral_mode spectral_mode)751{752	struct ath_hw *ah = spec_priv->ah;753 754	if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {755		ath_err(common, "spectrum analyzer not implemented on this hardware\n");756		return -1;757	}758 759	switch (spectral_mode) {760	case SPECTRAL_DISABLED:761		spec_priv->spec_config.enabled = 0;762		break;763	case SPECTRAL_BACKGROUND:764		/* send endless samples.765		 * TODO: is this really useful for "background"?766		 */767		spec_priv->spec_config.endless = 1;768		spec_priv->spec_config.enabled = 1;769		break;770	case SPECTRAL_CHANSCAN:771	case SPECTRAL_MANUAL:772		spec_priv->spec_config.endless = 0;773		spec_priv->spec_config.enabled = 1;774		break;775	default:776		return -1;777	}778 779	ath_ps_ops(common)->wakeup(common);780	ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config);781	ath_ps_ops(common)->restore(common);782 783	spec_priv->spectral_mode = spectral_mode;784 785	return 0;786}787EXPORT_SYMBOL(ath9k_cmn_spectral_scan_config);788 789static ssize_t write_file_spec_scan_ctl(struct file *file,790					const char __user *user_buf,791					size_t count, loff_t *ppos)792{793	struct ath_spec_scan_priv *spec_priv = file->private_data;794	struct ath_common *common = ath9k_hw_common(spec_priv->ah);795	char buf[32];796	ssize_t len;797 798	if (IS_ENABLED(CONFIG_ATH9K_TX99))799		return -EOPNOTSUPP;800 801	len = min(count, sizeof(buf) - 1);802	if (copy_from_user(buf, user_buf, len))803		return -EFAULT;804 805	buf[len] = '\0';806 807	if (strncmp("trigger", buf, 7) == 0) {808		ath9k_cmn_spectral_scan_trigger(common, spec_priv);809	} else if (strncmp("background", buf, 10) == 0) {810		ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_BACKGROUND);811		ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");812	} else if (strncmp("chanscan", buf, 8) == 0) {813		ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_CHANSCAN);814		ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");815	} else if (strncmp("manual", buf, 6) == 0) {816		ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_MANUAL);817		ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");818	} else if (strncmp("disable", buf, 7) == 0) {819		ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_DISABLED);820		ath_dbg(common, CONFIG, "spectral scan: disabled\n");821	} else {822		return -EINVAL;823	}824 825	return count;826}827 828static const struct file_operations fops_spec_scan_ctl = {829	.read = read_file_spec_scan_ctl,830	.write = write_file_spec_scan_ctl,831	.open = simple_open,832	.owner = THIS_MODULE,833	.llseek = default_llseek,834};835 836/*************************/837/* spectral_short_repeat */838/*************************/839 840static ssize_t read_file_spectral_short_repeat(struct file *file,841					       char __user *user_buf,842					       size_t count, loff_t *ppos)843{844	struct ath_spec_scan_priv *spec_priv = file->private_data;845	char buf[32];846	unsigned int len;847 848	len = sprintf(buf, "%d\n", spec_priv->spec_config.short_repeat);849	return simple_read_from_buffer(user_buf, count, ppos, buf, len);850}851 852static ssize_t write_file_spectral_short_repeat(struct file *file,853						const char __user *user_buf,854						size_t count, loff_t *ppos)855{856	struct ath_spec_scan_priv *spec_priv = file->private_data;857	unsigned long val;858	ssize_t ret;859 860	ret = kstrtoul_from_user(user_buf, count, 0, &val);861	if (ret)862		return ret;863 864	if (val > 1)865		return -EINVAL;866 867	spec_priv->spec_config.short_repeat = val;868	return count;869}870 871static const struct file_operations fops_spectral_short_repeat = {872	.read = read_file_spectral_short_repeat,873	.write = write_file_spectral_short_repeat,874	.open = simple_open,875	.owner = THIS_MODULE,876	.llseek = default_llseek,877};878 879/******************/880/* spectral_count */881/******************/882 883static ssize_t read_file_spectral_count(struct file *file,884					char __user *user_buf,885					size_t count, loff_t *ppos)886{887	struct ath_spec_scan_priv *spec_priv = file->private_data;888	char buf[32];889	unsigned int len;890 891	len = sprintf(buf, "%d\n", spec_priv->spec_config.count);892	return simple_read_from_buffer(user_buf, count, ppos, buf, len);893}894 895static ssize_t write_file_spectral_count(struct file *file,896					 const char __user *user_buf,897					 size_t count, loff_t *ppos)898{899	struct ath_spec_scan_priv *spec_priv = file->private_data;900	unsigned long val;901	ssize_t ret;902 903	ret = kstrtoul_from_user(user_buf, count, 0, &val);904	if (ret)905		return ret;906	if (val > 255)907		return -EINVAL;908 909	spec_priv->spec_config.count = val;910	return count;911}912 913static const struct file_operations fops_spectral_count = {914	.read = read_file_spectral_count,915	.write = write_file_spectral_count,916	.open = simple_open,917	.owner = THIS_MODULE,918	.llseek = default_llseek,919};920 921/*******************/922/* spectral_period */923/*******************/924 925static ssize_t read_file_spectral_period(struct file *file,926					 char __user *user_buf,927					 size_t count, loff_t *ppos)928{929	struct ath_spec_scan_priv *spec_priv = file->private_data;930	char buf[32];931	unsigned int len;932 933	len = sprintf(buf, "%d\n", spec_priv->spec_config.period);934	return simple_read_from_buffer(user_buf, count, ppos, buf, len);935}936 937static ssize_t write_file_spectral_period(struct file *file,938					  const char __user *user_buf,939					  size_t count, loff_t *ppos)940{941	struct ath_spec_scan_priv *spec_priv = file->private_data;942	unsigned long val;943	ssize_t ret;944 945	ret = kstrtoul_from_user(user_buf, count, 0, &val);946	if (ret)947		return ret;948 949	if (val > 255)950		return -EINVAL;951 952	spec_priv->spec_config.period = val;953	return count;954}955 956static const struct file_operations fops_spectral_period = {957	.read = read_file_spectral_period,958	.write = write_file_spectral_period,959	.open = simple_open,960	.owner = THIS_MODULE,961	.llseek = default_llseek,962};963 964/***********************/965/* spectral_fft_period */966/***********************/967 968static ssize_t read_file_spectral_fft_period(struct file *file,969					     char __user *user_buf,970					     size_t count, loff_t *ppos)971{972	struct ath_spec_scan_priv *spec_priv = file->private_data;973	char buf[32];974	unsigned int len;975 976	len = sprintf(buf, "%d\n", spec_priv->spec_config.fft_period);977	return simple_read_from_buffer(user_buf, count, ppos, buf, len);978}979 980static ssize_t write_file_spectral_fft_period(struct file *file,981					      const char __user *user_buf,982					      size_t count, loff_t *ppos)983{984	struct ath_spec_scan_priv *spec_priv = file->private_data;985	unsigned long val;986	ssize_t ret;987 988	ret = kstrtoul_from_user(user_buf, count, 0, &val);989	if (ret)990		return ret;991 992	if (val > 15)993		return -EINVAL;994 995	spec_priv->spec_config.fft_period = val;996	return count;997}998 999static const struct file_operations fops_spectral_fft_period = {1000	.read = read_file_spectral_fft_period,1001	.write = write_file_spectral_fft_period,1002	.open = simple_open,1003	.owner = THIS_MODULE,1004	.llseek = default_llseek,1005};1006 1007/*******************/1008/* Relay interface */1009/*******************/1010 1011static struct dentry *create_buf_file_handler(const char *filename,1012					      struct dentry *parent,1013					      umode_t mode,1014					      struct rchan_buf *buf,1015					      int *is_global)1016{1017	struct dentry *buf_file;1018 1019	buf_file = debugfs_create_file(filename, mode, parent, buf,1020				       &relay_file_operations);1021	if (IS_ERR(buf_file))1022		return NULL;1023 1024	*is_global = 1;1025	return buf_file;1026}1027 1028static int remove_buf_file_handler(struct dentry *dentry)1029{1030	debugfs_remove(dentry);1031 1032	return 0;1033}1034 1035static const struct rchan_callbacks rfs_spec_scan_cb = {1036	.create_buf_file = create_buf_file_handler,1037	.remove_buf_file = remove_buf_file_handler,1038};1039 1040/*********************/1041/* Debug Init/Deinit */1042/*********************/1043 1044void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv)1045{1046	if (spec_priv->rfs_chan_spec_scan) {1047		relay_close(spec_priv->rfs_chan_spec_scan);1048		spec_priv->rfs_chan_spec_scan = NULL;1049	}1050}1051EXPORT_SYMBOL(ath9k_cmn_spectral_deinit_debug);1052 1053void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv,1054				   struct dentry *debugfs_phy)1055{1056	spec_priv->rfs_chan_spec_scan = relay_open("spectral_scan",1057					    debugfs_phy,1058					    1024, 256, &rfs_spec_scan_cb,1059					    NULL);1060	if (!spec_priv->rfs_chan_spec_scan)1061		return;1062 1063	debugfs_create_file("spectral_scan_ctl",1064			    0600,1065			    debugfs_phy, spec_priv,1066			    &fops_spec_scan_ctl);1067	debugfs_create_file("spectral_short_repeat",1068			    0600,1069			    debugfs_phy, spec_priv,1070			    &fops_spectral_short_repeat);1071	debugfs_create_file("spectral_count",1072			    0600,1073			    debugfs_phy, spec_priv,1074			    &fops_spectral_count);1075	debugfs_create_file("spectral_period",1076			    0600,1077			    debugfs_phy, spec_priv,1078			    &fops_spectral_period);1079	debugfs_create_file("spectral_fft_period",1080			    0600,1081			    debugfs_phy, spec_priv,1082			    &fops_spectral_fft_period);1083}1084EXPORT_SYMBOL(ath9k_cmn_spectral_init_debug);1085