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