diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath10k/spectral.c')
-rw-r--r-- | drivers/net/wireless/ath/ath10k/spectral.c | 561 |
1 files changed, 561 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c new file mode 100644 index 000000000000..3e1454b74e00 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/spectral.c | |||
@@ -0,0 +1,561 @@ | |||
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) { | ||
240 | ath10k_warn(ar, "failed to enable spectral scan: %d\n", res); | ||
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) { | ||
274 | ath10k_warn(ar, "failed to configure spectral scan: %d\n", res); | ||
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) { | ||
335 | ath10k_warn(ar, "failed to reconfigure spectral scan: %d\n", | ||
336 | res); | ||
337 | } | ||
338 | res = ath10k_spectral_scan_trigger(ar); | ||
339 | if (res < 0) { | ||
340 | ath10k_warn(ar, "failed to trigger spectral scan: %d\n", | ||
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 | } | ||