brintos

brintos / linux-shallow public Read only

0
0
Text · 28.0 KiB · 79e0911 Raw
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, &param);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, &param->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