1056 lines · c
1// SPDX-License-Identifier: BSD-3-Clause-Clear2/*3 * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.4 * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.5 */6 7#include <linux/relay.h>8#include "core.h"9#include "debug.h"10 11#define ATH11K_SPECTRAL_NUM_RESP_PER_EVENT 212#define ATH11K_SPECTRAL_EVENT_TIMEOUT_MS 113 14#define ATH11K_SPECTRAL_DWORD_SIZE 415#define ATH11K_SPECTRAL_MIN_BINS 3216#define ATH11K_SPECTRAL_MIN_IB_BINS (ATH11K_SPECTRAL_MIN_BINS >> 1)17#define ATH11K_SPECTRAL_MAX_IB_BINS(x) ((x)->hw_params.spectral.max_fft_bins >> 1)18 19#define ATH11K_SPECTRAL_SCAN_COUNT_MAX 409520 21/* Max channel computed by sum of 2g and 5g band channels */22#define ATH11K_SPECTRAL_TOTAL_CHANNEL 4123#define ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL 7024#define ATH11K_SPECTRAL_PER_SAMPLE_SIZE(x) (sizeof(struct fft_sample_ath11k) + \25 ATH11K_SPECTRAL_MAX_IB_BINS(x))26#define ATH11K_SPECTRAL_TOTAL_SAMPLE (ATH11K_SPECTRAL_TOTAL_CHANNEL * \27 ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL)28#define ATH11K_SPECTRAL_SUB_BUFF_SIZE(x) ATH11K_SPECTRAL_PER_SAMPLE_SIZE(x)29#define ATH11K_SPECTRAL_NUM_SUB_BUF ATH11K_SPECTRAL_TOTAL_SAMPLE30 31#define ATH11K_SPECTRAL_20MHZ 2032#define ATH11K_SPECTRAL_40MHZ 4033#define ATH11K_SPECTRAL_80MHZ 8034#define ATH11K_SPECTRAL_160MHZ 16035 36#define ATH11K_SPECTRAL_SIGNATURE 0xFA37 38#define ATH11K_SPECTRAL_TAG_RADAR_SUMMARY 0x039#define ATH11K_SPECTRAL_TAG_RADAR_FFT 0x140#define ATH11K_SPECTRAL_TAG_SCAN_SUMMARY 0x241#define ATH11K_SPECTRAL_TAG_SCAN_SEARCH 0x342 43#define SPECTRAL_TLV_HDR_LEN GENMASK(15, 0)44#define SPECTRAL_TLV_HDR_TAG GENMASK(23, 16)45#define SPECTRAL_TLV_HDR_SIGN GENMASK(31, 24)46 47#define SPECTRAL_SUMMARY_INFO0_AGC_TOTAL_GAIN GENMASK(7, 0)48#define SPECTRAL_SUMMARY_INFO0_OB_FLAG BIT(8)49#define SPECTRAL_SUMMARY_INFO0_GRP_IDX GENMASK(16, 9)50#define SPECTRAL_SUMMARY_INFO0_RECENT_RFSAT BIT(17)51#define SPECTRAL_SUMMARY_INFO0_INBAND_PWR_DB GENMASK(27, 18)52#define SPECTRAL_SUMMARY_INFO0_FALSE_SCAN BIT(28)53#define SPECTRAL_SUMMARY_INFO0_DETECTOR_ID GENMASK(30, 29)54#define SPECTRAL_SUMMARY_INFO0_PRI80 BIT(31)55 56#define SPECTRAL_SUMMARY_INFO2_PEAK_SIGNED_IDX GENMASK(11, 0)57#define SPECTRAL_SUMMARY_INFO2_PEAK_MAGNITUDE GENMASK(21, 12)58#define SPECTRAL_SUMMARY_INFO2_NARROWBAND_MASK GENMASK(29, 22)59#define SPECTRAL_SUMMARY_INFO2_GAIN_CHANGE BIT(30)60 61struct spectral_tlv {62 __le32 timestamp;63 __le32 header;64} __packed;65 66struct spectral_summary_fft_report {67 __le32 timestamp;68 __le32 tlv_header;69 __le32 info0;70 __le32 reserve0;71 __le32 info2;72 __le32 reserve1;73} __packed;74 75struct ath11k_spectral_summary_report {76 struct wmi_dma_buf_release_meta_data meta;77 u32 timestamp;78 u8 agc_total_gain;79 u8 grp_idx;80 u16 inb_pwr_db;81 s16 peak_idx;82 u16 peak_mag;83 u8 detector_id;84 bool out_of_band_flag;85 bool rf_saturation;86 bool primary80;87 bool gain_change;88 bool false_scan;89};90 91#define SPECTRAL_FFT_REPORT_INFO0_DETECTOR_ID GENMASK(1, 0)92#define SPECTRAL_FFT_REPORT_INFO0_FFT_NUM GENMASK(4, 2)93#define SPECTRAL_FFT_REPORT_INFO0_RADAR_CHECK GENMASK(16, 5)94#define SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX GENMASK(27, 17)95#define SPECTRAL_FFT_REPORT_INFO0_CHAIN_IDX GENMASK(30, 28)96 97#define SPECTRAL_FFT_REPORT_INFO1_BASE_PWR_DB GENMASK(8, 0)98#define SPECTRAL_FFT_REPORT_INFO1_TOTAL_GAIN_DB GENMASK(16, 9)99 100#define SPECTRAL_FFT_REPORT_INFO2_NUM_STRONG_BINS GENMASK(7, 0)101#define SPECTRAL_FFT_REPORT_INFO2_PEAK_MAGNITUDE GENMASK(17, 8)102#define SPECTRAL_FFT_REPORT_INFO2_AVG_PWR_DB GENMASK(24, 18)103#define SPECTRAL_FFT_REPORT_INFO2_REL_PWR_DB GENMASK(31, 25)104 105struct spectral_search_fft_report {106 __le32 timestamp;107 __le32 tlv_header;108 __le32 info0;109 __le32 info1;110 __le32 info2;111 __le32 reserve0;112 u8 bins[];113} __packed;114 115struct ath11k_spectral_search_report {116 u32 timestamp;117 u8 detector_id;118 u8 fft_count;119 u16 radar_check;120 s16 peak_idx;121 u8 chain_idx;122 u16 base_pwr_db;123 u8 total_gain_db;124 u8 strong_bin_count;125 u16 peak_mag;126 u8 avg_pwr_db;127 u8 rel_pwr_db;128};129 130static struct dentry *create_buf_file_handler(const char *filename,131 struct dentry *parent,132 umode_t mode,133 struct rchan_buf *buf,134 int *is_global)135{136 struct dentry *buf_file;137 138 buf_file = debugfs_create_file(filename, mode, parent, buf,139 &relay_file_operations);140 *is_global = 1;141 return buf_file;142}143 144static int remove_buf_file_handler(struct dentry *dentry)145{146 debugfs_remove(dentry);147 148 return 0;149}150 151static const struct rchan_callbacks rfs_scan_cb = {152 .create_buf_file = create_buf_file_handler,153 .remove_buf_file = remove_buf_file_handler,154};155 156static struct ath11k_vif *ath11k_spectral_get_vdev(struct ath11k *ar)157{158 struct ath11k_vif *arvif;159 160 lockdep_assert_held(&ar->conf_mutex);161 162 if (list_empty(&ar->arvifs))163 return NULL;164 165 /* if there already is a vif doing spectral, return that. */166 list_for_each_entry(arvif, &ar->arvifs, list)167 if (arvif->spectral_enabled)168 return arvif;169 170 /* otherwise, return the first vif. */171 return list_first_entry(&ar->arvifs, typeof(*arvif), list);172}173 174static int ath11k_spectral_scan_trigger(struct ath11k *ar)175{176 struct ath11k_vif *arvif;177 int ret;178 179 lockdep_assert_held(&ar->conf_mutex);180 181 arvif = ath11k_spectral_get_vdev(ar);182 if (!arvif)183 return -ENODEV;184 185 if (ar->spectral.mode == ATH11K_SPECTRAL_DISABLED)186 return 0;187 188 ar->spectral.is_primary = true;189 190 ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,191 ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR,192 ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE);193 if (ret)194 return ret;195 196 ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,197 ATH11K_WMI_SPECTRAL_TRIGGER_CMD_TRIGGER,198 ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE);199 if (ret)200 return ret;201 202 return 0;203}204 205static int ath11k_spectral_scan_config(struct ath11k *ar,206 enum ath11k_spectral_mode mode)207{208 struct ath11k_wmi_vdev_spectral_conf_param param = { 0 };209 struct ath11k_vif *arvif;210 int ret, count;211 212 lockdep_assert_held(&ar->conf_mutex);213 214 arvif = ath11k_spectral_get_vdev(ar);215 if (!arvif)216 return -ENODEV;217 218 arvif->spectral_enabled = (mode != ATH11K_SPECTRAL_DISABLED);219 220 spin_lock_bh(&ar->spectral.lock);221 ar->spectral.mode = mode;222 spin_unlock_bh(&ar->spectral.lock);223 224 ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,225 ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR,226 ATH11K_WMI_SPECTRAL_ENABLE_CMD_DISABLE);227 if (ret) {228 ath11k_warn(ar->ab, "failed to enable spectral scan: %d\n", ret);229 return ret;230 }231 232 if (mode == ATH11K_SPECTRAL_DISABLED)233 return 0;234 235 if (mode == ATH11K_SPECTRAL_BACKGROUND)236 count = ATH11K_WMI_SPECTRAL_COUNT_DEFAULT;237 else238 count = max_t(u16, 1, ar->spectral.count);239 240 param.vdev_id = arvif->vdev_id;241 param.scan_count = count;242 param.scan_fft_size = ar->spectral.fft_size;243 param.scan_period = ATH11K_WMI_SPECTRAL_PERIOD_DEFAULT;244 param.scan_priority = ATH11K_WMI_SPECTRAL_PRIORITY_DEFAULT;245 param.scan_gc_ena = ATH11K_WMI_SPECTRAL_GC_ENA_DEFAULT;246 param.scan_restart_ena = ATH11K_WMI_SPECTRAL_RESTART_ENA_DEFAULT;247 param.scan_noise_floor_ref = ATH11K_WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT;248 param.scan_init_delay = ATH11K_WMI_SPECTRAL_INIT_DELAY_DEFAULT;249 param.scan_nb_tone_thr = ATH11K_WMI_SPECTRAL_NB_TONE_THR_DEFAULT;250 param.scan_str_bin_thr = ATH11K_WMI_SPECTRAL_STR_BIN_THR_DEFAULT;251 param.scan_wb_rpt_mode = ATH11K_WMI_SPECTRAL_WB_RPT_MODE_DEFAULT;252 param.scan_rssi_rpt_mode = ATH11K_WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT;253 param.scan_rssi_thr = ATH11K_WMI_SPECTRAL_RSSI_THR_DEFAULT;254 param.scan_pwr_format = ATH11K_WMI_SPECTRAL_PWR_FORMAT_DEFAULT;255 param.scan_rpt_mode = ATH11K_WMI_SPECTRAL_RPT_MODE_DEFAULT;256 param.scan_bin_scale = ATH11K_WMI_SPECTRAL_BIN_SCALE_DEFAULT;257 param.scan_dbm_adj = ATH11K_WMI_SPECTRAL_DBM_ADJ_DEFAULT;258 param.scan_chn_mask = ATH11K_WMI_SPECTRAL_CHN_MASK_DEFAULT;259 260 ret = ath11k_wmi_vdev_spectral_conf(ar, ¶m);261 if (ret) {262 ath11k_warn(ar->ab, "failed to configure spectral scan: %d\n", ret);263 return ret;264 }265 266 return 0;267}268 269static ssize_t ath11k_read_file_spec_scan_ctl(struct file *file,270 char __user *user_buf,271 size_t count, loff_t *ppos)272{273 struct ath11k *ar = file->private_data;274 char *mode = "";275 size_t len;276 enum ath11k_spectral_mode spectral_mode;277 278 mutex_lock(&ar->conf_mutex);279 spectral_mode = ar->spectral.mode;280 mutex_unlock(&ar->conf_mutex);281 282 switch (spectral_mode) {283 case ATH11K_SPECTRAL_DISABLED:284 mode = "disable";285 break;286 case ATH11K_SPECTRAL_BACKGROUND:287 mode = "background";288 break;289 case ATH11K_SPECTRAL_MANUAL:290 mode = "manual";291 break;292 }293 294 len = strlen(mode);295 return simple_read_from_buffer(user_buf, count, ppos, mode, len);296}297 298static ssize_t ath11k_write_file_spec_scan_ctl(struct file *file,299 const char __user *user_buf,300 size_t count, loff_t *ppos)301{302 struct ath11k *ar = file->private_data;303 char buf[32];304 ssize_t len;305 int ret;306 307 len = min(count, sizeof(buf) - 1);308 if (copy_from_user(buf, user_buf, len))309 return -EFAULT;310 311 buf[len] = '\0';312 313 mutex_lock(&ar->conf_mutex);314 315 if (strncmp("trigger", buf, 7) == 0) {316 if (ar->spectral.mode == ATH11K_SPECTRAL_MANUAL ||317 ar->spectral.mode == ATH11K_SPECTRAL_BACKGROUND) {318 /* reset the configuration to adopt possibly changed319 * debugfs parameters320 */321 ret = ath11k_spectral_scan_config(ar, ar->spectral.mode);322 if (ret) {323 ath11k_warn(ar->ab, "failed to reconfigure spectral scan: %d\n",324 ret);325 goto unlock;326 }327 328 ret = ath11k_spectral_scan_trigger(ar);329 if (ret) {330 ath11k_warn(ar->ab, "failed to trigger spectral scan: %d\n",331 ret);332 }333 } else {334 ret = -EINVAL;335 }336 } else if (strncmp("background", buf, 10) == 0) {337 ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_BACKGROUND);338 } else if (strncmp("manual", buf, 6) == 0) {339 ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_MANUAL);340 } else if (strncmp("disable", buf, 7) == 0) {341 ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_DISABLED);342 } else {343 ret = -EINVAL;344 }345 346unlock:347 mutex_unlock(&ar->conf_mutex);348 349 if (ret)350 return ret;351 352 return count;353}354 355static const struct file_operations fops_scan_ctl = {356 .read = ath11k_read_file_spec_scan_ctl,357 .write = ath11k_write_file_spec_scan_ctl,358 .open = simple_open,359 .owner = THIS_MODULE,360 .llseek = default_llseek,361};362 363static ssize_t ath11k_read_file_spectral_count(struct file *file,364 char __user *user_buf,365 size_t count, loff_t *ppos)366{367 struct ath11k *ar = file->private_data;368 char buf[32];369 size_t len;370 u16 spectral_count;371 372 mutex_lock(&ar->conf_mutex);373 spectral_count = ar->spectral.count;374 mutex_unlock(&ar->conf_mutex);375 376 len = sprintf(buf, "%d\n", spectral_count);377 return simple_read_from_buffer(user_buf, count, ppos, buf, len);378}379 380static ssize_t ath11k_write_file_spectral_count(struct file *file,381 const char __user *user_buf,382 size_t count, loff_t *ppos)383{384 struct ath11k *ar = file->private_data;385 unsigned long val;386 ssize_t ret;387 388 ret = kstrtoul_from_user(user_buf, count, 0, &val);389 if (ret)390 return ret;391 392 if (val > ATH11K_SPECTRAL_SCAN_COUNT_MAX)393 return -EINVAL;394 395 mutex_lock(&ar->conf_mutex);396 ar->spectral.count = val;397 mutex_unlock(&ar->conf_mutex);398 399 return count;400}401 402static const struct file_operations fops_scan_count = {403 .read = ath11k_read_file_spectral_count,404 .write = ath11k_write_file_spectral_count,405 .open = simple_open,406 .owner = THIS_MODULE,407 .llseek = default_llseek,408};409 410static ssize_t ath11k_read_file_spectral_bins(struct file *file,411 char __user *user_buf,412 size_t count, loff_t *ppos)413{414 struct ath11k *ar = file->private_data;415 char buf[32];416 unsigned int bins, fft_size;417 size_t len;418 419 mutex_lock(&ar->conf_mutex);420 421 fft_size = ar->spectral.fft_size;422 bins = 1 << fft_size;423 424 mutex_unlock(&ar->conf_mutex);425 426 len = sprintf(buf, "%d\n", bins);427 return simple_read_from_buffer(user_buf, count, ppos, buf, len);428}429 430static ssize_t ath11k_write_file_spectral_bins(struct file *file,431 const char __user *user_buf,432 size_t count, loff_t *ppos)433{434 struct ath11k *ar = file->private_data;435 unsigned long val;436 ssize_t ret;437 438 ret = kstrtoul_from_user(user_buf, count, 0, &val);439 if (ret)440 return ret;441 442 if (val < ATH11K_SPECTRAL_MIN_BINS ||443 val > ar->ab->hw_params.spectral.max_fft_bins)444 return -EINVAL;445 446 if (!is_power_of_2(val))447 return -EINVAL;448 449 mutex_lock(&ar->conf_mutex);450 ar->spectral.fft_size = ilog2(val);451 mutex_unlock(&ar->conf_mutex);452 453 return count;454}455 456static const struct file_operations fops_scan_bins = {457 .read = ath11k_read_file_spectral_bins,458 .write = ath11k_write_file_spectral_bins,459 .open = simple_open,460 .owner = THIS_MODULE,461 .llseek = default_llseek,462};463 464static int ath11k_spectral_pull_summary(struct ath11k *ar,465 struct wmi_dma_buf_release_meta_data *meta,466 struct spectral_summary_fft_report *summary,467 struct ath11k_spectral_summary_report *report)468{469 report->timestamp = __le32_to_cpu(summary->timestamp);470 report->agc_total_gain = FIELD_GET(SPECTRAL_SUMMARY_INFO0_AGC_TOTAL_GAIN,471 __le32_to_cpu(summary->info0));472 report->out_of_band_flag = FIELD_GET(SPECTRAL_SUMMARY_INFO0_OB_FLAG,473 __le32_to_cpu(summary->info0));474 report->grp_idx = FIELD_GET(SPECTRAL_SUMMARY_INFO0_GRP_IDX,475 __le32_to_cpu(summary->info0));476 report->rf_saturation = FIELD_GET(SPECTRAL_SUMMARY_INFO0_RECENT_RFSAT,477 __le32_to_cpu(summary->info0));478 report->inb_pwr_db = FIELD_GET(SPECTRAL_SUMMARY_INFO0_INBAND_PWR_DB,479 __le32_to_cpu(summary->info0));480 report->false_scan = FIELD_GET(SPECTRAL_SUMMARY_INFO0_FALSE_SCAN,481 __le32_to_cpu(summary->info0));482 report->detector_id = FIELD_GET(SPECTRAL_SUMMARY_INFO0_DETECTOR_ID,483 __le32_to_cpu(summary->info0));484 report->primary80 = FIELD_GET(SPECTRAL_SUMMARY_INFO0_PRI80,485 __le32_to_cpu(summary->info0));486 report->peak_idx = FIELD_GET(SPECTRAL_SUMMARY_INFO2_PEAK_SIGNED_IDX,487 __le32_to_cpu(summary->info2));488 report->peak_mag = FIELD_GET(SPECTRAL_SUMMARY_INFO2_PEAK_MAGNITUDE,489 __le32_to_cpu(summary->info2));490 report->gain_change = FIELD_GET(SPECTRAL_SUMMARY_INFO2_GAIN_CHANGE,491 __le32_to_cpu(summary->info2));492 493 memcpy(&report->meta, meta, sizeof(*meta));494 495 return 0;496}497 498static int ath11k_spectral_pull_search(struct ath11k *ar,499 struct spectral_search_fft_report *search,500 struct ath11k_spectral_search_report *report)501{502 report->timestamp = __le32_to_cpu(search->timestamp);503 report->detector_id = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_DETECTOR_ID,504 __le32_to_cpu(search->info0));505 report->fft_count = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_FFT_NUM,506 __le32_to_cpu(search->info0));507 report->radar_check = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_RADAR_CHECK,508 __le32_to_cpu(search->info0));509 report->peak_idx = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX,510 __le32_to_cpu(search->info0));511 report->chain_idx = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_CHAIN_IDX,512 __le32_to_cpu(search->info0));513 report->base_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO1_BASE_PWR_DB,514 __le32_to_cpu(search->info1));515 report->total_gain_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO1_TOTAL_GAIN_DB,516 __le32_to_cpu(search->info1));517 report->strong_bin_count = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_NUM_STRONG_BINS,518 __le32_to_cpu(search->info2));519 report->peak_mag = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_PEAK_MAGNITUDE,520 __le32_to_cpu(search->info2));521 report->avg_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_AVG_PWR_DB,522 __le32_to_cpu(search->info2));523 report->rel_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_REL_PWR_DB,524 __le32_to_cpu(search->info2));525 526 return 0;527}528 529static u8 ath11k_spectral_get_max_exp(s8 max_index, u8 max_magnitude,530 int bin_len, u8 *bins)531{532 int dc_pos;533 u8 max_exp;534 535 dc_pos = bin_len / 2;536 537 /* peak index outside of bins */538 if (dc_pos <= max_index || -dc_pos >= max_index)539 return 0;540 541 for (max_exp = 0; max_exp < 8; max_exp++) {542 if (bins[dc_pos + max_index] == (max_magnitude >> max_exp))543 break;544 }545 546 /* max_exp not found */547 if (bins[dc_pos + max_index] != (max_magnitude >> max_exp))548 return 0;549 550 return max_exp;551}552 553static void ath11k_spectral_parse_fft(u8 *outbins, u8 *inbins, int num_bins, u8 fft_sz)554{555 int i, j;556 557 i = 0;558 j = 0;559 while (i < num_bins) {560 outbins[i] = inbins[j];561 i++;562 j += fft_sz;563 }564}565 566static567int ath11k_spectral_process_fft(struct ath11k *ar,568 struct ath11k_spectral_summary_report *summary,569 void *data,570 struct fft_sample_ath11k *fft_sample,571 u32 data_len)572{573 struct ath11k_base *ab = ar->ab;574 struct spectral_search_fft_report *fft_report = data;575 struct ath11k_spectral_search_report search;576 struct spectral_tlv *tlv;577 int tlv_len, bin_len, num_bins;578 u16 length, freq;579 u8 chan_width_mhz, bin_sz;580 int ret;581 u32 check_length;582 bool fragment_sample = false;583 584 lockdep_assert_held(&ar->spectral.lock);585 586 if (!ab->hw_params.spectral.fft_sz) {587 ath11k_warn(ab, "invalid bin size type for hw rev %d\n",588 ab->hw_rev);589 return -EINVAL;590 }591 592 tlv = data;593 tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN, __le32_to_cpu(tlv->header));594 /* convert Dword into bytes */595 tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE;596 bin_len = tlv_len - ab->hw_params.spectral.fft_hdr_len;597 598 if (data_len < (bin_len + sizeof(*fft_report))) {599 ath11k_warn(ab, "mismatch in expected bin len %d and data len %d\n",600 bin_len, data_len);601 return -EINVAL;602 }603 604 bin_sz = ab->hw_params.spectral.fft_sz + ab->hw_params.spectral.fft_pad_sz;605 num_bins = bin_len / bin_sz;606 /* Only In-band bins are useful to user for visualize */607 num_bins >>= 1;608 609 if (num_bins < ATH11K_SPECTRAL_MIN_IB_BINS ||610 num_bins > ATH11K_SPECTRAL_MAX_IB_BINS(ab) ||611 !is_power_of_2(num_bins)) {612 ath11k_warn(ab, "Invalid num of bins %d\n", num_bins);613 return -EINVAL;614 }615 616 check_length = sizeof(*fft_report) + (num_bins * ab->hw_params.spectral.fft_sz);617 ret = ath11k_dbring_validate_buffer(ar, data, check_length);618 if (ret) {619 ath11k_warn(ar->ab, "found magic value in fft data, dropping\n");620 return ret;621 }622 623 ret = ath11k_spectral_pull_search(ar, data, &search);624 if (ret) {625 ath11k_warn(ab, "failed to pull search report %d\n", ret);626 return ret;627 }628 629 chan_width_mhz = summary->meta.ch_width;630 631 switch (chan_width_mhz) {632 case ATH11K_SPECTRAL_20MHZ:633 case ATH11K_SPECTRAL_40MHZ:634 case ATH11K_SPECTRAL_80MHZ:635 fft_sample->chan_width_mhz = chan_width_mhz;636 break;637 case ATH11K_SPECTRAL_160MHZ:638 if (ab->hw_params.spectral.fragment_160mhz) {639 chan_width_mhz /= 2;640 fragment_sample = true;641 }642 fft_sample->chan_width_mhz = chan_width_mhz;643 break;644 default:645 ath11k_warn(ab, "invalid channel width %d\n", chan_width_mhz);646 return -EINVAL;647 }648 649 length = sizeof(*fft_sample) - sizeof(struct fft_sample_tlv) + num_bins;650 fft_sample->tlv.type = ATH_FFT_SAMPLE_ATH11K;651 fft_sample->tlv.length = __cpu_to_be16(length);652 653 fft_sample->tsf = __cpu_to_be32(search.timestamp);654 fft_sample->max_magnitude = __cpu_to_be16(search.peak_mag);655 fft_sample->max_index = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX,656 __le32_to_cpu(fft_report->info0));657 658 summary->inb_pwr_db >>= 1;659 fft_sample->rssi = __cpu_to_be16(summary->inb_pwr_db);660 fft_sample->noise = __cpu_to_be32(summary->meta.noise_floor[search.chain_idx]);661 662 freq = summary->meta.freq1;663 fft_sample->freq1 = __cpu_to_be16(freq);664 665 freq = summary->meta.freq2;666 fft_sample->freq2 = __cpu_to_be16(freq);667 668 /* If freq2 is available then the spectral scan results are fragmented669 * as primary and secondary670 */671 if (fragment_sample && freq) {672 if (!ar->spectral.is_primary)673 fft_sample->freq1 = cpu_to_be16(freq);674 675 /* We have to toggle the is_primary to handle the next report */676 ar->spectral.is_primary = !ar->spectral.is_primary;677 }678 679 ath11k_spectral_parse_fft(fft_sample->data, fft_report->bins, num_bins,680 ab->hw_params.spectral.fft_sz);681 682 fft_sample->max_exp = ath11k_spectral_get_max_exp(fft_sample->max_index,683 search.peak_mag,684 num_bins,685 fft_sample->data);686 687 if (ar->spectral.rfs_scan)688 relay_write(ar->spectral.rfs_scan, fft_sample,689 length + sizeof(struct fft_sample_tlv));690 691 return 0;692}693 694static int ath11k_spectral_process_data(struct ath11k *ar,695 struct ath11k_dbring_data *param)696{697 struct ath11k_base *ab = ar->ab;698 struct spectral_tlv *tlv;699 struct spectral_summary_fft_report *summary = NULL;700 struct ath11k_spectral_summary_report summ_rpt;701 struct fft_sample_ath11k *fft_sample = NULL;702 u8 *data;703 u32 data_len, i;704 u8 sign, tag;705 int tlv_len, sample_sz;706 int ret;707 bool quit = false;708 709 spin_lock_bh(&ar->spectral.lock);710 711 if (!ar->spectral.enabled) {712 ret = -EINVAL;713 goto unlock;714 }715 716 sample_sz = sizeof(*fft_sample) + ATH11K_SPECTRAL_MAX_IB_BINS(ab);717 fft_sample = kmalloc(sample_sz, GFP_ATOMIC);718 if (!fft_sample) {719 ret = -ENOBUFS;720 goto unlock;721 }722 723 data = param->data;724 data_len = param->data_sz;725 i = 0;726 while (!quit && (i < data_len)) {727 if ((i + sizeof(*tlv)) > data_len) {728 ath11k_warn(ab, "failed to parse spectral tlv hdr at bytes %d\n",729 i);730 ret = -EINVAL;731 goto err;732 }733 734 tlv = (struct spectral_tlv *)&data[i];735 sign = FIELD_GET(SPECTRAL_TLV_HDR_SIGN,736 __le32_to_cpu(tlv->header));737 if (sign != ATH11K_SPECTRAL_SIGNATURE) {738 ath11k_warn(ab, "Invalid sign 0x%x at bytes %d\n",739 sign, i);740 ret = -EINVAL;741 goto err;742 }743 744 tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN,745 __le32_to_cpu(tlv->header));746 /* convert Dword into bytes */747 tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE;748 if ((i + sizeof(*tlv) + tlv_len) > data_len) {749 ath11k_warn(ab, "failed to parse spectral tlv payload at bytes %d tlv_len:%d data_len:%d\n",750 i, tlv_len, data_len);751 ret = -EINVAL;752 goto err;753 }754 755 tag = FIELD_GET(SPECTRAL_TLV_HDR_TAG,756 __le32_to_cpu(tlv->header));757 switch (tag) {758 case ATH11K_SPECTRAL_TAG_SCAN_SUMMARY:759 /* HW bug in tlv length of summary report,760 * HW report 3 DWORD size but the data payload761 * is 4 DWORD size (16 bytes).762 * Need to remove this workaround once HW bug fixed763 */764 tlv_len = sizeof(*summary) - sizeof(*tlv) +765 ab->hw_params.spectral.summary_pad_sz;766 767 if (tlv_len < (sizeof(*summary) - sizeof(*tlv))) {768 ath11k_warn(ab, "failed to parse spectral summary at bytes %d tlv_len:%d\n",769 i, tlv_len);770 ret = -EINVAL;771 goto err;772 }773 774 ret = ath11k_dbring_validate_buffer(ar, data, tlv_len);775 if (ret) {776 ath11k_warn(ar->ab, "found magic value in spectral summary, dropping\n");777 goto err;778 }779 780 summary = (struct spectral_summary_fft_report *)tlv;781 ath11k_spectral_pull_summary(ar, ¶m->meta,782 summary, &summ_rpt);783 break;784 case ATH11K_SPECTRAL_TAG_SCAN_SEARCH:785 if (tlv_len < (sizeof(struct spectral_search_fft_report) -786 sizeof(*tlv))) {787 ath11k_warn(ab, "failed to parse spectral search fft at bytes %d\n",788 i);789 ret = -EINVAL;790 goto err;791 }792 793 memset(fft_sample, 0, sample_sz);794 ret = ath11k_spectral_process_fft(ar, &summ_rpt, tlv,795 fft_sample,796 data_len - i);797 if (ret) {798 ath11k_warn(ab, "failed to process spectral fft at bytes %d\n",799 i);800 goto err;801 }802 quit = true;803 break;804 }805 806 i += sizeof(*tlv) + tlv_len;807 }808 809 ret = 0;810 811err:812 kfree(fft_sample);813unlock:814 spin_unlock_bh(&ar->spectral.lock);815 return ret;816}817 818static int ath11k_spectral_ring_alloc(struct ath11k *ar,819 struct ath11k_dbring_cap *db_cap)820{821 struct ath11k_spectral *sp = &ar->spectral;822 int ret;823 824 ret = ath11k_dbring_srng_setup(ar, &sp->rx_ring,825 0, db_cap->min_elem);826 if (ret) {827 ath11k_warn(ar->ab, "failed to setup db ring\n");828 return ret;829 }830 831 ath11k_dbring_set_cfg(ar, &sp->rx_ring,832 ATH11K_SPECTRAL_NUM_RESP_PER_EVENT,833 ATH11K_SPECTRAL_EVENT_TIMEOUT_MS,834 ath11k_spectral_process_data);835 836 ret = ath11k_dbring_buf_setup(ar, &sp->rx_ring, db_cap);837 if (ret) {838 ath11k_warn(ar->ab, "failed to setup db ring buffer\n");839 goto srng_cleanup;840 }841 842 ret = ath11k_dbring_wmi_cfg_setup(ar, &sp->rx_ring,843 WMI_DIRECT_BUF_SPECTRAL);844 if (ret) {845 ath11k_warn(ar->ab, "failed to setup db ring cfg\n");846 goto buffer_cleanup;847 }848 849 return 0;850 851buffer_cleanup:852 ath11k_dbring_buf_cleanup(ar, &sp->rx_ring);853srng_cleanup:854 ath11k_dbring_srng_cleanup(ar, &sp->rx_ring);855 return ret;856}857 858static inline void ath11k_spectral_ring_free(struct ath11k *ar)859{860 struct ath11k_spectral *sp = &ar->spectral;861 862 ath11k_dbring_srng_cleanup(ar, &sp->rx_ring);863 ath11k_dbring_buf_cleanup(ar, &sp->rx_ring);864}865 866static inline void ath11k_spectral_debug_unregister(struct ath11k *ar)867{868 debugfs_remove(ar->spectral.scan_bins);869 ar->spectral.scan_bins = NULL;870 871 debugfs_remove(ar->spectral.scan_count);872 ar->spectral.scan_count = NULL;873 874 debugfs_remove(ar->spectral.scan_ctl);875 ar->spectral.scan_ctl = NULL;876 877 if (ar->spectral.rfs_scan) {878 relay_close(ar->spectral.rfs_scan);879 ar->spectral.rfs_scan = NULL;880 }881}882 883int ath11k_spectral_vif_stop(struct ath11k_vif *arvif)884{885 if (!arvif->spectral_enabled)886 return 0;887 888 return ath11k_spectral_scan_config(arvif->ar, ATH11K_SPECTRAL_DISABLED);889}890 891void ath11k_spectral_reset_buffer(struct ath11k *ar)892{893 if (!ar->spectral.enabled)894 return;895 896 if (ar->spectral.rfs_scan)897 relay_reset(ar->spectral.rfs_scan);898}899 900void ath11k_spectral_deinit(struct ath11k_base *ab)901{902 struct ath11k *ar;903 struct ath11k_spectral *sp;904 int i;905 906 for (i = 0; i < ab->num_radios; i++) {907 ar = ab->pdevs[i].ar;908 sp = &ar->spectral;909 910 if (!sp->enabled)911 continue;912 913 mutex_lock(&ar->conf_mutex);914 ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_DISABLED);915 mutex_unlock(&ar->conf_mutex);916 917 spin_lock_bh(&sp->lock);918 sp->enabled = false;919 spin_unlock_bh(&sp->lock);920 921 ath11k_spectral_debug_unregister(ar);922 ath11k_spectral_ring_free(ar);923 }924}925 926static inline int ath11k_spectral_debug_register(struct ath11k *ar)927{928 int ret;929 930 ar->spectral.rfs_scan = relay_open("spectral_scan",931 ar->debug.debugfs_pdev,932 ATH11K_SPECTRAL_SUB_BUFF_SIZE(ar->ab),933 ATH11K_SPECTRAL_NUM_SUB_BUF,934 &rfs_scan_cb, NULL);935 if (!ar->spectral.rfs_scan) {936 ath11k_warn(ar->ab, "failed to open relay in pdev %d\n",937 ar->pdev_idx);938 return -EINVAL;939 }940 941 ar->spectral.scan_ctl = debugfs_create_file("spectral_scan_ctl",942 0600,943 ar->debug.debugfs_pdev, ar,944 &fops_scan_ctl);945 if (!ar->spectral.scan_ctl) {946 ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",947 ar->pdev_idx);948 ret = -EINVAL;949 goto debug_unregister;950 }951 952 ar->spectral.scan_count = debugfs_create_file("spectral_count",953 0600,954 ar->debug.debugfs_pdev, ar,955 &fops_scan_count);956 if (!ar->spectral.scan_count) {957 ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",958 ar->pdev_idx);959 ret = -EINVAL;960 goto debug_unregister;961 }962 963 ar->spectral.scan_bins = debugfs_create_file("spectral_bins",964 0600,965 ar->debug.debugfs_pdev, ar,966 &fops_scan_bins);967 if (!ar->spectral.scan_bins) {968 ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",969 ar->pdev_idx);970 ret = -EINVAL;971 goto debug_unregister;972 }973 974 return 0;975 976debug_unregister:977 ath11k_spectral_debug_unregister(ar);978 return ret;979}980 981int ath11k_spectral_init(struct ath11k_base *ab)982{983 struct ath11k *ar;984 struct ath11k_spectral *sp;985 struct ath11k_dbring_cap db_cap;986 int ret;987 int i;988 989 if (!test_bit(WMI_TLV_SERVICE_FREQINFO_IN_METADATA,990 ab->wmi_ab.svc_map))991 return 0;992 993 if (!ab->hw_params.spectral.fft_sz)994 return 0;995 996 for (i = 0; i < ab->num_radios; i++) {997 ar = ab->pdevs[i].ar;998 sp = &ar->spectral;999 1000 ret = ath11k_dbring_get_cap(ar->ab, ar->pdev_idx,1001 WMI_DIRECT_BUF_SPECTRAL,1002 &db_cap);1003 if (ret)1004 continue;1005 1006 idr_init(&sp->rx_ring.bufs_idr);1007 spin_lock_init(&sp->rx_ring.idr_lock);1008 spin_lock_init(&sp->lock);1009 1010 ret = ath11k_spectral_ring_alloc(ar, &db_cap);1011 if (ret) {1012 ath11k_warn(ab, "failed to init spectral ring for pdev %d\n",1013 i);1014 goto deinit;1015 }1016 1017 spin_lock_bh(&sp->lock);1018 1019 sp->mode = ATH11K_SPECTRAL_DISABLED;1020 sp->count = ATH11K_WMI_SPECTRAL_COUNT_DEFAULT;1021 sp->fft_size = ATH11K_WMI_SPECTRAL_FFT_SIZE_DEFAULT;1022 sp->enabled = true;1023 1024 spin_unlock_bh(&sp->lock);1025 1026 ret = ath11k_spectral_debug_register(ar);1027 if (ret) {1028 ath11k_warn(ab, "failed to register spectral for pdev %d\n",1029 i);1030 goto deinit;1031 }1032 }1033 1034 return 0;1035 1036deinit:1037 ath11k_spectral_deinit(ab);1038 return ret;1039}1040 1041enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar)1042{1043 if (ar->spectral.enabled)1044 return ar->spectral.mode;1045 else1046 return ATH11K_SPECTRAL_DISABLED;1047}1048 1049struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar)1050{1051 if (ar->spectral.enabled)1052 return &ar->spectral.rx_ring;1053 else1054 return NULL;1055}1056