2 * Copyright (c) 2013 Qualcomm Atheros, Inc.
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.
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.
17 #include <linux/relay.h>
20 static s8
fix_rssi_inv_only(u8 rssi_val
)
27 static void ath_debug_send_fft_sample(struct ath_softc
*sc
,
28 struct fft_sample_tlv
*fft_sample_tlv
)
31 if (!sc
->rfs_chan_spec_scan
)
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
);
39 /* returns 1 if this was a spectral frame, even if not handled. */
40 int ath_process_fft(struct ath_softc
*sc
, struct ieee80211_hdr
*hdr
,
41 struct ath_rx_status
*rs
, u64 tsf
)
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
;
51 u16 fft_len
, length
, freq
= ah
->curchan
->chan
->center_freq
;
52 enum nl80211_channel_type chan_type
;
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.
58 if (rs
->rs_phyerr
!= ATH9K_PHYERR_RADAR
&&
59 rs
->rs_phyerr
!= ATH9K_PHYERR_FALSE_RADAR_EXT
&&
60 rs
->rs_phyerr
!= ATH9K_PHYERR_SPECTRAL
)
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.
66 radar_info
= ((struct ath_radar_info
*)&vdata
[len
]) - 1;
67 if (!(radar_info
->pulse_bw_info
& SPECTRAL_SCAN_BITMASK
))
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
;
77 fft_len
= SPECTRAL_HT20_TOTAL_DATA_LEN
;
78 num_bins
= SPECTRAL_HT20_NUM_BINS
;
79 bins
= (u8
*)fft_sample_20
.data
;
82 /* Variation in the data length is possible and will be fixed later */
83 if ((len
> fft_len
+ 2) || (len
< fft_len
- 1))
86 switch (len
- fft_len
) {
88 /* length correct, nothing to do. */
89 memcpy(bins
, vdata
, num_bins
);
92 /* first byte missing, duplicate it. */
93 memcpy(&bins
[1], vdata
, num_bins
- 1);
97 /* MAC added 2 extra bytes at bin 30 and 32, remove them. */
98 memcpy(bins
, vdata
, 30);
100 memcpy(&bins
[31], &vdata
[33], num_bins
- 31);
103 /* MAC added 2 extra bytes AND first byte is missing. */
105 memcpy(&bins
[1], vdata
, 30);
106 bins
[31] = vdata
[31];
107 memcpy(&bins
[32], &vdata
[33], num_bins
- 32);
113 /* DC value (value in the middle) is the blind spot of the spectral
114 * sample and invalid, interpolate it.
116 dc_pos
= num_bins
/ 2;
117 bins
[dc_pos
] = (bins
[dc_pos
+ 1] + bins
[dc_pos
- 1]) / 2;
119 if ((chan_type
== NL80211_CHAN_HT40MINUS
) ||
120 (chan_type
== NL80211_CHAN_HT40PLUS
)) {
121 s8 lower_rssi
, upper_rssi
;
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
;
130 ext_nf
= ath9k_hw_getchan_noise(ah
, ah
->curchan
,
131 caldata
->nfCalHist
[3].privNF
);
133 ext_nf
= ATH_DEFAULT_NOISE_FLOOR
;
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
;
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]);
145 fft_sample_40
.lower_noise
= ah
->noise
;
146 fft_sample_40
.upper_noise
= ext_nf
;
148 lower_rssi
= fix_rssi_inv_only(rs
->rs_rssi_ext
[0]);
149 upper_rssi
= fix_rssi_inv_only(rs
->rs_rssi_ctl
[0]);
151 fft_sample_40
.lower_noise
= ext_nf
;
152 fft_sample_40
.upper_noise
= ah
->noise
;
154 fft_sample_40
.lower_rssi
= lower_rssi
;
155 fft_sample_40
.upper_rssi
= upper_rssi
;
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;
172 fft_sample_40
.tsf
= __cpu_to_be64(tsf
);
174 tlv
= (struct fft_sample_tlv
*)&fft_sample_40
;
176 u8 max_index
, bitmap_w
;
178 struct ath_ht20_mag_info
*mag_info
;
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
);
185 fft_sample_20
.rssi
= fix_rssi_inv_only(rs
->rs_rssi_ctl
[0]);
186 fft_sample_20
.noise
= ah
->noise
;
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;
197 fft_sample_20
.tsf
= __cpu_to_be64(tsf
);
199 tlv
= (struct fft_sample_tlv
*)&fft_sample_20
;
202 ath_debug_send_fft_sample(sc
, tlv
);
207 /*********************/
208 /* spectral_scan_ctl */
209 /*********************/
211 static ssize_t
read_file_spec_scan_ctl(struct file
*file
, char __user
*user_buf
,
212 size_t count
, loff_t
*ppos
)
214 struct ath_softc
*sc
= file
->private_data
;
218 switch (sc
->spectral_mode
) {
219 case SPECTRAL_DISABLED
:
222 case SPECTRAL_BACKGROUND
:
225 case SPECTRAL_CHANSCAN
:
228 case SPECTRAL_MANUAL
:
233 return simple_read_from_buffer(user_buf
, count
, ppos
, mode
, len
);
236 static ssize_t
write_file_spec_scan_ctl(struct file
*file
,
237 const char __user
*user_buf
,
238 size_t count
, loff_t
*ppos
)
240 struct ath_softc
*sc
= file
->private_data
;
241 struct ath_common
*common
= ath9k_hw_common(sc
->sc_ah
);
245 if (config_enabled(CONFIG_ATH9K_TX99
))
248 len
= min(count
, sizeof(buf
) - 1);
249 if (copy_from_user(buf
, user_buf
, len
))
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");
275 static const struct file_operations fops_spec_scan_ctl
= {
276 .read
= read_file_spec_scan_ctl
,
277 .write
= write_file_spec_scan_ctl
,
279 .owner
= THIS_MODULE
,
280 .llseek
= default_llseek
,
283 /*************************/
284 /* spectral_short_repeat */
285 /*************************/
287 static ssize_t
read_file_spectral_short_repeat(struct file
*file
,
288 char __user
*user_buf
,
289 size_t count
, loff_t
*ppos
)
291 struct ath_softc
*sc
= file
->private_data
;
295 len
= sprintf(buf
, "%d\n", sc
->spec_config
.short_repeat
);
296 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
299 static ssize_t
write_file_spectral_short_repeat(struct file
*file
,
300 const char __user
*user_buf
,
301 size_t count
, loff_t
*ppos
)
303 struct ath_softc
*sc
= file
->private_data
;
308 len
= min(count
, sizeof(buf
) - 1);
309 if (copy_from_user(buf
, user_buf
, len
))
313 if (kstrtoul(buf
, 0, &val
))
316 if (val
< 0 || val
> 1)
319 sc
->spec_config
.short_repeat
= val
;
323 static const struct file_operations fops_spectral_short_repeat
= {
324 .read
= read_file_spectral_short_repeat
,
325 .write
= write_file_spectral_short_repeat
,
327 .owner
= THIS_MODULE
,
328 .llseek
= default_llseek
,
335 static ssize_t
read_file_spectral_count(struct file
*file
,
336 char __user
*user_buf
,
337 size_t count
, loff_t
*ppos
)
339 struct ath_softc
*sc
= file
->private_data
;
343 len
= sprintf(buf
, "%d\n", sc
->spec_config
.count
);
344 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
347 static ssize_t
write_file_spectral_count(struct file
*file
,
348 const char __user
*user_buf
,
349 size_t count
, loff_t
*ppos
)
351 struct ath_softc
*sc
= file
->private_data
;
356 len
= min(count
, sizeof(buf
) - 1);
357 if (copy_from_user(buf
, user_buf
, len
))
361 if (kstrtoul(buf
, 0, &val
))
364 if (val
< 0 || val
> 255)
367 sc
->spec_config
.count
= val
;
371 static const struct file_operations fops_spectral_count
= {
372 .read
= read_file_spectral_count
,
373 .write
= write_file_spectral_count
,
375 .owner
= THIS_MODULE
,
376 .llseek
= default_llseek
,
379 /*******************/
380 /* spectral_period */
381 /*******************/
383 static ssize_t
read_file_spectral_period(struct file
*file
,
384 char __user
*user_buf
,
385 size_t count
, loff_t
*ppos
)
387 struct ath_softc
*sc
= file
->private_data
;
391 len
= sprintf(buf
, "%d\n", sc
->spec_config
.period
);
392 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
395 static ssize_t
write_file_spectral_period(struct file
*file
,
396 const char __user
*user_buf
,
397 size_t count
, loff_t
*ppos
)
399 struct ath_softc
*sc
= file
->private_data
;
404 len
= min(count
, sizeof(buf
) - 1);
405 if (copy_from_user(buf
, user_buf
, len
))
409 if (kstrtoul(buf
, 0, &val
))
412 if (val
< 0 || val
> 255)
415 sc
->spec_config
.period
= val
;
419 static const struct file_operations fops_spectral_period
= {
420 .read
= read_file_spectral_period
,
421 .write
= write_file_spectral_period
,
423 .owner
= THIS_MODULE
,
424 .llseek
= default_llseek
,
427 /***********************/
428 /* spectral_fft_period */
429 /***********************/
431 static ssize_t
read_file_spectral_fft_period(struct file
*file
,
432 char __user
*user_buf
,
433 size_t count
, loff_t
*ppos
)
435 struct ath_softc
*sc
= file
->private_data
;
439 len
= sprintf(buf
, "%d\n", sc
->spec_config
.fft_period
);
440 return simple_read_from_buffer(user_buf
, count
, ppos
, buf
, len
);
443 static ssize_t
write_file_spectral_fft_period(struct file
*file
,
444 const char __user
*user_buf
,
445 size_t count
, loff_t
*ppos
)
447 struct ath_softc
*sc
= file
->private_data
;
452 len
= min(count
, sizeof(buf
) - 1);
453 if (copy_from_user(buf
, user_buf
, len
))
457 if (kstrtoul(buf
, 0, &val
))
460 if (val
< 0 || val
> 15)
463 sc
->spec_config
.fft_period
= val
;
467 static const struct file_operations fops_spectral_fft_period
= {
468 .read
= read_file_spectral_fft_period
,
469 .write
= write_file_spectral_fft_period
,
471 .owner
= THIS_MODULE
,
472 .llseek
= default_llseek
,
475 /*******************/
476 /* Relay interface */
477 /*******************/
479 static struct dentry
*create_buf_file_handler(const char *filename
,
480 struct dentry
*parent
,
482 struct rchan_buf
*buf
,
485 struct dentry
*buf_file
;
487 buf_file
= debugfs_create_file(filename
, mode
, parent
, buf
,
488 &relay_file_operations
);
493 static int remove_buf_file_handler(struct dentry
*dentry
)
495 debugfs_remove(dentry
);
500 static struct rchan_callbacks rfs_spec_scan_cb
= {
501 .create_buf_file
= create_buf_file_handler
,
502 .remove_buf_file
= remove_buf_file_handler
,
505 /*********************/
506 /* Debug Init/Deinit */
507 /*********************/
509 void ath9k_spectral_deinit_debug(struct ath_softc
*sc
)
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
;
517 void ath9k_spectral_init_debug(struct ath_softc
*sc
)
519 sc
->rfs_chan_spec_scan
= relay_open("spectral_scan",
520 sc
->debug
.debugfs_phy
,
521 1024, 256, &rfs_spec_scan_cb
,
523 debugfs_create_file("spectral_scan_ctl",
525 sc
->debug
.debugfs_phy
, sc
,
526 &fops_spec_scan_ctl
);
527 debugfs_create_file("spectral_short_repeat",
529 sc
->debug
.debugfs_phy
, sc
,
530 &fops_spectral_short_repeat
);
531 debugfs_create_file("spectral_count",
533 sc
->debug
.debugfs_phy
, sc
,
534 &fops_spectral_count
);
535 debugfs_create_file("spectral_period",
537 sc
->debug
.debugfs_phy
, sc
,
538 &fops_spectral_period
);
539 debugfs_create_file("spectral_fft_period",
541 sc
->debug
.debugfs_phy
, sc
,
542 &fops_spectral_fft_period
);