12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064 |
- // SPDX-License-Identifier: BSD-3-Clause-Clear
- /*
- * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- */
- #include <linux/relay.h>
- #include "core.h"
- #include "debug.h"
- #define ATH11K_SPECTRAL_NUM_RESP_PER_EVENT 2
- #define ATH11K_SPECTRAL_EVENT_TIMEOUT_MS 1
- #define ATH11K_SPECTRAL_DWORD_SIZE 4
- #define ATH11K_SPECTRAL_MIN_BINS 32
- #define ATH11K_SPECTRAL_MIN_IB_BINS (ATH11K_SPECTRAL_MIN_BINS >> 1)
- #define ATH11K_SPECTRAL_MAX_IB_BINS(x) ((x)->hw_params.spectral.max_fft_bins >> 1)
- #define ATH11K_SPECTRAL_SCAN_COUNT_MAX 4095
- /* Max channel computed by sum of 2g and 5g band channels */
- #define ATH11K_SPECTRAL_TOTAL_CHANNEL 41
- #define ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL 70
- #define ATH11K_SPECTRAL_PER_SAMPLE_SIZE(x) (sizeof(struct fft_sample_ath11k) + \
- ATH11K_SPECTRAL_MAX_IB_BINS(x))
- #define ATH11K_SPECTRAL_TOTAL_SAMPLE (ATH11K_SPECTRAL_TOTAL_CHANNEL * \
- ATH11K_SPECTRAL_SAMPLES_PER_CHANNEL)
- #define ATH11K_SPECTRAL_SUB_BUFF_SIZE(x) ATH11K_SPECTRAL_PER_SAMPLE_SIZE(x)
- #define ATH11K_SPECTRAL_NUM_SUB_BUF ATH11K_SPECTRAL_TOTAL_SAMPLE
- #define ATH11K_SPECTRAL_20MHZ 20
- #define ATH11K_SPECTRAL_40MHZ 40
- #define ATH11K_SPECTRAL_80MHZ 80
- #define ATH11K_SPECTRAL_160MHZ 160
- #define ATH11K_SPECTRAL_SIGNATURE 0xFA
- #define ATH11K_SPECTRAL_TAG_RADAR_SUMMARY 0x0
- #define ATH11K_SPECTRAL_TAG_RADAR_FFT 0x1
- #define ATH11K_SPECTRAL_TAG_SCAN_SUMMARY 0x2
- #define ATH11K_SPECTRAL_TAG_SCAN_SEARCH 0x3
- #define SPECTRAL_TLV_HDR_LEN GENMASK(15, 0)
- #define SPECTRAL_TLV_HDR_TAG GENMASK(23, 16)
- #define SPECTRAL_TLV_HDR_SIGN GENMASK(31, 24)
- #define SPECTRAL_SUMMARY_INFO0_AGC_TOTAL_GAIN GENMASK(7, 0)
- #define SPECTRAL_SUMMARY_INFO0_OB_FLAG BIT(8)
- #define SPECTRAL_SUMMARY_INFO0_GRP_IDX GENMASK(16, 9)
- #define SPECTRAL_SUMMARY_INFO0_RECENT_RFSAT BIT(17)
- #define SPECTRAL_SUMMARY_INFO0_INBAND_PWR_DB GENMASK(27, 18)
- #define SPECTRAL_SUMMARY_INFO0_FALSE_SCAN BIT(28)
- #define SPECTRAL_SUMMARY_INFO0_DETECTOR_ID GENMASK(30, 29)
- #define SPECTRAL_SUMMARY_INFO0_PRI80 BIT(31)
- #define SPECTRAL_SUMMARY_INFO2_PEAK_SIGNED_IDX GENMASK(11, 0)
- #define SPECTRAL_SUMMARY_INFO2_PEAK_MAGNITUDE GENMASK(21, 12)
- #define SPECTRAL_SUMMARY_INFO2_NARROWBAND_MASK GENMASK(29, 22)
- #define SPECTRAL_SUMMARY_INFO2_GAIN_CHANGE BIT(30)
- struct spectral_tlv {
- __le32 timestamp;
- __le32 header;
- } __packed;
- struct spectral_summary_fft_report {
- __le32 timestamp;
- __le32 tlv_header;
- __le32 info0;
- __le32 reserve0;
- __le32 info2;
- __le32 reserve1;
- } __packed;
- struct ath11k_spectral_summary_report {
- struct wmi_dma_buf_release_meta_data meta;
- u32 timestamp;
- u8 agc_total_gain;
- u8 grp_idx;
- u16 inb_pwr_db;
- s16 peak_idx;
- u16 peak_mag;
- u8 detector_id;
- bool out_of_band_flag;
- bool rf_saturation;
- bool primary80;
- bool gain_change;
- bool false_scan;
- };
- #define SPECTRAL_FFT_REPORT_INFO0_DETECTOR_ID GENMASK(1, 0)
- #define SPECTRAL_FFT_REPORT_INFO0_FFT_NUM GENMASK(4, 2)
- #define SPECTRAL_FFT_REPORT_INFO0_RADAR_CHECK GENMASK(16, 5)
- #define SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX GENMASK(27, 17)
- #define SPECTRAL_FFT_REPORT_INFO0_CHAIN_IDX GENMASK(30, 28)
- #define SPECTRAL_FFT_REPORT_INFO1_BASE_PWR_DB GENMASK(8, 0)
- #define SPECTRAL_FFT_REPORT_INFO1_TOTAL_GAIN_DB GENMASK(16, 9)
- #define SPECTRAL_FFT_REPORT_INFO2_NUM_STRONG_BINS GENMASK(7, 0)
- #define SPECTRAL_FFT_REPORT_INFO2_PEAK_MAGNITUDE GENMASK(17, 8)
- #define SPECTRAL_FFT_REPORT_INFO2_AVG_PWR_DB GENMASK(24, 18)
- #define SPECTRAL_FFT_REPORT_INFO2_REL_PWR_DB GENMASK(31, 25)
- struct spectral_search_fft_report {
- __le32 timestamp;
- __le32 tlv_header;
- __le32 info0;
- __le32 info1;
- __le32 info2;
- __le32 reserve0;
- u8 bins[];
- } __packed;
- struct ath11k_spectral_search_report {
- u32 timestamp;
- u8 detector_id;
- u8 fft_count;
- u16 radar_check;
- s16 peak_idx;
- u8 chain_idx;
- u16 base_pwr_db;
- u8 total_gain_db;
- u8 strong_bin_count;
- u16 peak_mag;
- u8 avg_pwr_db;
- u8 rel_pwr_db;
- };
- static struct dentry *create_buf_file_handler(const char *filename,
- struct dentry *parent,
- umode_t mode,
- struct rchan_buf *buf,
- int *is_global)
- {
- struct dentry *buf_file;
- buf_file = debugfs_create_file(filename, mode, parent, buf,
- &relay_file_operations);
- *is_global = 1;
- return buf_file;
- }
- static int remove_buf_file_handler(struct dentry *dentry)
- {
- debugfs_remove(dentry);
- return 0;
- }
- static const struct rchan_callbacks rfs_scan_cb = {
- .create_buf_file = create_buf_file_handler,
- .remove_buf_file = remove_buf_file_handler,
- };
- static struct ath11k_vif *ath11k_spectral_get_vdev(struct ath11k *ar)
- {
- struct ath11k_vif *arvif;
- lockdep_assert_held(&ar->conf_mutex);
- if (list_empty(&ar->arvifs))
- return NULL;
- /* if there already is a vif doing spectral, return that. */
- list_for_each_entry(arvif, &ar->arvifs, list)
- if (arvif->spectral_enabled)
- return arvif;
- /* otherwise, return the first vif. */
- return list_first_entry(&ar->arvifs, typeof(*arvif), list);
- }
- static int ath11k_spectral_scan_trigger(struct ath11k *ar)
- {
- struct ath11k_vif *arvif;
- int ret;
- lockdep_assert_held(&ar->conf_mutex);
- arvif = ath11k_spectral_get_vdev(ar);
- if (!arvif)
- return -ENODEV;
- if (ar->spectral.mode == ATH11K_SPECTRAL_DISABLED)
- return 0;
- ar->spectral.is_primary = true;
- ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
- ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
- ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE);
- if (ret)
- return ret;
- ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
- ATH11K_WMI_SPECTRAL_TRIGGER_CMD_TRIGGER,
- ATH11K_WMI_SPECTRAL_ENABLE_CMD_ENABLE);
- if (ret)
- return ret;
- return 0;
- }
- static int ath11k_spectral_scan_config(struct ath11k *ar,
- enum ath11k_spectral_mode mode)
- {
- struct ath11k_wmi_vdev_spectral_conf_param param = { 0 };
- struct ath11k_vif *arvif;
- int ret, count;
- lockdep_assert_held(&ar->conf_mutex);
- arvif = ath11k_spectral_get_vdev(ar);
- if (!arvif)
- return -ENODEV;
- arvif->spectral_enabled = (mode != ATH11K_SPECTRAL_DISABLED);
- spin_lock_bh(&ar->spectral.lock);
- ar->spectral.mode = mode;
- spin_unlock_bh(&ar->spectral.lock);
- ret = ath11k_wmi_vdev_spectral_enable(ar, arvif->vdev_id,
- ATH11K_WMI_SPECTRAL_TRIGGER_CMD_CLEAR,
- ATH11K_WMI_SPECTRAL_ENABLE_CMD_DISABLE);
- if (ret) {
- ath11k_warn(ar->ab, "failed to enable spectral scan: %d\n", ret);
- return ret;
- }
- if (mode == ATH11K_SPECTRAL_DISABLED)
- return 0;
- if (mode == ATH11K_SPECTRAL_BACKGROUND)
- count = ATH11K_WMI_SPECTRAL_COUNT_DEFAULT;
- else
- count = max_t(u16, 1, ar->spectral.count);
- param.vdev_id = arvif->vdev_id;
- param.scan_count = count;
- param.scan_fft_size = ar->spectral.fft_size;
- param.scan_period = ATH11K_WMI_SPECTRAL_PERIOD_DEFAULT;
- param.scan_priority = ATH11K_WMI_SPECTRAL_PRIORITY_DEFAULT;
- param.scan_gc_ena = ATH11K_WMI_SPECTRAL_GC_ENA_DEFAULT;
- param.scan_restart_ena = ATH11K_WMI_SPECTRAL_RESTART_ENA_DEFAULT;
- param.scan_noise_floor_ref = ATH11K_WMI_SPECTRAL_NOISE_FLOOR_REF_DEFAULT;
- param.scan_init_delay = ATH11K_WMI_SPECTRAL_INIT_DELAY_DEFAULT;
- param.scan_nb_tone_thr = ATH11K_WMI_SPECTRAL_NB_TONE_THR_DEFAULT;
- param.scan_str_bin_thr = ATH11K_WMI_SPECTRAL_STR_BIN_THR_DEFAULT;
- param.scan_wb_rpt_mode = ATH11K_WMI_SPECTRAL_WB_RPT_MODE_DEFAULT;
- param.scan_rssi_rpt_mode = ATH11K_WMI_SPECTRAL_RSSI_RPT_MODE_DEFAULT;
- param.scan_rssi_thr = ATH11K_WMI_SPECTRAL_RSSI_THR_DEFAULT;
- param.scan_pwr_format = ATH11K_WMI_SPECTRAL_PWR_FORMAT_DEFAULT;
- param.scan_rpt_mode = ATH11K_WMI_SPECTRAL_RPT_MODE_DEFAULT;
- param.scan_bin_scale = ATH11K_WMI_SPECTRAL_BIN_SCALE_DEFAULT;
- param.scan_dbm_adj = ATH11K_WMI_SPECTRAL_DBM_ADJ_DEFAULT;
- param.scan_chn_mask = ATH11K_WMI_SPECTRAL_CHN_MASK_DEFAULT;
- ret = ath11k_wmi_vdev_spectral_conf(ar, ¶m);
- if (ret) {
- ath11k_warn(ar->ab, "failed to configure spectral scan: %d\n", ret);
- return ret;
- }
- return 0;
- }
- static ssize_t ath11k_read_file_spec_scan_ctl(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct ath11k *ar = file->private_data;
- char *mode = "";
- size_t len;
- enum ath11k_spectral_mode spectral_mode;
- mutex_lock(&ar->conf_mutex);
- spectral_mode = ar->spectral.mode;
- mutex_unlock(&ar->conf_mutex);
- switch (spectral_mode) {
- case ATH11K_SPECTRAL_DISABLED:
- mode = "disable";
- break;
- case ATH11K_SPECTRAL_BACKGROUND:
- mode = "background";
- break;
- case ATH11K_SPECTRAL_MANUAL:
- mode = "manual";
- break;
- }
- len = strlen(mode);
- return simple_read_from_buffer(user_buf, count, ppos, mode, len);
- }
- static ssize_t ath11k_write_file_spec_scan_ctl(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct ath11k *ar = file->private_data;
- char buf[32];
- ssize_t len;
- int ret;
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len))
- return -EFAULT;
- buf[len] = '\0';
- mutex_lock(&ar->conf_mutex);
- if (strncmp("trigger", buf, 7) == 0) {
- if (ar->spectral.mode == ATH11K_SPECTRAL_MANUAL ||
- ar->spectral.mode == ATH11K_SPECTRAL_BACKGROUND) {
- /* reset the configuration to adopt possibly changed
- * debugfs parameters
- */
- ret = ath11k_spectral_scan_config(ar, ar->spectral.mode);
- if (ret) {
- ath11k_warn(ar->ab, "failed to reconfigure spectral scan: %d\n",
- ret);
- goto unlock;
- }
- ret = ath11k_spectral_scan_trigger(ar);
- if (ret) {
- ath11k_warn(ar->ab, "failed to trigger spectral scan: %d\n",
- ret);
- }
- } else {
- ret = -EINVAL;
- }
- } else if (strncmp("background", buf, 10) == 0) {
- ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_BACKGROUND);
- } else if (strncmp("manual", buf, 6) == 0) {
- ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_MANUAL);
- } else if (strncmp("disable", buf, 7) == 0) {
- ret = ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_DISABLED);
- } else {
- ret = -EINVAL;
- }
- unlock:
- mutex_unlock(&ar->conf_mutex);
- if (ret)
- return ret;
- return count;
- }
- static const struct file_operations fops_scan_ctl = {
- .read = ath11k_read_file_spec_scan_ctl,
- .write = ath11k_write_file_spec_scan_ctl,
- .open = simple_open,
- .owner = THIS_MODULE,
- .llseek = default_llseek,
- };
- static ssize_t ath11k_read_file_spectral_count(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct ath11k *ar = file->private_data;
- char buf[32];
- size_t len;
- u16 spectral_count;
- mutex_lock(&ar->conf_mutex);
- spectral_count = ar->spectral.count;
- mutex_unlock(&ar->conf_mutex);
- len = sprintf(buf, "%d\n", spectral_count);
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
- }
- static ssize_t ath11k_write_file_spectral_count(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct ath11k *ar = file->private_data;
- unsigned long val;
- char buf[32];
- ssize_t len;
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len))
- return -EFAULT;
- buf[len] = '\0';
- if (kstrtoul(buf, 0, &val))
- return -EINVAL;
- if (val > ATH11K_SPECTRAL_SCAN_COUNT_MAX)
- return -EINVAL;
- mutex_lock(&ar->conf_mutex);
- ar->spectral.count = val;
- mutex_unlock(&ar->conf_mutex);
- return count;
- }
- static const struct file_operations fops_scan_count = {
- .read = ath11k_read_file_spectral_count,
- .write = ath11k_write_file_spectral_count,
- .open = simple_open,
- .owner = THIS_MODULE,
- .llseek = default_llseek,
- };
- static ssize_t ath11k_read_file_spectral_bins(struct file *file,
- char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct ath11k *ar = file->private_data;
- char buf[32];
- unsigned int bins, fft_size;
- size_t len;
- mutex_lock(&ar->conf_mutex);
- fft_size = ar->spectral.fft_size;
- bins = 1 << fft_size;
- mutex_unlock(&ar->conf_mutex);
- len = sprintf(buf, "%d\n", bins);
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
- }
- static ssize_t ath11k_write_file_spectral_bins(struct file *file,
- const char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- struct ath11k *ar = file->private_data;
- unsigned long val;
- char buf[32];
- ssize_t len;
- len = min(count, sizeof(buf) - 1);
- if (copy_from_user(buf, user_buf, len))
- return -EFAULT;
- buf[len] = '\0';
- if (kstrtoul(buf, 0, &val))
- return -EINVAL;
- if (val < ATH11K_SPECTRAL_MIN_BINS ||
- val > ar->ab->hw_params.spectral.max_fft_bins)
- return -EINVAL;
- if (!is_power_of_2(val))
- return -EINVAL;
- mutex_lock(&ar->conf_mutex);
- ar->spectral.fft_size = ilog2(val);
- mutex_unlock(&ar->conf_mutex);
- return count;
- }
- static const struct file_operations fops_scan_bins = {
- .read = ath11k_read_file_spectral_bins,
- .write = ath11k_write_file_spectral_bins,
- .open = simple_open,
- .owner = THIS_MODULE,
- .llseek = default_llseek,
- };
- static int ath11k_spectral_pull_summary(struct ath11k *ar,
- struct wmi_dma_buf_release_meta_data *meta,
- struct spectral_summary_fft_report *summary,
- struct ath11k_spectral_summary_report *report)
- {
- report->timestamp = __le32_to_cpu(summary->timestamp);
- report->agc_total_gain = FIELD_GET(SPECTRAL_SUMMARY_INFO0_AGC_TOTAL_GAIN,
- __le32_to_cpu(summary->info0));
- report->out_of_band_flag = FIELD_GET(SPECTRAL_SUMMARY_INFO0_OB_FLAG,
- __le32_to_cpu(summary->info0));
- report->grp_idx = FIELD_GET(SPECTRAL_SUMMARY_INFO0_GRP_IDX,
- __le32_to_cpu(summary->info0));
- report->rf_saturation = FIELD_GET(SPECTRAL_SUMMARY_INFO0_RECENT_RFSAT,
- __le32_to_cpu(summary->info0));
- report->inb_pwr_db = FIELD_GET(SPECTRAL_SUMMARY_INFO0_INBAND_PWR_DB,
- __le32_to_cpu(summary->info0));
- report->false_scan = FIELD_GET(SPECTRAL_SUMMARY_INFO0_FALSE_SCAN,
- __le32_to_cpu(summary->info0));
- report->detector_id = FIELD_GET(SPECTRAL_SUMMARY_INFO0_DETECTOR_ID,
- __le32_to_cpu(summary->info0));
- report->primary80 = FIELD_GET(SPECTRAL_SUMMARY_INFO0_PRI80,
- __le32_to_cpu(summary->info0));
- report->peak_idx = FIELD_GET(SPECTRAL_SUMMARY_INFO2_PEAK_SIGNED_IDX,
- __le32_to_cpu(summary->info2));
- report->peak_mag = FIELD_GET(SPECTRAL_SUMMARY_INFO2_PEAK_MAGNITUDE,
- __le32_to_cpu(summary->info2));
- report->gain_change = FIELD_GET(SPECTRAL_SUMMARY_INFO2_GAIN_CHANGE,
- __le32_to_cpu(summary->info2));
- memcpy(&report->meta, meta, sizeof(*meta));
- return 0;
- }
- static int ath11k_spectral_pull_search(struct ath11k *ar,
- struct spectral_search_fft_report *search,
- struct ath11k_spectral_search_report *report)
- {
- report->timestamp = __le32_to_cpu(search->timestamp);
- report->detector_id = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_DETECTOR_ID,
- __le32_to_cpu(search->info0));
- report->fft_count = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_FFT_NUM,
- __le32_to_cpu(search->info0));
- report->radar_check = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_RADAR_CHECK,
- __le32_to_cpu(search->info0));
- report->peak_idx = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX,
- __le32_to_cpu(search->info0));
- report->chain_idx = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_CHAIN_IDX,
- __le32_to_cpu(search->info0));
- report->base_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO1_BASE_PWR_DB,
- __le32_to_cpu(search->info1));
- report->total_gain_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO1_TOTAL_GAIN_DB,
- __le32_to_cpu(search->info1));
- report->strong_bin_count = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_NUM_STRONG_BINS,
- __le32_to_cpu(search->info2));
- report->peak_mag = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_PEAK_MAGNITUDE,
- __le32_to_cpu(search->info2));
- report->avg_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_AVG_PWR_DB,
- __le32_to_cpu(search->info2));
- report->rel_pwr_db = FIELD_GET(SPECTRAL_FFT_REPORT_INFO2_REL_PWR_DB,
- __le32_to_cpu(search->info2));
- return 0;
- }
- static u8 ath11k_spectral_get_max_exp(s8 max_index, u8 max_magnitude,
- int bin_len, u8 *bins)
- {
- int dc_pos;
- u8 max_exp;
- dc_pos = bin_len / 2;
- /* peak index outside of bins */
- if (dc_pos <= max_index || -dc_pos >= max_index)
- return 0;
- for (max_exp = 0; max_exp < 8; max_exp++) {
- if (bins[dc_pos + max_index] == (max_magnitude >> max_exp))
- break;
- }
- /* max_exp not found */
- if (bins[dc_pos + max_index] != (max_magnitude >> max_exp))
- return 0;
- return max_exp;
- }
- static void ath11k_spectral_parse_fft(u8 *outbins, u8 *inbins, int num_bins, u8 fft_sz)
- {
- int i, j;
- i = 0;
- j = 0;
- while (i < num_bins) {
- outbins[i] = inbins[j];
- i++;
- j += fft_sz;
- }
- }
- static
- int ath11k_spectral_process_fft(struct ath11k *ar,
- struct ath11k_spectral_summary_report *summary,
- void *data,
- struct fft_sample_ath11k *fft_sample,
- u32 data_len)
- {
- struct ath11k_base *ab = ar->ab;
- struct spectral_search_fft_report *fft_report = data;
- struct ath11k_spectral_search_report search;
- struct spectral_tlv *tlv;
- int tlv_len, bin_len, num_bins;
- u16 length, freq;
- u8 chan_width_mhz, bin_sz;
- int ret;
- u32 check_length;
- bool fragment_sample = false;
- lockdep_assert_held(&ar->spectral.lock);
- if (!ab->hw_params.spectral.fft_sz) {
- ath11k_warn(ab, "invalid bin size type for hw rev %d\n",
- ab->hw_rev);
- return -EINVAL;
- }
- tlv = (struct spectral_tlv *)data;
- tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN, __le32_to_cpu(tlv->header));
- /* convert Dword into bytes */
- tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE;
- bin_len = tlv_len - ab->hw_params.spectral.fft_hdr_len;
- if (data_len < (bin_len + sizeof(*fft_report))) {
- ath11k_warn(ab, "mismatch in expected bin len %d and data len %d\n",
- bin_len, data_len);
- return -EINVAL;
- }
- bin_sz = ab->hw_params.spectral.fft_sz + ab->hw_params.spectral.fft_pad_sz;
- num_bins = bin_len / bin_sz;
- /* Only In-band bins are useful to user for visualize */
- num_bins >>= 1;
- if (num_bins < ATH11K_SPECTRAL_MIN_IB_BINS ||
- num_bins > ATH11K_SPECTRAL_MAX_IB_BINS(ab) ||
- !is_power_of_2(num_bins)) {
- ath11k_warn(ab, "Invalid num of bins %d\n", num_bins);
- return -EINVAL;
- }
- check_length = sizeof(*fft_report) + (num_bins * ab->hw_params.spectral.fft_sz);
- ret = ath11k_dbring_validate_buffer(ar, data, check_length);
- if (ret) {
- ath11k_warn(ar->ab, "found magic value in fft data, dropping\n");
- return ret;
- }
- ret = ath11k_spectral_pull_search(ar, data, &search);
- if (ret) {
- ath11k_warn(ab, "failed to pull search report %d\n", ret);
- return ret;
- }
- chan_width_mhz = summary->meta.ch_width;
- switch (chan_width_mhz) {
- case ATH11K_SPECTRAL_20MHZ:
- case ATH11K_SPECTRAL_40MHZ:
- case ATH11K_SPECTRAL_80MHZ:
- fft_sample->chan_width_mhz = chan_width_mhz;
- break;
- case ATH11K_SPECTRAL_160MHZ:
- if (ab->hw_params.spectral.fragment_160mhz) {
- chan_width_mhz /= 2;
- fragment_sample = true;
- }
- fft_sample->chan_width_mhz = chan_width_mhz;
- break;
- default:
- ath11k_warn(ab, "invalid channel width %d\n", chan_width_mhz);
- return -EINVAL;
- }
- length = sizeof(*fft_sample) - sizeof(struct fft_sample_tlv) + num_bins;
- fft_sample->tlv.type = ATH_FFT_SAMPLE_ATH11K;
- fft_sample->tlv.length = __cpu_to_be16(length);
- fft_sample->tsf = __cpu_to_be32(search.timestamp);
- fft_sample->max_magnitude = __cpu_to_be16(search.peak_mag);
- fft_sample->max_index = FIELD_GET(SPECTRAL_FFT_REPORT_INFO0_PEAK_SIGNED_IDX,
- __le32_to_cpu(fft_report->info0));
- summary->inb_pwr_db >>= 1;
- fft_sample->rssi = __cpu_to_be16(summary->inb_pwr_db);
- fft_sample->noise = __cpu_to_be32(summary->meta.noise_floor[search.chain_idx]);
- freq = summary->meta.freq1;
- fft_sample->freq1 = __cpu_to_be16(freq);
- freq = summary->meta.freq2;
- fft_sample->freq2 = __cpu_to_be16(freq);
- /* If freq2 is available then the spectral scan results are fragmented
- * as primary and secondary
- */
- if (fragment_sample && freq) {
- if (!ar->spectral.is_primary)
- fft_sample->freq1 = cpu_to_be16(freq);
- /* We have to toggle the is_primary to handle the next report */
- ar->spectral.is_primary = !ar->spectral.is_primary;
- }
- ath11k_spectral_parse_fft(fft_sample->data, fft_report->bins, num_bins,
- ab->hw_params.spectral.fft_sz);
- fft_sample->max_exp = ath11k_spectral_get_max_exp(fft_sample->max_index,
- search.peak_mag,
- num_bins,
- fft_sample->data);
- if (ar->spectral.rfs_scan)
- relay_write(ar->spectral.rfs_scan, fft_sample,
- length + sizeof(struct fft_sample_tlv));
- return 0;
- }
- static int ath11k_spectral_process_data(struct ath11k *ar,
- struct ath11k_dbring_data *param)
- {
- struct ath11k_base *ab = ar->ab;
- struct spectral_tlv *tlv;
- struct spectral_summary_fft_report *summary = NULL;
- struct ath11k_spectral_summary_report summ_rpt;
- struct fft_sample_ath11k *fft_sample = NULL;
- u8 *data;
- u32 data_len, i;
- u8 sign, tag;
- int tlv_len, sample_sz;
- int ret;
- bool quit = false;
- spin_lock_bh(&ar->spectral.lock);
- if (!ar->spectral.enabled) {
- ret = -EINVAL;
- goto unlock;
- }
- sample_sz = sizeof(*fft_sample) + ATH11K_SPECTRAL_MAX_IB_BINS(ab);
- fft_sample = kmalloc(sample_sz, GFP_ATOMIC);
- if (!fft_sample) {
- ret = -ENOBUFS;
- goto unlock;
- }
- data = param->data;
- data_len = param->data_sz;
- i = 0;
- while (!quit && (i < data_len)) {
- if ((i + sizeof(*tlv)) > data_len) {
- ath11k_warn(ab, "failed to parse spectral tlv hdr at bytes %d\n",
- i);
- ret = -EINVAL;
- goto err;
- }
- tlv = (struct spectral_tlv *)&data[i];
- sign = FIELD_GET(SPECTRAL_TLV_HDR_SIGN,
- __le32_to_cpu(tlv->header));
- if (sign != ATH11K_SPECTRAL_SIGNATURE) {
- ath11k_warn(ab, "Invalid sign 0x%x at bytes %d\n",
- sign, i);
- ret = -EINVAL;
- goto err;
- }
- tlv_len = FIELD_GET(SPECTRAL_TLV_HDR_LEN,
- __le32_to_cpu(tlv->header));
- /* convert Dword into bytes */
- tlv_len *= ATH11K_SPECTRAL_DWORD_SIZE;
- if ((i + sizeof(*tlv) + tlv_len) > data_len) {
- ath11k_warn(ab, "failed to parse spectral tlv payload at bytes %d tlv_len:%d data_len:%d\n",
- i, tlv_len, data_len);
- ret = -EINVAL;
- goto err;
- }
- tag = FIELD_GET(SPECTRAL_TLV_HDR_TAG,
- __le32_to_cpu(tlv->header));
- switch (tag) {
- case ATH11K_SPECTRAL_TAG_SCAN_SUMMARY:
- /* HW bug in tlv length of summary report,
- * HW report 3 DWORD size but the data payload
- * is 4 DWORD size (16 bytes).
- * Need to remove this workaround once HW bug fixed
- */
- tlv_len = sizeof(*summary) - sizeof(*tlv) +
- ab->hw_params.spectral.summary_pad_sz;
- if (tlv_len < (sizeof(*summary) - sizeof(*tlv))) {
- ath11k_warn(ab, "failed to parse spectral summary at bytes %d tlv_len:%d\n",
- i, tlv_len);
- ret = -EINVAL;
- goto err;
- }
- ret = ath11k_dbring_validate_buffer(ar, data, tlv_len);
- if (ret) {
- ath11k_warn(ar->ab, "found magic value in spectral summary, dropping\n");
- goto err;
- }
- summary = (struct spectral_summary_fft_report *)tlv;
- ath11k_spectral_pull_summary(ar, ¶m->meta,
- summary, &summ_rpt);
- break;
- case ATH11K_SPECTRAL_TAG_SCAN_SEARCH:
- if (tlv_len < (sizeof(struct spectral_search_fft_report) -
- sizeof(*tlv))) {
- ath11k_warn(ab, "failed to parse spectral search fft at bytes %d\n",
- i);
- ret = -EINVAL;
- goto err;
- }
- memset(fft_sample, 0, sample_sz);
- ret = ath11k_spectral_process_fft(ar, &summ_rpt, tlv,
- fft_sample,
- data_len - i);
- if (ret) {
- ath11k_warn(ab, "failed to process spectral fft at bytes %d\n",
- i);
- goto err;
- }
- quit = true;
- break;
- }
- i += sizeof(*tlv) + tlv_len;
- }
- ret = 0;
- err:
- kfree(fft_sample);
- unlock:
- spin_unlock_bh(&ar->spectral.lock);
- return ret;
- }
- static int ath11k_spectral_ring_alloc(struct ath11k *ar,
- struct ath11k_dbring_cap *db_cap)
- {
- struct ath11k_spectral *sp = &ar->spectral;
- int ret;
- ret = ath11k_dbring_srng_setup(ar, &sp->rx_ring,
- 0, db_cap->min_elem);
- if (ret) {
- ath11k_warn(ar->ab, "failed to setup db ring\n");
- return ret;
- }
- ath11k_dbring_set_cfg(ar, &sp->rx_ring,
- ATH11K_SPECTRAL_NUM_RESP_PER_EVENT,
- ATH11K_SPECTRAL_EVENT_TIMEOUT_MS,
- ath11k_spectral_process_data);
- ret = ath11k_dbring_buf_setup(ar, &sp->rx_ring, db_cap);
- if (ret) {
- ath11k_warn(ar->ab, "failed to setup db ring buffer\n");
- goto srng_cleanup;
- }
- ret = ath11k_dbring_wmi_cfg_setup(ar, &sp->rx_ring,
- WMI_DIRECT_BUF_SPECTRAL);
- if (ret) {
- ath11k_warn(ar->ab, "failed to setup db ring cfg\n");
- goto buffer_cleanup;
- }
- return 0;
- buffer_cleanup:
- ath11k_dbring_buf_cleanup(ar, &sp->rx_ring);
- srng_cleanup:
- ath11k_dbring_srng_cleanup(ar, &sp->rx_ring);
- return ret;
- }
- static inline void ath11k_spectral_ring_free(struct ath11k *ar)
- {
- struct ath11k_spectral *sp = &ar->spectral;
- ath11k_dbring_srng_cleanup(ar, &sp->rx_ring);
- ath11k_dbring_buf_cleanup(ar, &sp->rx_ring);
- }
- static inline void ath11k_spectral_debug_unregister(struct ath11k *ar)
- {
- debugfs_remove(ar->spectral.scan_bins);
- ar->spectral.scan_bins = NULL;
- debugfs_remove(ar->spectral.scan_count);
- ar->spectral.scan_count = NULL;
- debugfs_remove(ar->spectral.scan_ctl);
- ar->spectral.scan_ctl = NULL;
- if (ar->spectral.rfs_scan) {
- relay_close(ar->spectral.rfs_scan);
- ar->spectral.rfs_scan = NULL;
- }
- }
- int ath11k_spectral_vif_stop(struct ath11k_vif *arvif)
- {
- if (!arvif->spectral_enabled)
- return 0;
- return ath11k_spectral_scan_config(arvif->ar, ATH11K_SPECTRAL_DISABLED);
- }
- void ath11k_spectral_reset_buffer(struct ath11k *ar)
- {
- if (!ar->spectral.enabled)
- return;
- if (ar->spectral.rfs_scan)
- relay_reset(ar->spectral.rfs_scan);
- }
- void ath11k_spectral_deinit(struct ath11k_base *ab)
- {
- struct ath11k *ar;
- struct ath11k_spectral *sp;
- int i;
- for (i = 0; i < ab->num_radios; i++) {
- ar = ab->pdevs[i].ar;
- sp = &ar->spectral;
- if (!sp->enabled)
- continue;
- mutex_lock(&ar->conf_mutex);
- ath11k_spectral_scan_config(ar, ATH11K_SPECTRAL_DISABLED);
- mutex_unlock(&ar->conf_mutex);
- spin_lock_bh(&sp->lock);
- sp->enabled = false;
- spin_unlock_bh(&sp->lock);
- ath11k_spectral_debug_unregister(ar);
- ath11k_spectral_ring_free(ar);
- }
- }
- static inline int ath11k_spectral_debug_register(struct ath11k *ar)
- {
- int ret;
- ar->spectral.rfs_scan = relay_open("spectral_scan",
- ar->debug.debugfs_pdev,
- ATH11K_SPECTRAL_SUB_BUFF_SIZE(ar->ab),
- ATH11K_SPECTRAL_NUM_SUB_BUF,
- &rfs_scan_cb, NULL);
- if (!ar->spectral.rfs_scan) {
- ath11k_warn(ar->ab, "failed to open relay in pdev %d\n",
- ar->pdev_idx);
- return -EINVAL;
- }
- ar->spectral.scan_ctl = debugfs_create_file("spectral_scan_ctl",
- 0600,
- ar->debug.debugfs_pdev, ar,
- &fops_scan_ctl);
- if (!ar->spectral.scan_ctl) {
- ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
- ar->pdev_idx);
- ret = -EINVAL;
- goto debug_unregister;
- }
- ar->spectral.scan_count = debugfs_create_file("spectral_count",
- 0600,
- ar->debug.debugfs_pdev, ar,
- &fops_scan_count);
- if (!ar->spectral.scan_count) {
- ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
- ar->pdev_idx);
- ret = -EINVAL;
- goto debug_unregister;
- }
- ar->spectral.scan_bins = debugfs_create_file("spectral_bins",
- 0600,
- ar->debug.debugfs_pdev, ar,
- &fops_scan_bins);
- if (!ar->spectral.scan_bins) {
- ath11k_warn(ar->ab, "failed to open debugfs in pdev %d\n",
- ar->pdev_idx);
- ret = -EINVAL;
- goto debug_unregister;
- }
- return 0;
- debug_unregister:
- ath11k_spectral_debug_unregister(ar);
- return ret;
- }
- int ath11k_spectral_init(struct ath11k_base *ab)
- {
- struct ath11k *ar;
- struct ath11k_spectral *sp;
- struct ath11k_dbring_cap db_cap;
- int ret;
- int i;
- if (!test_bit(WMI_TLV_SERVICE_FREQINFO_IN_METADATA,
- ab->wmi_ab.svc_map))
- return 0;
- if (!ab->hw_params.spectral.fft_sz)
- return 0;
- for (i = 0; i < ab->num_radios; i++) {
- ar = ab->pdevs[i].ar;
- sp = &ar->spectral;
- ret = ath11k_dbring_get_cap(ar->ab, ar->pdev_idx,
- WMI_DIRECT_BUF_SPECTRAL,
- &db_cap);
- if (ret)
- continue;
- idr_init(&sp->rx_ring.bufs_idr);
- spin_lock_init(&sp->rx_ring.idr_lock);
- spin_lock_init(&sp->lock);
- ret = ath11k_spectral_ring_alloc(ar, &db_cap);
- if (ret) {
- ath11k_warn(ab, "failed to init spectral ring for pdev %d\n",
- i);
- goto deinit;
- }
- spin_lock_bh(&sp->lock);
- sp->mode = ATH11K_SPECTRAL_DISABLED;
- sp->count = ATH11K_WMI_SPECTRAL_COUNT_DEFAULT;
- sp->fft_size = ATH11K_WMI_SPECTRAL_FFT_SIZE_DEFAULT;
- sp->enabled = true;
- spin_unlock_bh(&sp->lock);
- ret = ath11k_spectral_debug_register(ar);
- if (ret) {
- ath11k_warn(ab, "failed to register spectral for pdev %d\n",
- i);
- goto deinit;
- }
- }
- return 0;
- deinit:
- ath11k_spectral_deinit(ab);
- return ret;
- }
- enum ath11k_spectral_mode ath11k_spectral_get_mode(struct ath11k *ar)
- {
- if (ar->spectral.enabled)
- return ar->spectral.mode;
- else
- return ATH11K_SPECTRAL_DISABLED;
- }
- struct ath11k_dbring *ath11k_spectral_get_dbring(struct ath11k *ar)
- {
- if (ar->spectral.enabled)
- return &ar->spectral.rx_ring;
- else
- return NULL;
- }
|