1 | // SPDX-License-Identifier: ISC |
2 | /* |
3 | * Copyright (c) 2013-2017 Qualcomm Atheros, Inc. |
4 | */ |
5 | |
6 | #include <linux/relay.h> |
7 | #include "core.h" |
8 | #include "debug.h" |
9 | #include "wmi-ops.h" |
10 | |
11 | static void send_fft_sample(struct ath10k *ar, |
12 | const struct fft_sample_tlv *fft_sample_tlv) |
13 | { |
14 | int length; |
15 | |
16 | if (!ar->spectral.rfs_chan_spec_scan) |
17 | return; |
18 | |
19 | length = __be16_to_cpu(fft_sample_tlv->length) + |
20 | sizeof(*fft_sample_tlv); |
21 | relay_write(chan: ar->spectral.rfs_chan_spec_scan, data: fft_sample_tlv, length); |
22 | } |
23 | |
24 | static uint8_t get_max_exp(s8 max_index, u16 max_magnitude, size_t bin_len, |
25 | u8 *data) |
26 | { |
27 | int dc_pos; |
28 | u8 max_exp; |
29 | |
30 | dc_pos = bin_len / 2; |
31 | |
32 | /* peak index outside of bins */ |
33 | if (dc_pos < max_index || -dc_pos >= max_index) |
34 | return 0; |
35 | |
36 | for (max_exp = 0; max_exp < 8; max_exp++) { |
37 | if (data[dc_pos + max_index] == (max_magnitude >> max_exp)) |
38 | break; |
39 | } |
40 | |
41 | /* max_exp not found */ |
42 | if (data[dc_pos + max_index] != (max_magnitude >> max_exp)) |
43 | return 0; |
44 | |
45 | return max_exp; |
46 | } |
47 | |
48 | static inline size_t ath10k_spectral_fix_bin_size(struct ath10k *ar, |
49 | size_t bin_len) |
50 | { |
51 | /* some chipsets reports bin size as 2^n bytes + 'm' bytes in |
52 | * report mode 2. First 2^n bytes carries inband tones and last |
53 | * 'm' bytes carries band edge detection data mainly used in |
54 | * radar detection purpose. Strip last 'm' bytes to make bin size |
55 | * as a valid one. 'm' can take possible values of 4, 12. |
56 | */ |
57 | if (!is_power_of_2(n: bin_len)) |
58 | bin_len -= ar->hw_params.spectral_bin_discard; |
59 | |
60 | return bin_len; |
61 | } |
62 | |
63 | int ath10k_spectral_process_fft(struct ath10k *ar, |
64 | struct wmi_phyerr_ev_arg *phyerr, |
65 | const struct phyerr_fft_report *fftr, |
66 | size_t bin_len, u64 tsf) |
67 | { |
68 | struct fft_sample_ath10k *fft_sample; |
69 | u8 buf[sizeof(*fft_sample) + SPECTRAL_ATH10K_MAX_NUM_BINS]; |
70 | u16 freq1, freq2, total_gain_db, base_pwr_db, length, peak_mag; |
71 | u32 reg0, reg1; |
72 | u8 chain_idx, *bins; |
73 | int dc_pos; |
74 | |
75 | fft_sample = (struct fft_sample_ath10k *)&buf; |
76 | |
77 | bin_len = ath10k_spectral_fix_bin_size(ar, bin_len); |
78 | |
79 | if (bin_len < 64 || bin_len > SPECTRAL_ATH10K_MAX_NUM_BINS) |
80 | return -EINVAL; |
81 | |
82 | reg0 = __le32_to_cpu(fftr->reg0); |
83 | reg1 = __le32_to_cpu(fftr->reg1); |
84 | |
85 | length = sizeof(*fft_sample) - sizeof(struct fft_sample_tlv) + bin_len; |
86 | fft_sample->tlv.type = ATH_FFT_SAMPLE_ATH10K; |
87 | fft_sample->tlv.length = __cpu_to_be16(length); |
88 | |
89 | /* TODO: there might be a reason why the hardware reports 20/40/80 MHz, |
90 | * but the results/plots suggest that its actually 22/44/88 MHz. |
91 | */ |
92 | switch (phyerr->chan_width_mhz) { |
93 | case 20: |
94 | fft_sample->chan_width_mhz = 22; |
95 | break; |
96 | case 40: |
97 | fft_sample->chan_width_mhz = 44; |
98 | break; |
99 | case 80: |
100 | /* TODO: As experiments with an analogue sender and various |
101 | * configurations (fft-sizes of 64/128/256 and 20/40/80 Mhz) |
102 | * show, the particular configuration of 80 MHz/64 bins does |
103 | * not match with the other samples at all. Until the reason |
104 | * for that is found, don't report these samples. |
105 | */ |
106 | if (bin_len == 64) |
107 | return -EINVAL; |
108 | fft_sample->chan_width_mhz = 88; |
109 | break; |
110 | default: |
111 | fft_sample->chan_width_mhz = phyerr->chan_width_mhz; |
112 | } |
113 | |
114 | fft_sample->relpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_RELPWR_DB); |
115 | fft_sample->avgpwr_db = MS(reg1, SEARCH_FFT_REPORT_REG1_AVGPWR_DB); |
116 | |
117 | peak_mag = MS(reg1, SEARCH_FFT_REPORT_REG1_PEAK_MAG); |
118 | fft_sample->max_magnitude = __cpu_to_be16(peak_mag); |
119 | fft_sample->max_index = MS(reg0, SEARCH_FFT_REPORT_REG0_PEAK_SIDX); |
120 | fft_sample->rssi = phyerr->rssi_combined; |
121 | |
122 | total_gain_db = MS(reg0, SEARCH_FFT_REPORT_REG0_TOTAL_GAIN_DB); |
123 | base_pwr_db = MS(reg0, SEARCH_FFT_REPORT_REG0_BASE_PWR_DB); |
124 | fft_sample->total_gain_db = __cpu_to_be16(total_gain_db); |
125 | fft_sample->base_pwr_db = __cpu_to_be16(base_pwr_db); |
126 | |
127 | freq1 = phyerr->freq1; |
128 | freq2 = phyerr->freq2; |
129 | fft_sample->freq1 = __cpu_to_be16(freq1); |
130 | fft_sample->freq2 = __cpu_to_be16(freq2); |
131 | |
132 | chain_idx = MS(reg0, SEARCH_FFT_REPORT_REG0_FFT_CHN_IDX); |
133 | |
134 | fft_sample->noise = __cpu_to_be16(phyerr->nf_chains[chain_idx]); |
135 | |
136 | bins = (u8 *)fftr; |
137 | bins += sizeof(*fftr) + ar->hw_params.spectral_bin_offset; |
138 | |
139 | fft_sample->tsf = __cpu_to_be64(tsf); |
140 | |
141 | /* max_exp has been directly reported by previous hardware (ath9k), |
142 | * maybe its possible to get it by other means? |
143 | */ |
144 | fft_sample->max_exp = get_max_exp(max_index: fft_sample->max_index, max_magnitude: peak_mag, |
145 | bin_len, data: bins); |
146 | |
147 | memcpy(fft_sample->data, bins, bin_len); |
148 | |
149 | /* DC value (value in the middle) is the blind spot of the spectral |
150 | * sample and invalid, interpolate it. |
151 | */ |
152 | dc_pos = bin_len / 2; |
153 | fft_sample->data[dc_pos] = (fft_sample->data[dc_pos + 1] + |
154 | fft_sample->data[dc_pos - 1]) / 2; |
155 | |
156 | send_fft_sample(ar, fft_sample_tlv: &fft_sample->tlv); |
157 | |
158 | return 0; |
159 | } |
160 | |
161 | static struct ath10k_vif *ath10k_get_spectral_vdev(struct ath10k *ar) |
162 | { |
163 | struct ath10k_vif *arvif; |
164 | |
165 | lockdep_assert_held(&ar->conf_mutex); |
166 | |
167 | if (list_empty(head: &ar->arvifs)) |
168 | return NULL; |
169 | |
170 | /* if there already is a vif doing spectral, return that. */ |
171 | list_for_each_entry(arvif, &ar->arvifs, list) |
172 | if (arvif->spectral_enabled) |
173 | return arvif; |
174 | |
175 | /* otherwise, return the first vif. */ |
176 | return list_first_entry(&ar->arvifs, typeof(*arvif), list); |
177 | } |
178 | |
179 | static int ath10k_spectral_scan_trigger(struct ath10k *ar) |
180 | { |
181 | struct ath10k_vif *arvif; |
182 | int res; |
183 | int vdev_id; |
184 | |
185 | lockdep_assert_held(&ar->conf_mutex); |
186 | |
187 | arvif = ath10k_get_spectral_vdev(ar); |
188 | if (!arvif) |
189 | return -ENODEV; |
190 | vdev_id = arvif->vdev_id; |
191 | |
192 | if (ar->spectral.mode == SPECTRAL_DISABLED) |
193 | return 0; |
194 | |
195 | res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id, |
196 | WMI_SPECTRAL_TRIGGER_CMD_CLEAR, |
197 | WMI_SPECTRAL_ENABLE_CMD_ENABLE); |
198 | if (res < 0) |
199 | return res; |
200 | |
201 | res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id, |
202 | WMI_SPECTRAL_TRIGGER_CMD_TRIGGER, |
203 | WMI_SPECTRAL_ENABLE_CMD_ENABLE); |
204 | if (res < 0) |
205 | return res; |
206 | |
207 | return 0; |
208 | } |
209 | |
210 | static int ath10k_spectral_scan_config(struct ath10k *ar, |
211 | enum ath10k_spectral_mode mode) |
212 | { |
213 | struct wmi_vdev_spectral_conf_arg arg; |
214 | struct ath10k_vif *arvif; |
215 | int vdev_id, count, res = 0; |
216 | |
217 | lockdep_assert_held(&ar->conf_mutex); |
218 | |
219 | arvif = ath10k_get_spectral_vdev(ar); |
220 | if (!arvif) |
221 | return -ENODEV; |
222 | |
223 | vdev_id = arvif->vdev_id; |
224 | |
225 | arvif->spectral_enabled = (mode != SPECTRAL_DISABLED); |
226 | ar->spectral.mode = mode; |
227 | |
228 | res = ath10k_wmi_vdev_spectral_enable(ar, vdev_id, |
229 | WMI_SPECTRAL_TRIGGER_CMD_CLEAR, |
230 | WMI_SPECTRAL_ENABLE_CMD_DISABLE); |
231 | if (res < 0) { |
232 | ath10k_warn(ar, fmt: "failed to enable spectral scan: %d\n" , res); |
233 | return res; |
234 | } |
235 | |
236 | if (mode == SPECTRAL_DISABLED) |
237 | return 0; |
238 | |
239 | if (mode == SPECTRAL_BACKGROUND) |
240 | count = WMI_SPECTRAL_COUNT_DEFAULT; |
241 | else |
242 | count = max_t(u8, 1, ar->spectral.config.count); |
243 | |
244 | arg.vdev_id = vdev_id; |
245 | arg.scan_count = count; |
246 | arg.scan_period = WMI_SPECTRAL_PERIOD_DEFAULT; |
247 | arg.scan_priority = WMI_SPECTRAL_PRIORITY_DEFAULT; |
248 | arg.scan_fft_size = ar->spectral.config.fft_size; |
249 | arg.scan_gc_ena = WMI_SPECTRAL_GC_ENA_DEFAULT; |
250 | arg.scan_restart_ena = WMI_SPECTRAL_RESTART_ENA_DEFAULT; |
251 | arg.scan_noise_floor_ref = WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT; |
252 | arg.scan_init_delay = WMI_SPECTRAL_INIT_DELAY_DEFAULT; |
253 | arg.scan_nb_tone_thr = WMI_SPECTRAL_NB_TONE_THR_DEFAULT; |
254 | arg.scan_str_bin_thr = WMI_SPECTRAL_STR_BIN_THR_DEFAULT; |
255 | arg.scan_wb_rpt_mode = WMI_SPECTRAL_WB_RPT_MODE_DEFAULT; |
256 | arg.scan_rssi_rpt_mode = WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT; |
257 | arg.scan_rssi_thr = WMI_SPECTRAL_RSSI_THR_DEFAULT; |
258 | arg.scan_pwr_format = WMI_SPECTRAL_PWR_FORMAT_DEFAULT; |
259 | arg.scan_rpt_mode = WMI_SPECTRAL_RPT_MODE_DEFAULT; |
260 | arg.scan_bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT; |
261 | arg.scan_dbm_adj = WMI_SPECTRAL_DBM_ADJ_DEFAULT; |
262 | arg.scan_chn_mask = WMI_SPECTRAL_CHN_MASK_DEFAULT; |
263 | |
264 | res = ath10k_wmi_vdev_spectral_conf(ar, arg: &arg); |
265 | if (res < 0) { |
266 | ath10k_warn(ar, fmt: "failed to configure spectral scan: %d\n" , res); |
267 | return res; |
268 | } |
269 | |
270 | return 0; |
271 | } |
272 | |
273 | static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, |
274 | size_t count, loff_t *ppos) |
275 | { |
276 | struct ath10k *ar = file->private_data; |
277 | char *mode = "" ; |
278 | size_t len; |
279 | enum ath10k_spectral_mode spectral_mode; |
280 | |
281 | mutex_lock(&ar->conf_mutex); |
282 | spectral_mode = ar->spectral.mode; |
283 | mutex_unlock(lock: &ar->conf_mutex); |
284 | |
285 | switch (spectral_mode) { |
286 | case SPECTRAL_DISABLED: |
287 | mode = "disable" ; |
288 | break; |
289 | case SPECTRAL_BACKGROUND: |
290 | mode = "background" ; |
291 | break; |
292 | case SPECTRAL_MANUAL: |
293 | mode = "manual" ; |
294 | break; |
295 | } |
296 | |
297 | len = strlen(mode); |
298 | return simple_read_from_buffer(to: user_buf, count, ppos, from: mode, available: len); |
299 | } |
300 | |
301 | static ssize_t write_file_spec_scan_ctl(struct file *file, |
302 | const char __user *user_buf, |
303 | size_t count, loff_t *ppos) |
304 | { |
305 | struct ath10k *ar = file->private_data; |
306 | char buf[32]; |
307 | ssize_t len; |
308 | int res; |
309 | |
310 | len = min(count, sizeof(buf) - 1); |
311 | if (copy_from_user(to: buf, from: user_buf, n: len)) |
312 | return -EFAULT; |
313 | |
314 | buf[len] = '\0'; |
315 | |
316 | mutex_lock(&ar->conf_mutex); |
317 | |
318 | if (strncmp("trigger" , buf, 7) == 0) { |
319 | if (ar->spectral.mode == SPECTRAL_MANUAL || |
320 | ar->spectral.mode == SPECTRAL_BACKGROUND) { |
321 | /* reset the configuration to adopt possibly changed |
322 | * debugfs parameters |
323 | */ |
324 | res = ath10k_spectral_scan_config(ar, |
325 | mode: ar->spectral.mode); |
326 | if (res < 0) { |
327 | ath10k_warn(ar, fmt: "failed to reconfigure spectral scan: %d\n" , |
328 | res); |
329 | } |
330 | res = ath10k_spectral_scan_trigger(ar); |
331 | if (res < 0) { |
332 | ath10k_warn(ar, fmt: "failed to trigger spectral scan: %d\n" , |
333 | res); |
334 | } |
335 | } else { |
336 | res = -EINVAL; |
337 | } |
338 | } else if (strncmp("background" , buf, 10) == 0) { |
339 | res = ath10k_spectral_scan_config(ar, mode: SPECTRAL_BACKGROUND); |
340 | } else if (strncmp("manual" , buf, 6) == 0) { |
341 | res = ath10k_spectral_scan_config(ar, mode: SPECTRAL_MANUAL); |
342 | } else if (strncmp("disable" , buf, 7) == 0) { |
343 | res = ath10k_spectral_scan_config(ar, mode: SPECTRAL_DISABLED); |
344 | } else { |
345 | res = -EINVAL; |
346 | } |
347 | |
348 | mutex_unlock(lock: &ar->conf_mutex); |
349 | |
350 | if (res < 0) |
351 | return res; |
352 | |
353 | return count; |
354 | } |
355 | |
356 | static const struct file_operations fops_spec_scan_ctl = { |
357 | .read = read_file_spec_scan_ctl, |
358 | .write = write_file_spec_scan_ctl, |
359 | .open = simple_open, |
360 | .owner = THIS_MODULE, |
361 | .llseek = default_llseek, |
362 | }; |
363 | |
364 | static ssize_t read_file_spectral_count(struct file *file, |
365 | char __user *user_buf, |
366 | size_t count, loff_t *ppos) |
367 | { |
368 | struct ath10k *ar = file->private_data; |
369 | char buf[32]; |
370 | size_t len; |
371 | u8 spectral_count; |
372 | |
373 | mutex_lock(&ar->conf_mutex); |
374 | spectral_count = ar->spectral.config.count; |
375 | mutex_unlock(lock: &ar->conf_mutex); |
376 | |
377 | len = sprintf(buf, fmt: "%d\n" , spectral_count); |
378 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
379 | } |
380 | |
381 | static ssize_t write_file_spectral_count(struct file *file, |
382 | const char __user *user_buf, |
383 | size_t count, loff_t *ppos) |
384 | { |
385 | struct ath10k *ar = file->private_data; |
386 | unsigned long val; |
387 | ssize_t ret; |
388 | |
389 | ret = kstrtoul_from_user(s: user_buf, count, base: 0, res: &val); |
390 | if (ret) |
391 | return ret; |
392 | |
393 | if (val > 255) |
394 | return -EINVAL; |
395 | |
396 | mutex_lock(&ar->conf_mutex); |
397 | ar->spectral.config.count = val; |
398 | mutex_unlock(lock: &ar->conf_mutex); |
399 | |
400 | return count; |
401 | } |
402 | |
403 | static const struct file_operations fops_spectral_count = { |
404 | .read = read_file_spectral_count, |
405 | .write = write_file_spectral_count, |
406 | .open = simple_open, |
407 | .owner = THIS_MODULE, |
408 | .llseek = default_llseek, |
409 | }; |
410 | |
411 | static ssize_t read_file_spectral_bins(struct file *file, |
412 | char __user *user_buf, |
413 | size_t count, loff_t *ppos) |
414 | { |
415 | struct ath10k *ar = file->private_data; |
416 | char buf[32]; |
417 | unsigned int bins, fft_size, bin_scale; |
418 | size_t len; |
419 | |
420 | mutex_lock(&ar->conf_mutex); |
421 | |
422 | fft_size = ar->spectral.config.fft_size; |
423 | bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT; |
424 | bins = 1 << (fft_size - bin_scale); |
425 | |
426 | mutex_unlock(lock: &ar->conf_mutex); |
427 | |
428 | len = sprintf(buf, fmt: "%d\n" , bins); |
429 | return simple_read_from_buffer(to: user_buf, count, ppos, from: buf, available: len); |
430 | } |
431 | |
432 | static ssize_t write_file_spectral_bins(struct file *file, |
433 | const char __user *user_buf, |
434 | size_t count, loff_t *ppos) |
435 | { |
436 | struct ath10k *ar = file->private_data; |
437 | unsigned long val; |
438 | ssize_t ret; |
439 | |
440 | ret = kstrtoul_from_user(s: user_buf, count, base: 0, res: &val); |
441 | if (ret) |
442 | return ret; |
443 | |
444 | if (val < 64 || val > SPECTRAL_ATH10K_MAX_NUM_BINS) |
445 | return -EINVAL; |
446 | |
447 | if (!is_power_of_2(n: val)) |
448 | return -EINVAL; |
449 | |
450 | mutex_lock(&ar->conf_mutex); |
451 | ar->spectral.config.fft_size = ilog2(val); |
452 | ar->spectral.config.fft_size += WMI_SPECTRAL_BIN_SCALE_DEFAULT; |
453 | mutex_unlock(lock: &ar->conf_mutex); |
454 | |
455 | return count; |
456 | } |
457 | |
458 | static const struct file_operations fops_spectral_bins = { |
459 | .read = read_file_spectral_bins, |
460 | .write = write_file_spectral_bins, |
461 | .open = simple_open, |
462 | .owner = THIS_MODULE, |
463 | .llseek = default_llseek, |
464 | }; |
465 | |
466 | static struct dentry *create_buf_file_handler(const char *filename, |
467 | struct dentry *parent, |
468 | umode_t mode, |
469 | struct rchan_buf *buf, |
470 | int *is_global) |
471 | { |
472 | struct dentry *buf_file; |
473 | |
474 | buf_file = debugfs_create_file(name: filename, mode, parent, data: buf, |
475 | fops: &relay_file_operations); |
476 | if (IS_ERR(ptr: buf_file)) |
477 | return NULL; |
478 | |
479 | *is_global = 1; |
480 | return buf_file; |
481 | } |
482 | |
483 | static int remove_buf_file_handler(struct dentry *dentry) |
484 | { |
485 | debugfs_remove(dentry); |
486 | |
487 | return 0; |
488 | } |
489 | |
490 | static const struct rchan_callbacks rfs_spec_scan_cb = { |
491 | .create_buf_file = create_buf_file_handler, |
492 | .remove_buf_file = remove_buf_file_handler, |
493 | }; |
494 | |
495 | int ath10k_spectral_start(struct ath10k *ar) |
496 | { |
497 | struct ath10k_vif *arvif; |
498 | |
499 | lockdep_assert_held(&ar->conf_mutex); |
500 | |
501 | list_for_each_entry(arvif, &ar->arvifs, list) |
502 | arvif->spectral_enabled = 0; |
503 | |
504 | ar->spectral.mode = SPECTRAL_DISABLED; |
505 | ar->spectral.config.count = WMI_SPECTRAL_COUNT_DEFAULT; |
506 | ar->spectral.config.fft_size = WMI_SPECTRAL_FFT_SIZE_DEFAULT; |
507 | |
508 | return 0; |
509 | } |
510 | |
511 | int ath10k_spectral_vif_stop(struct ath10k_vif *arvif) |
512 | { |
513 | if (!arvif->spectral_enabled) |
514 | return 0; |
515 | |
516 | return ath10k_spectral_scan_config(ar: arvif->ar, mode: SPECTRAL_DISABLED); |
517 | } |
518 | |
519 | int ath10k_spectral_create(struct ath10k *ar) |
520 | { |
521 | /* The buffer size covers whole channels in dual bands up to 128 bins. |
522 | * Scan with bigger than 128 bins needs to be run on single band each. |
523 | */ |
524 | ar->spectral.rfs_chan_spec_scan = relay_open(base_filename: "spectral_scan" , |
525 | parent: ar->debug.debugfs_phy, |
526 | subbuf_size: 1140, n_subbufs: 2500, |
527 | cb: &rfs_spec_scan_cb, NULL); |
528 | debugfs_create_file(name: "spectral_scan_ctl" , |
529 | mode: 0600, |
530 | parent: ar->debug.debugfs_phy, data: ar, |
531 | fops: &fops_spec_scan_ctl); |
532 | debugfs_create_file(name: "spectral_count" , |
533 | mode: 0600, |
534 | parent: ar->debug.debugfs_phy, data: ar, |
535 | fops: &fops_spectral_count); |
536 | debugfs_create_file(name: "spectral_bins" , |
537 | mode: 0600, |
538 | parent: ar->debug.debugfs_phy, data: ar, |
539 | fops: &fops_spectral_bins); |
540 | |
541 | return 0; |
542 | } |
543 | |
544 | void ath10k_spectral_destroy(struct ath10k *ar) |
545 | { |
546 | if (ar->spectral.rfs_chan_spec_scan) { |
547 | relay_close(chan: ar->spectral.rfs_chan_spec_scan); |
548 | ar->spectral.rfs_chan_spec_scan = NULL; |
549 | } |
550 | } |
551 | |