Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  1/*
  2 * Copyright (c) 2013 Qualcomm Atheros, Inc.
  3 *
  4 * Permission to use, copy, modify, and/or distribute this software for any
  5 * purpose with or without fee is hereby granted, provided that the above
  6 * copyright notice and this permission notice appear in all copies.
  7 *
  8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 15 */
 16
 17#include <linux/relay.h>
 18#include "ath9k.h"
 19
 20static s8 fix_rssi_inv_only(u8 rssi_val)
 21{
 22	if (rssi_val == 128)
 23		rssi_val = 0;
 24	return (s8) rssi_val;
 25}
 26
 27static void ath_debug_send_fft_sample(struct ath_softc *sc,
 28				      struct fft_sample_tlv *fft_sample_tlv)
 29{
 30	int length;
 31	if (!sc->rfs_chan_spec_scan)
 32		return;
 33
 34	length = __be16_to_cpu(fft_sample_tlv->length) +
 35		 sizeof(*fft_sample_tlv);
 36	relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length);
 37}
 38
 39/* returns 1 if this was a spectral frame, even if not handled. */
 40int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
 41		    struct ath_rx_status *rs, u64 tsf)
 42{
 43	struct ath_hw *ah = sc->sc_ah;
 44	u8 num_bins, *bins, *vdata = (u8 *)hdr;
 45	struct fft_sample_ht20 fft_sample_20;
 46	struct fft_sample_ht20_40 fft_sample_40;
 47	struct fft_sample_tlv *tlv;
 48	struct ath_radar_info *radar_info;
 49	int len = rs->rs_datalen;
 50	int dc_pos;
 51	u16 fft_len, length, freq = ah->curchan->chan->center_freq;
 52	enum nl80211_channel_type chan_type;
 53
 54	/* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
 55	 * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
 56	 * yet, but this is supposed to be possible as well.
 57	 */
 58	if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
 59	    rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
 60	    rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)
 61		return 0;
 62
 63	/* check if spectral scan bit is set. This does not have to be checked
 64	 * if received through a SPECTRAL phy error, but shouldn't hurt.
 65	 */
 66	radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
 67	if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
 68		return 0;
 69
 70	chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef);
 71	if ((chan_type == NL80211_CHAN_HT40MINUS) ||
 72	    (chan_type == NL80211_CHAN_HT40PLUS)) {
 73		fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
 74		num_bins = SPECTRAL_HT20_40_NUM_BINS;
 75		bins = (u8 *)fft_sample_40.data;
 76	} else {
 77		fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;
 78		num_bins = SPECTRAL_HT20_NUM_BINS;
 79		bins = (u8 *)fft_sample_20.data;
 80	}
 81
 82	/* Variation in the data length is possible and will be fixed later */
 83	if ((len > fft_len + 2) || (len < fft_len - 1))
 84		return 1;
 85
 86	switch (len - fft_len) {
 87	case 0:
 88		/* length correct, nothing to do. */
 89		memcpy(bins, vdata, num_bins);
 90		break;
 91	case -1:
 92		/* first byte missing, duplicate it. */
 93		memcpy(&bins[1], vdata, num_bins - 1);
 94		bins[0] = vdata[0];
 95		break;
 96	case 2:
 97		/* MAC added 2 extra bytes at bin 30 and 32, remove them. */
 98		memcpy(bins, vdata, 30);
 99		bins[30] = vdata[31];
100		memcpy(&bins[31], &vdata[33], num_bins - 31);
101		break;
102	case 1:
103		/* MAC added 2 extra bytes AND first byte is missing. */
104		bins[0] = vdata[0];
105		memcpy(&bins[1], vdata, 30);
106		bins[31] = vdata[31];
107		memcpy(&bins[32], &vdata[33], num_bins - 32);
108		break;
109	default:
110		return 1;
111	}
112
113	/* DC value (value in the middle) is the blind spot of the spectral
114	 * sample and invalid, interpolate it.
115	 */
116	dc_pos = num_bins / 2;
117	bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2;
118
119	if ((chan_type == NL80211_CHAN_HT40MINUS) ||
120	    (chan_type == NL80211_CHAN_HT40PLUS)) {
121		s8 lower_rssi, upper_rssi;
122		s16 ext_nf;
123		u8 lower_max_index, upper_max_index;
124		u8 lower_bitmap_w, upper_bitmap_w;
125		u16 lower_mag, upper_mag;
126		struct ath9k_hw_cal_data *caldata = ah->caldata;
127		struct ath_ht20_40_mag_info *mag_info;
128
129		if (caldata)
130			ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,
131					caldata->nfCalHist[3].privNF);
132		else
133			ext_nf = ATH_DEFAULT_NOISE_FLOOR;
134
135		length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);
136		fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;
137		fft_sample_40.tlv.length = __cpu_to_be16(length);
138		fft_sample_40.freq = __cpu_to_be16(freq);
139		fft_sample_40.channel_type = chan_type;
140
141		if (chan_type == NL80211_CHAN_HT40PLUS) {
142			lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
143			upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
144
145			fft_sample_40.lower_noise = ah->noise;
146			fft_sample_40.upper_noise = ext_nf;
147		} else {
148			lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
149			upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
150
151			fft_sample_40.lower_noise = ext_nf;
152			fft_sample_40.upper_noise = ah->noise;
153		}
154		fft_sample_40.lower_rssi = lower_rssi;
155		fft_sample_40.upper_rssi = upper_rssi;
156
157		mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1;
158		lower_mag = spectral_max_magnitude(mag_info->lower_bins);
159		upper_mag = spectral_max_magnitude(mag_info->upper_bins);
160		fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
161		fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
162		lower_max_index = spectral_max_index(mag_info->lower_bins);
163		upper_max_index = spectral_max_index(mag_info->upper_bins);
164		fft_sample_40.lower_max_index = lower_max_index;
165		fft_sample_40.upper_max_index = upper_max_index;
166		lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);
167		upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);
168		fft_sample_40.lower_bitmap_weight = lower_bitmap_w;
169		fft_sample_40.upper_bitmap_weight = upper_bitmap_w;
170		fft_sample_40.max_exp = mag_info->max_exp & 0xf;
171
172		fft_sample_40.tsf = __cpu_to_be64(tsf);
173
174		tlv = (struct fft_sample_tlv *)&fft_sample_40;
175	} else {
176		u8 max_index, bitmap_w;
177		u16 magnitude;
178		struct ath_ht20_mag_info *mag_info;
179
180		length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);
181		fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;
182		fft_sample_20.tlv.length = __cpu_to_be16(length);
183		fft_sample_20.freq = __cpu_to_be16(freq);
184
185		fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
186		fft_sample_20.noise = ah->noise;
187
188		mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
189		magnitude = spectral_max_magnitude(mag_info->all_bins);
190		fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
191		max_index = spectral_max_index(mag_info->all_bins);
192		fft_sample_20.max_index = max_index;
193		bitmap_w = spectral_bitmap_weight(mag_info->all_bins);
194		fft_sample_20.bitmap_weight = bitmap_w;
195		fft_sample_20.max_exp = mag_info->max_exp & 0xf;
196
197		fft_sample_20.tsf = __cpu_to_be64(tsf);
198
199		tlv = (struct fft_sample_tlv *)&fft_sample_20;
200	}
201
202	ath_debug_send_fft_sample(sc, tlv);
203
204	return 1;
205}
206
207/*********************/
208/* spectral_scan_ctl */
209/*********************/
210
211static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
212				       size_t count, loff_t *ppos)
213{
214	struct ath_softc *sc = file->private_data;
215	char *mode = "";
216	unsigned int len;
217
218	switch (sc->spectral_mode) {
219	case SPECTRAL_DISABLED:
220		mode = "disable";
221		break;
222	case SPECTRAL_BACKGROUND:
223		mode = "background";
224		break;
225	case SPECTRAL_CHANSCAN:
226		mode = "chanscan";
227		break;
228	case SPECTRAL_MANUAL:
229		mode = "manual";
230		break;
231	}
232	len = strlen(mode);
233	return simple_read_from_buffer(user_buf, count, ppos, mode, len);
234}
235
236static ssize_t write_file_spec_scan_ctl(struct file *file,
237					const char __user *user_buf,
238					size_t count, loff_t *ppos)
239{
240	struct ath_softc *sc = file->private_data;
241	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
242	char buf[32];
243	ssize_t len;
244
245	if (config_enabled(CONFIG_ATH9K_TX99))
246		return -EOPNOTSUPP;
247
248	len = min(count, sizeof(buf) - 1);
249	if (copy_from_user(buf, user_buf, len))
250		return -EFAULT;
251
252	buf[len] = '\0';
253
254	if (strncmp("trigger", buf, 7) == 0) {
255		ath9k_spectral_scan_trigger(sc->hw);
256	} else if (strncmp("background", buf, 9) == 0) {
257		ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND);
258		ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");
259	} else if (strncmp("chanscan", buf, 8) == 0) {
260		ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN);
261		ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");
262	} else if (strncmp("manual", buf, 6) == 0) {
263		ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL);
264		ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");
265	} else if (strncmp("disable", buf, 7) == 0) {
266		ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED);
267		ath_dbg(common, CONFIG, "spectral scan: disabled\n");
268	} else {
269		return -EINVAL;
270	}
271
272	return count;
273}
274
275static const struct file_operations fops_spec_scan_ctl = {
276	.read = read_file_spec_scan_ctl,
277	.write = write_file_spec_scan_ctl,
278	.open = simple_open,
279	.owner = THIS_MODULE,
280	.llseek = default_llseek,
281};
282
283/*************************/
284/* spectral_short_repeat */
285/*************************/
286
287static ssize_t read_file_spectral_short_repeat(struct file *file,
288					       char __user *user_buf,
289					       size_t count, loff_t *ppos)
290{
291	struct ath_softc *sc = file->private_data;
292	char buf[32];
293	unsigned int len;
294
295	len = sprintf(buf, "%d\n", sc->spec_config.short_repeat);
296	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
297}
298
299static ssize_t write_file_spectral_short_repeat(struct file *file,
300						const char __user *user_buf,
301						size_t count, loff_t *ppos)
302{
303	struct ath_softc *sc = file->private_data;
304	unsigned long val;
305	char buf[32];
306	ssize_t len;
307
308	len = min(count, sizeof(buf) - 1);
309	if (copy_from_user(buf, user_buf, len))
310		return -EFAULT;
311
312	buf[len] = '\0';
313	if (kstrtoul(buf, 0, &val))
314		return -EINVAL;
315
316	if (val < 0 || val > 1)
317		return -EINVAL;
318
319	sc->spec_config.short_repeat = val;
320	return count;
321}
322
323static const struct file_operations fops_spectral_short_repeat = {
324	.read = read_file_spectral_short_repeat,
325	.write = write_file_spectral_short_repeat,
326	.open = simple_open,
327	.owner = THIS_MODULE,
328	.llseek = default_llseek,
329};
330
331/******************/
332/* spectral_count */
333/******************/
334
335static ssize_t read_file_spectral_count(struct file *file,
336					char __user *user_buf,
337					size_t count, loff_t *ppos)
338{
339	struct ath_softc *sc = file->private_data;
340	char buf[32];
341	unsigned int len;
342
343	len = sprintf(buf, "%d\n", sc->spec_config.count);
344	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
345}
346
347static ssize_t write_file_spectral_count(struct file *file,
348					 const char __user *user_buf,
349					 size_t count, loff_t *ppos)
350{
351	struct ath_softc *sc = file->private_data;
352	unsigned long val;
353	char buf[32];
354	ssize_t len;
355
356	len = min(count, sizeof(buf) - 1);
357	if (copy_from_user(buf, user_buf, len))
358		return -EFAULT;
359
360	buf[len] = '\0';
361	if (kstrtoul(buf, 0, &val))
362		return -EINVAL;
363
364	if (val < 0 || val > 255)
365		return -EINVAL;
366
367	sc->spec_config.count = val;
368	return count;
369}
370
371static const struct file_operations fops_spectral_count = {
372	.read = read_file_spectral_count,
373	.write = write_file_spectral_count,
374	.open = simple_open,
375	.owner = THIS_MODULE,
376	.llseek = default_llseek,
377};
378
379/*******************/
380/* spectral_period */
381/*******************/
382
383static ssize_t read_file_spectral_period(struct file *file,
384					 char __user *user_buf,
385					 size_t count, loff_t *ppos)
386{
387	struct ath_softc *sc = file->private_data;
388	char buf[32];
389	unsigned int len;
390
391	len = sprintf(buf, "%d\n", sc->spec_config.period);
392	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
393}
394
395static ssize_t write_file_spectral_period(struct file *file,
396					  const char __user *user_buf,
397					  size_t count, loff_t *ppos)
398{
399	struct ath_softc *sc = file->private_data;
400	unsigned long val;
401	char buf[32];
402	ssize_t len;
403
404	len = min(count, sizeof(buf) - 1);
405	if (copy_from_user(buf, user_buf, len))
406		return -EFAULT;
407
408	buf[len] = '\0';
409	if (kstrtoul(buf, 0, &val))
410		return -EINVAL;
411
412	if (val < 0 || val > 255)
413		return -EINVAL;
414
415	sc->spec_config.period = val;
416	return count;
417}
418
419static const struct file_operations fops_spectral_period = {
420	.read = read_file_spectral_period,
421	.write = write_file_spectral_period,
422	.open = simple_open,
423	.owner = THIS_MODULE,
424	.llseek = default_llseek,
425};
426
427/***********************/
428/* spectral_fft_period */
429/***********************/
430
431static ssize_t read_file_spectral_fft_period(struct file *file,
432					     char __user *user_buf,
433					     size_t count, loff_t *ppos)
434{
435	struct ath_softc *sc = file->private_data;
436	char buf[32];
437	unsigned int len;
438
439	len = sprintf(buf, "%d\n", sc->spec_config.fft_period);
440	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
441}
442
443static ssize_t write_file_spectral_fft_period(struct file *file,
444					      const char __user *user_buf,
445					      size_t count, loff_t *ppos)
446{
447	struct ath_softc *sc = file->private_data;
448	unsigned long val;
449	char buf[32];
450	ssize_t len;
451
452	len = min(count, sizeof(buf) - 1);
453	if (copy_from_user(buf, user_buf, len))
454		return -EFAULT;
455
456	buf[len] = '\0';
457	if (kstrtoul(buf, 0, &val))
458		return -EINVAL;
459
460	if (val < 0 || val > 15)
461		return -EINVAL;
462
463	sc->spec_config.fft_period = val;
464	return count;
465}
466
467static const struct file_operations fops_spectral_fft_period = {
468	.read = read_file_spectral_fft_period,
469	.write = write_file_spectral_fft_period,
470	.open = simple_open,
471	.owner = THIS_MODULE,
472	.llseek = default_llseek,
473};
474
475/*******************/
476/* Relay interface */
477/*******************/
478
479static struct dentry *create_buf_file_handler(const char *filename,
480					      struct dentry *parent,
481					      umode_t mode,
482					      struct rchan_buf *buf,
483					      int *is_global)
484{
485	struct dentry *buf_file;
486
487	buf_file = debugfs_create_file(filename, mode, parent, buf,
488				       &relay_file_operations);
489	*is_global = 1;
490	return buf_file;
491}
492
493static int remove_buf_file_handler(struct dentry *dentry)
494{
495	debugfs_remove(dentry);
496
497	return 0;
498}
499
500static struct rchan_callbacks rfs_spec_scan_cb = {
501	.create_buf_file = create_buf_file_handler,
502	.remove_buf_file = remove_buf_file_handler,
503};
504
505/*********************/
506/* Debug Init/Deinit */
507/*********************/
508
509void ath9k_spectral_deinit_debug(struct ath_softc *sc)
510{
511	if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) {
512		relay_close(sc->rfs_chan_spec_scan);
513		sc->rfs_chan_spec_scan = NULL;
514	}
515}
516
517void ath9k_spectral_init_debug(struct ath_softc *sc)
518{
519	sc->rfs_chan_spec_scan = relay_open("spectral_scan",
520					    sc->debug.debugfs_phy,
521					    1024, 256, &rfs_spec_scan_cb,
522					    NULL);
523	debugfs_create_file("spectral_scan_ctl",
524			    S_IRUSR | S_IWUSR,
525			    sc->debug.debugfs_phy, sc,
526			    &fops_spec_scan_ctl);
527	debugfs_create_file("spectral_short_repeat",
528			    S_IRUSR | S_IWUSR,
529			    sc->debug.debugfs_phy, sc,
530			    &fops_spectral_short_repeat);
531	debugfs_create_file("spectral_count",
532			    S_IRUSR | S_IWUSR,
533			    sc->debug.debugfs_phy, sc,
534			    &fops_spectral_count);
535	debugfs_create_file("spectral_period",
536			    S_IRUSR | S_IWUSR,
537			    sc->debug.debugfs_phy, sc,
538			    &fops_spectral_period);
539	debugfs_create_file("spectral_fft_period",
540			    S_IRUSR | S_IWUSR,
541			    sc->debug.debugfs_phy, sc,
542			    &fops_spectral_fft_period);
543}