Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/dev/athk/ath10k/spectral.c
48375 views
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(ar->spectral.rfs_chan_spec_scan, 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(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(fft_sample->max_index, peak_mag,
145
bin_len, 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);
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(&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, "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);
265
if (res < 0) {
266
ath10k_warn(ar, "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(&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(user_buf, count, ppos, mode, 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(buf, user_buf, 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
ar->spectral.mode);
326
if (res < 0) {
327
ath10k_warn(ar, "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, "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, SPECTRAL_BACKGROUND);
340
} else if (strncmp("manual", buf, 6) == 0) {
341
res = ath10k_spectral_scan_config(ar, SPECTRAL_MANUAL);
342
} else if (strncmp("disable", buf, 7) == 0) {
343
res = ath10k_spectral_scan_config(ar, SPECTRAL_DISABLED);
344
} else {
345
res = -EINVAL;
346
}
347
348
mutex_unlock(&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(&ar->conf_mutex);
376
377
len = sprintf(buf, "%d\n", spectral_count);
378
return simple_read_from_buffer(user_buf, count, ppos, buf, 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
char buf[32];
388
ssize_t len;
389
390
len = min(count, sizeof(buf) - 1);
391
if (copy_from_user(buf, user_buf, len))
392
return -EFAULT;
393
394
buf[len] = '\0';
395
if (kstrtoul(buf, 0, &val))
396
return -EINVAL;
397
398
if (val > 255)
399
return -EINVAL;
400
401
mutex_lock(&ar->conf_mutex);
402
ar->spectral.config.count = val;
403
mutex_unlock(&ar->conf_mutex);
404
405
return count;
406
}
407
408
static const struct file_operations fops_spectral_count = {
409
.read = read_file_spectral_count,
410
.write = write_file_spectral_count,
411
.open = simple_open,
412
.owner = THIS_MODULE,
413
.llseek = default_llseek,
414
};
415
416
static ssize_t read_file_spectral_bins(struct file *file,
417
char __user *user_buf,
418
size_t count, loff_t *ppos)
419
{
420
struct ath10k *ar = file->private_data;
421
char buf[32];
422
unsigned int bins, fft_size, bin_scale;
423
size_t len;
424
425
mutex_lock(&ar->conf_mutex);
426
427
fft_size = ar->spectral.config.fft_size;
428
bin_scale = WMI_SPECTRAL_BIN_SCALE_DEFAULT;
429
bins = 1 << (fft_size - bin_scale);
430
431
mutex_unlock(&ar->conf_mutex);
432
433
len = sprintf(buf, "%d\n", bins);
434
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
435
}
436
437
static ssize_t write_file_spectral_bins(struct file *file,
438
const char __user *user_buf,
439
size_t count, loff_t *ppos)
440
{
441
struct ath10k *ar = file->private_data;
442
unsigned long val;
443
char buf[32];
444
ssize_t len;
445
446
len = min(count, sizeof(buf) - 1);
447
if (copy_from_user(buf, user_buf, len))
448
return -EFAULT;
449
450
buf[len] = '\0';
451
if (kstrtoul(buf, 0, &val))
452
return -EINVAL;
453
454
if (val < 64 || val > SPECTRAL_ATH10K_MAX_NUM_BINS)
455
return -EINVAL;
456
457
if (!is_power_of_2(val))
458
return -EINVAL;
459
460
mutex_lock(&ar->conf_mutex);
461
ar->spectral.config.fft_size = ilog2(val);
462
ar->spectral.config.fft_size += WMI_SPECTRAL_BIN_SCALE_DEFAULT;
463
mutex_unlock(&ar->conf_mutex);
464
465
return count;
466
}
467
468
static const struct file_operations fops_spectral_bins = {
469
.read = read_file_spectral_bins,
470
.write = write_file_spectral_bins,
471
.open = simple_open,
472
.owner = THIS_MODULE,
473
.llseek = default_llseek,
474
};
475
476
static struct dentry *create_buf_file_handler(const char *filename,
477
struct dentry *parent,
478
umode_t mode,
479
struct rchan_buf *buf,
480
int *is_global)
481
{
482
struct dentry *buf_file;
483
484
buf_file = debugfs_create_file(filename, mode, parent, buf,
485
&relay_file_operations);
486
if (IS_ERR(buf_file))
487
return NULL;
488
489
*is_global = 1;
490
return buf_file;
491
}
492
493
static int remove_buf_file_handler(struct dentry *dentry)
494
{
495
debugfs_remove(dentry);
496
497
return 0;
498
}
499
500
static const 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
int ath10k_spectral_start(struct ath10k *ar)
506
{
507
struct ath10k_vif *arvif;
508
509
lockdep_assert_held(&ar->conf_mutex);
510
511
list_for_each_entry(arvif, &ar->arvifs, list)
512
arvif->spectral_enabled = 0;
513
514
ar->spectral.mode = SPECTRAL_DISABLED;
515
ar->spectral.config.count = WMI_SPECTRAL_COUNT_DEFAULT;
516
ar->spectral.config.fft_size = WMI_SPECTRAL_FFT_SIZE_DEFAULT;
517
518
return 0;
519
}
520
521
int ath10k_spectral_vif_stop(struct ath10k_vif *arvif)
522
{
523
if (!arvif->spectral_enabled)
524
return 0;
525
526
return ath10k_spectral_scan_config(arvif->ar, SPECTRAL_DISABLED);
527
}
528
529
int ath10k_spectral_create(struct ath10k *ar)
530
{
531
/* The buffer size covers whole channels in dual bands up to 128 bins.
532
* Scan with bigger than 128 bins needs to be run on single band each.
533
*/
534
ar->spectral.rfs_chan_spec_scan = relay_open("spectral_scan",
535
ar->debug.debugfs_phy,
536
1140, 2500,
537
&rfs_spec_scan_cb, NULL);
538
debugfs_create_file("spectral_scan_ctl",
539
0600,
540
ar->debug.debugfs_phy, ar,
541
&fops_spec_scan_ctl);
542
debugfs_create_file("spectral_count",
543
0600,
544
ar->debug.debugfs_phy, ar,
545
&fops_spectral_count);
546
debugfs_create_file("spectral_bins",
547
0600,
548
ar->debug.debugfs_phy, ar,
549
&fops_spectral_bins);
550
551
return 0;
552
}
553
554
void ath10k_spectral_destroy(struct ath10k *ar)
555
{
556
if (ar->spectral.rfs_chan_spec_scan) {
557
relay_close(ar->spectral.rfs_chan_spec_scan);
558
ar->spectral.rfs_chan_spec_scan = NULL;
559
}
560
}
561
562