162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2013 Qualcomm Atheros, Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/relay.h> 1862306a36Sopenharmony_ci#include <linux/random.h> 1962306a36Sopenharmony_ci#include "ath9k.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic s8 fix_rssi_inv_only(u8 rssi_val) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci if (rssi_val == 128) 2462306a36Sopenharmony_ci rssi_val = 0; 2562306a36Sopenharmony_ci return (s8) rssi_val; 2662306a36Sopenharmony_ci} 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic void ath_debug_send_fft_sample(struct ath_spec_scan_priv *spec_priv, 2962306a36Sopenharmony_ci struct fft_sample_tlv *fft_sample_tlv) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci int length; 3262306a36Sopenharmony_ci if (!spec_priv->rfs_chan_spec_scan) 3362306a36Sopenharmony_ci return; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci length = __be16_to_cpu(fft_sample_tlv->length) + 3662306a36Sopenharmony_ci sizeof(*fft_sample_tlv); 3762306a36Sopenharmony_ci relay_write(spec_priv->rfs_chan_spec_scan, fft_sample_tlv, length); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_citypedef int (ath_cmn_fft_idx_validator) (u8 *sample_end, int bytes_read); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int 4362306a36Sopenharmony_ciath_cmn_max_idx_verify_ht20_fft(u8 *sample_end, int bytes_read) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct ath_ht20_mag_info *mag_info; 4662306a36Sopenharmony_ci u8 *sample; 4762306a36Sopenharmony_ci u16 max_magnitude; 4862306a36Sopenharmony_ci u8 max_index; 4962306a36Sopenharmony_ci u8 max_exp; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* Sanity check so that we don't read outside the read 5262306a36Sopenharmony_ci * buffer 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN - 1) 5562306a36Sopenharmony_ci return -1; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci mag_info = (struct ath_ht20_mag_info *) (sample_end - 5862306a36Sopenharmony_ci sizeof(struct ath_ht20_mag_info) + 1); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci sample = sample_end - SPECTRAL_HT20_SAMPLE_LEN + 1; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci max_index = spectral_max_index_ht20(mag_info->all_bins); 6362306a36Sopenharmony_ci max_magnitude = spectral_max_magnitude(mag_info->all_bins); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci max_exp = mag_info->max_exp & 0xf; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* Don't try to read something outside the read buffer 6862306a36Sopenharmony_ci * in case of a missing byte (so bins[0] will be outside 6962306a36Sopenharmony_ci * the read buffer) 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN && max_index < 1) 7262306a36Sopenharmony_ci return -1; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if ((sample[max_index] & 0xf8) != ((max_magnitude >> max_exp) & 0xf8)) 7562306a36Sopenharmony_ci return -1; 7662306a36Sopenharmony_ci else 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic int 8162306a36Sopenharmony_ciath_cmn_max_idx_verify_ht20_40_fft(u8 *sample_end, int bytes_read) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct ath_ht20_40_mag_info *mag_info; 8462306a36Sopenharmony_ci u8 *sample; 8562306a36Sopenharmony_ci u16 lower_mag, upper_mag; 8662306a36Sopenharmony_ci u8 lower_max_index, upper_max_index; 8762306a36Sopenharmony_ci u8 max_exp; 8862306a36Sopenharmony_ci int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Sanity check so that we don't read outside the read 9162306a36Sopenharmony_ci * buffer 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN - 1) 9462306a36Sopenharmony_ci return -1; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci mag_info = (struct ath_ht20_40_mag_info *) (sample_end - 9762306a36Sopenharmony_ci sizeof(struct ath_ht20_40_mag_info) + 1); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci sample = sample_end - SPECTRAL_HT20_40_SAMPLE_LEN + 1; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci lower_mag = spectral_max_magnitude(mag_info->lower_bins); 10262306a36Sopenharmony_ci lower_max_index = spectral_max_index_ht40(mag_info->lower_bins); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci upper_mag = spectral_max_magnitude(mag_info->upper_bins); 10562306a36Sopenharmony_ci upper_max_index = spectral_max_index_ht40(mag_info->upper_bins); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci max_exp = mag_info->max_exp & 0xf; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* Don't try to read something outside the read buffer 11062306a36Sopenharmony_ci * in case of a missing byte (so bins[0] will be outside 11162306a36Sopenharmony_ci * the read buffer) 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_ci if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN && 11462306a36Sopenharmony_ci ((upper_max_index < 1) || (lower_max_index < 1))) 11562306a36Sopenharmony_ci return -1; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (((sample[upper_max_index + dc_pos] & 0xf8) != 11862306a36Sopenharmony_ci ((upper_mag >> max_exp) & 0xf8)) || 11962306a36Sopenharmony_ci ((sample[lower_max_index] & 0xf8) != 12062306a36Sopenharmony_ci ((lower_mag >> max_exp) & 0xf8))) 12162306a36Sopenharmony_ci return -1; 12262306a36Sopenharmony_ci else 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_citypedef int (ath_cmn_fft_sample_handler) (struct ath_rx_status *rs, 12762306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv, 12862306a36Sopenharmony_ci u8 *sample_buf, u64 tsf, u16 freq, int chan_type); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int 13162306a36Sopenharmony_ciath_cmn_process_ht20_fft(struct ath_rx_status *rs, 13262306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv, 13362306a36Sopenharmony_ci u8 *sample_buf, 13462306a36Sopenharmony_ci u64 tsf, u16 freq, int chan_type) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct fft_sample_ht20 fft_sample_20; 13762306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(spec_priv->ah); 13862306a36Sopenharmony_ci struct ath_hw *ah = spec_priv->ah; 13962306a36Sopenharmony_ci struct ath_ht20_mag_info *mag_info; 14062306a36Sopenharmony_ci struct fft_sample_tlv *tlv; 14162306a36Sopenharmony_ci int i = 0; 14262306a36Sopenharmony_ci int ret = 0; 14362306a36Sopenharmony_ci int dc_pos = SPECTRAL_HT20_NUM_BINS / 2; 14462306a36Sopenharmony_ci u16 magnitude, tmp_mag, length; 14562306a36Sopenharmony_ci u8 max_index, bitmap_w, max_exp; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv); 14862306a36Sopenharmony_ci fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20; 14962306a36Sopenharmony_ci fft_sample_20.tlv.length = __cpu_to_be16(length); 15062306a36Sopenharmony_ci fft_sample_20.freq = __cpu_to_be16(freq); 15162306a36Sopenharmony_ci fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); 15262306a36Sopenharmony_ci fft_sample_20.noise = ah->noise; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci mag_info = (struct ath_ht20_mag_info *) (sample_buf + 15562306a36Sopenharmony_ci SPECTRAL_HT20_NUM_BINS); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci magnitude = spectral_max_magnitude(mag_info->all_bins); 15862306a36Sopenharmony_ci fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci max_index = spectral_max_index_ht20(mag_info->all_bins); 16162306a36Sopenharmony_ci fft_sample_20.max_index = max_index; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci bitmap_w = spectral_bitmap_weight(mag_info->all_bins); 16462306a36Sopenharmony_ci fft_sample_20.bitmap_weight = bitmap_w; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci max_exp = mag_info->max_exp & 0xf; 16762306a36Sopenharmony_ci fft_sample_20.max_exp = max_exp; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci fft_sample_20.tsf = __cpu_to_be64(tsf); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci memcpy(fft_sample_20.data, sample_buf, SPECTRAL_HT20_NUM_BINS); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "FFT HT20 frame: max mag 0x%X," 17462306a36Sopenharmony_ci "max_mag_idx %i\n", 17562306a36Sopenharmony_ci magnitude >> max_exp, 17662306a36Sopenharmony_ci max_index); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if ((fft_sample_20.data[max_index] & 0xf8) != 17962306a36Sopenharmony_ci ((magnitude >> max_exp) & 0xf8)) { 18062306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n"); 18162306a36Sopenharmony_ci ret = -1; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci /* DC value (value in the middle) is the blind spot of the spectral 18562306a36Sopenharmony_ci * sample and invalid, interpolate it. 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci fft_sample_20.data[dc_pos] = (fft_sample_20.data[dc_pos + 1] + 18862306a36Sopenharmony_ci fft_sample_20.data[dc_pos - 1]) / 2; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Check if the maximum magnitude is indeed maximum, 19162306a36Sopenharmony_ci * also if the maximum value was at dc_pos, calculate 19262306a36Sopenharmony_ci * a new one (since value at dc_pos is invalid). 19362306a36Sopenharmony_ci */ 19462306a36Sopenharmony_ci if (max_index == dc_pos) { 19562306a36Sopenharmony_ci tmp_mag = 0; 19662306a36Sopenharmony_ci for (i = 0; i < dc_pos; i++) { 19762306a36Sopenharmony_ci if (fft_sample_20.data[i] > tmp_mag) { 19862306a36Sopenharmony_ci tmp_mag = fft_sample_20.data[i]; 19962306a36Sopenharmony_ci fft_sample_20.max_index = i; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci magnitude = tmp_mag << max_exp; 20462306a36Sopenharmony_ci fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 20762306a36Sopenharmony_ci "Calculated new lower max 0x%X at %i\n", 20862306a36Sopenharmony_ci tmp_mag, fft_sample_20.max_index); 20962306a36Sopenharmony_ci } else 21062306a36Sopenharmony_ci for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++) { 21162306a36Sopenharmony_ci if (fft_sample_20.data[i] == (magnitude >> max_exp)) 21262306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 21362306a36Sopenharmony_ci "Got max: 0x%X at index %i\n", 21462306a36Sopenharmony_ci fft_sample_20.data[i], i); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (fft_sample_20.data[i] > (magnitude >> max_exp)) { 21762306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 21862306a36Sopenharmony_ci "Got bin %i greater than max: 0x%X\n", 21962306a36Sopenharmony_ci i, fft_sample_20.data[i]); 22062306a36Sopenharmony_ci ret = -1; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (ret < 0) 22562306a36Sopenharmony_ci return ret; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci tlv = (struct fft_sample_tlv *)&fft_sample_20; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ath_debug_send_fft_sample(spec_priv, tlv); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic int 23562306a36Sopenharmony_ciath_cmn_process_ht20_40_fft(struct ath_rx_status *rs, 23662306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv, 23762306a36Sopenharmony_ci u8 *sample_buf, 23862306a36Sopenharmony_ci u64 tsf, u16 freq, int chan_type) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct fft_sample_ht20_40 fft_sample_40; 24162306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(spec_priv->ah); 24262306a36Sopenharmony_ci struct ath_hw *ah = spec_priv->ah; 24362306a36Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 24462306a36Sopenharmony_ci struct ath_ht20_40_mag_info *mag_info; 24562306a36Sopenharmony_ci struct fft_sample_tlv *tlv; 24662306a36Sopenharmony_ci int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2; 24762306a36Sopenharmony_ci int i = 0; 24862306a36Sopenharmony_ci int ret = 0; 24962306a36Sopenharmony_ci s16 ext_nf; 25062306a36Sopenharmony_ci u16 lower_mag, upper_mag, tmp_mag, length; 25162306a36Sopenharmony_ci s8 lower_rssi, upper_rssi; 25262306a36Sopenharmony_ci u8 lower_max_index, upper_max_index; 25362306a36Sopenharmony_ci u8 lower_bitmap_w, upper_bitmap_w, max_exp; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (caldata) 25662306a36Sopenharmony_ci ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan, 25762306a36Sopenharmony_ci caldata->nfCalHist[3].privNF); 25862306a36Sopenharmony_ci else 25962306a36Sopenharmony_ci ext_nf = ATH_DEFAULT_NOISE_FLOOR; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv); 26262306a36Sopenharmony_ci fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40; 26362306a36Sopenharmony_ci fft_sample_40.tlv.length = __cpu_to_be16(length); 26462306a36Sopenharmony_ci fft_sample_40.freq = __cpu_to_be16(freq); 26562306a36Sopenharmony_ci fft_sample_40.channel_type = chan_type; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (chan_type == NL80211_CHAN_HT40PLUS) { 26862306a36Sopenharmony_ci lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); 26962306a36Sopenharmony_ci upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci fft_sample_40.lower_noise = ah->noise; 27262306a36Sopenharmony_ci fft_sample_40.upper_noise = ext_nf; 27362306a36Sopenharmony_ci } else { 27462306a36Sopenharmony_ci lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]); 27562306a36Sopenharmony_ci upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci fft_sample_40.lower_noise = ext_nf; 27862306a36Sopenharmony_ci fft_sample_40.upper_noise = ah->noise; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci fft_sample_40.lower_rssi = lower_rssi; 28262306a36Sopenharmony_ci fft_sample_40.upper_rssi = upper_rssi; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci mag_info = (struct ath_ht20_40_mag_info *) (sample_buf + 28562306a36Sopenharmony_ci SPECTRAL_HT20_40_NUM_BINS); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci lower_mag = spectral_max_magnitude(mag_info->lower_bins); 28862306a36Sopenharmony_ci fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci upper_mag = spectral_max_magnitude(mag_info->upper_bins); 29162306a36Sopenharmony_ci fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci lower_max_index = spectral_max_index_ht40(mag_info->lower_bins); 29462306a36Sopenharmony_ci fft_sample_40.lower_max_index = lower_max_index; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci upper_max_index = spectral_max_index_ht40(mag_info->upper_bins); 29762306a36Sopenharmony_ci fft_sample_40.upper_max_index = upper_max_index; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins); 30062306a36Sopenharmony_ci fft_sample_40.lower_bitmap_weight = lower_bitmap_w; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins); 30362306a36Sopenharmony_ci fft_sample_40.upper_bitmap_weight = upper_bitmap_w; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci max_exp = mag_info->max_exp & 0xf; 30662306a36Sopenharmony_ci fft_sample_40.max_exp = max_exp; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci fft_sample_40.tsf = __cpu_to_be64(tsf); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci memcpy(fft_sample_40.data, sample_buf, SPECTRAL_HT20_40_NUM_BINS); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "FFT HT20/40 frame: lower mag 0x%X," 31362306a36Sopenharmony_ci "lower_mag_idx %i, upper mag 0x%X," 31462306a36Sopenharmony_ci "upper_mag_idx %i\n", 31562306a36Sopenharmony_ci lower_mag >> max_exp, 31662306a36Sopenharmony_ci lower_max_index, 31762306a36Sopenharmony_ci upper_mag >> max_exp, 31862306a36Sopenharmony_ci upper_max_index); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Check if we got the expected magnitude values at 32162306a36Sopenharmony_ci * the expected bins 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci if (((fft_sample_40.data[upper_max_index + dc_pos] & 0xf8) 32462306a36Sopenharmony_ci != ((upper_mag >> max_exp) & 0xf8)) || 32562306a36Sopenharmony_ci ((fft_sample_40.data[lower_max_index] & 0xf8) 32662306a36Sopenharmony_ci != ((lower_mag >> max_exp) & 0xf8))) { 32762306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n"); 32862306a36Sopenharmony_ci ret = -1; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* DC value (value in the middle) is the blind spot of the spectral 33262306a36Sopenharmony_ci * sample and invalid, interpolate it. 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_ci fft_sample_40.data[dc_pos] = (fft_sample_40.data[dc_pos + 1] + 33562306a36Sopenharmony_ci fft_sample_40.data[dc_pos - 1]) / 2; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Check if the maximum magnitudes are indeed maximum, 33862306a36Sopenharmony_ci * also if the maximum value was at dc_pos, calculate 33962306a36Sopenharmony_ci * a new one (since value at dc_pos is invalid). 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci if (lower_max_index == dc_pos) { 34262306a36Sopenharmony_ci tmp_mag = 0; 34362306a36Sopenharmony_ci for (i = 0; i < dc_pos; i++) { 34462306a36Sopenharmony_ci if (fft_sample_40.data[i] > tmp_mag) { 34562306a36Sopenharmony_ci tmp_mag = fft_sample_40.data[i]; 34662306a36Sopenharmony_ci fft_sample_40.lower_max_index = i; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci lower_mag = tmp_mag << max_exp; 35162306a36Sopenharmony_ci fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 35462306a36Sopenharmony_ci "Calculated new lower max 0x%X at %i\n", 35562306a36Sopenharmony_ci tmp_mag, fft_sample_40.lower_max_index); 35662306a36Sopenharmony_ci } else 35762306a36Sopenharmony_ci for (i = 0; i < dc_pos; i++) { 35862306a36Sopenharmony_ci if (fft_sample_40.data[i] == (lower_mag >> max_exp)) 35962306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 36062306a36Sopenharmony_ci "Got lower mag: 0x%X at index %i\n", 36162306a36Sopenharmony_ci fft_sample_40.data[i], i); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (fft_sample_40.data[i] > (lower_mag >> max_exp)) { 36462306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 36562306a36Sopenharmony_ci "Got lower bin %i higher than max: 0x%X\n", 36662306a36Sopenharmony_ci i, fft_sample_40.data[i]); 36762306a36Sopenharmony_ci ret = -1; 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (upper_max_index == dc_pos) { 37262306a36Sopenharmony_ci tmp_mag = 0; 37362306a36Sopenharmony_ci for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { 37462306a36Sopenharmony_ci if (fft_sample_40.data[i] > tmp_mag) { 37562306a36Sopenharmony_ci tmp_mag = fft_sample_40.data[i]; 37662306a36Sopenharmony_ci fft_sample_40.upper_max_index = i; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci upper_mag = tmp_mag << max_exp; 38062306a36Sopenharmony_ci fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 38362306a36Sopenharmony_ci "Calculated new upper max 0x%X at %i\n", 38462306a36Sopenharmony_ci tmp_mag, fft_sample_40.upper_max_index); 38562306a36Sopenharmony_ci } else 38662306a36Sopenharmony_ci for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { 38762306a36Sopenharmony_ci if (fft_sample_40.data[i] == (upper_mag >> max_exp)) 38862306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 38962306a36Sopenharmony_ci "Got upper mag: 0x%X at index %i\n", 39062306a36Sopenharmony_ci fft_sample_40.data[i], i); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (fft_sample_40.data[i] > (upper_mag >> max_exp)) { 39362306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 39462306a36Sopenharmony_ci "Got upper bin %i higher than max: 0x%X\n", 39562306a36Sopenharmony_ci i, fft_sample_40.data[i]); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ret = -1; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (ret < 0) 40262306a36Sopenharmony_ci return ret; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci tlv = (struct fft_sample_tlv *)&fft_sample_40; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci ath_debug_send_fft_sample(spec_priv, tlv); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic inline void 41262306a36Sopenharmony_ciath_cmn_copy_fft_frame(u8 *in, u8 *out, int sample_len, int sample_bytes) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci switch (sample_bytes - sample_len) { 41562306a36Sopenharmony_ci case -1: 41662306a36Sopenharmony_ci /* First byte missing */ 41762306a36Sopenharmony_ci memcpy(&out[1], in, 41862306a36Sopenharmony_ci sample_len - 1); 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci case 0: 42162306a36Sopenharmony_ci /* Length correct, nothing to do. */ 42262306a36Sopenharmony_ci memcpy(out, in, sample_len); 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci case 1: 42562306a36Sopenharmony_ci /* MAC added 2 extra bytes AND first byte 42662306a36Sopenharmony_ci * is missing. 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_ci memcpy(&out[1], in, 30); 42962306a36Sopenharmony_ci out[31] = in[31]; 43062306a36Sopenharmony_ci memcpy(&out[32], &in[33], 43162306a36Sopenharmony_ci sample_len - 32); 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci case 2: 43462306a36Sopenharmony_ci /* MAC added 2 extra bytes at bin 30 and 32, 43562306a36Sopenharmony_ci * remove them. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci memcpy(out, in, 30); 43862306a36Sopenharmony_ci out[30] = in[31]; 43962306a36Sopenharmony_ci memcpy(&out[31], &in[33], 44062306a36Sopenharmony_ci sample_len - 31); 44162306a36Sopenharmony_ci break; 44262306a36Sopenharmony_ci default: 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic int 44862306a36Sopenharmony_ciath_cmn_is_fft_buf_full(struct ath_spec_scan_priv *spec_priv) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci int i = 0; 45162306a36Sopenharmony_ci int ret = 0; 45262306a36Sopenharmony_ci struct rchan_buf *buf; 45362306a36Sopenharmony_ci struct rchan *rc = spec_priv->rfs_chan_spec_scan; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci for_each_possible_cpu(i) { 45662306a36Sopenharmony_ci if ((buf = *per_cpu_ptr(rc->buf, i))) { 45762306a36Sopenharmony_ci ret += relay_buf_full(buf); 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (ret) 46262306a36Sopenharmony_ci return 1; 46362306a36Sopenharmony_ci else 46462306a36Sopenharmony_ci return 0; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/* returns 1 if this was a spectral frame, even if not handled. */ 46862306a36Sopenharmony_ciint ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr, 46962306a36Sopenharmony_ci struct ath_rx_status *rs, u64 tsf) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci u8 sample_buf[SPECTRAL_SAMPLE_MAX_LEN] = {0}; 47262306a36Sopenharmony_ci struct ath_hw *ah = spec_priv->ah; 47362306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(spec_priv->ah); 47462306a36Sopenharmony_ci struct ath_softc *sc = (struct ath_softc *)common->priv; 47562306a36Sopenharmony_ci u8 num_bins, *vdata = (u8 *)hdr; 47662306a36Sopenharmony_ci struct ath_radar_info *radar_info; 47762306a36Sopenharmony_ci int len = rs->rs_datalen; 47862306a36Sopenharmony_ci int i; 47962306a36Sopenharmony_ci int got_slen = 0; 48062306a36Sopenharmony_ci u8 *sample_start; 48162306a36Sopenharmony_ci int sample_bytes = 0; 48262306a36Sopenharmony_ci int ret = 0; 48362306a36Sopenharmony_ci u16 fft_len, sample_len, freq = ah->curchan->chan->center_freq; 48462306a36Sopenharmony_ci enum nl80211_channel_type chan_type; 48562306a36Sopenharmony_ci ath_cmn_fft_idx_validator *fft_idx_validator; 48662306a36Sopenharmony_ci ath_cmn_fft_sample_handler *fft_handler; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer 48962306a36Sopenharmony_ci * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT 49062306a36Sopenharmony_ci * yet, but this is supposed to be possible as well. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_ci if (rs->rs_phyerr != ATH9K_PHYERR_RADAR && 49362306a36Sopenharmony_ci rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT && 49462306a36Sopenharmony_ci rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci /* check if spectral scan bit is set. This does not have to be checked 49862306a36Sopenharmony_ci * if received through a SPECTRAL phy error, but shouldn't hurt. 49962306a36Sopenharmony_ci */ 50062306a36Sopenharmony_ci radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; 50162306a36Sopenharmony_ci if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (!spec_priv->rfs_chan_spec_scan) 50562306a36Sopenharmony_ci return 1; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* Output buffers are full, no need to process anything 50862306a36Sopenharmony_ci * since there is no space to put the result anyway 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci ret = ath_cmn_is_fft_buf_full(spec_priv); 51162306a36Sopenharmony_ci if (ret == 1) { 51262306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "FFT report ignored, no space " 51362306a36Sopenharmony_ci "left on output buffers\n"); 51462306a36Sopenharmony_ci return 1; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci chan_type = cfg80211_get_chandef_type(&common->hw->conf.chandef); 51862306a36Sopenharmony_ci if ((chan_type == NL80211_CHAN_HT40MINUS) || 51962306a36Sopenharmony_ci (chan_type == NL80211_CHAN_HT40PLUS)) { 52062306a36Sopenharmony_ci fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; 52162306a36Sopenharmony_ci sample_len = SPECTRAL_HT20_40_SAMPLE_LEN; 52262306a36Sopenharmony_ci num_bins = SPECTRAL_HT20_40_NUM_BINS; 52362306a36Sopenharmony_ci fft_idx_validator = &ath_cmn_max_idx_verify_ht20_40_fft; 52462306a36Sopenharmony_ci fft_handler = &ath_cmn_process_ht20_40_fft; 52562306a36Sopenharmony_ci } else { 52662306a36Sopenharmony_ci fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; 52762306a36Sopenharmony_ci sample_len = SPECTRAL_HT20_SAMPLE_LEN; 52862306a36Sopenharmony_ci num_bins = SPECTRAL_HT20_NUM_BINS; 52962306a36Sopenharmony_ci fft_idx_validator = ath_cmn_max_idx_verify_ht20_fft; 53062306a36Sopenharmony_ci fft_handler = &ath_cmn_process_ht20_fft; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "Got radar dump bw_info: 0x%X," 53462306a36Sopenharmony_ci "len: %i fft_len: %i\n", 53562306a36Sopenharmony_ci radar_info->pulse_bw_info, 53662306a36Sopenharmony_ci len, 53762306a36Sopenharmony_ci fft_len); 53862306a36Sopenharmony_ci sample_start = vdata; 53962306a36Sopenharmony_ci for (i = 0; i < len - 2; i++) { 54062306a36Sopenharmony_ci sample_bytes++; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Only a single sample received, no need to look 54362306a36Sopenharmony_ci * for the sample's end, do the correction based 54462306a36Sopenharmony_ci * on the packet's length instead. Note that hw 54562306a36Sopenharmony_ci * will always put the radar_info structure on 54662306a36Sopenharmony_ci * the end. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci if (len <= fft_len + 2) { 54962306a36Sopenharmony_ci sample_bytes = len - sizeof(struct ath_radar_info); 55062306a36Sopenharmony_ci got_slen = 1; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Search for the end of the FFT frame between 55462306a36Sopenharmony_ci * sample_len - 1 and sample_len + 2. exp_max is 3 55562306a36Sopenharmony_ci * bits long and it's the only value on the last 55662306a36Sopenharmony_ci * byte of the frame so since it'll be smaller than 55762306a36Sopenharmony_ci * the next byte (the first bin of the next sample) 55862306a36Sopenharmony_ci * 90% of the time, we can use it as a separator. 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_ci if (vdata[i] <= 0x7 && sample_bytes >= sample_len - 1) { 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci /* Got a frame length within boundaries, there are 56362306a36Sopenharmony_ci * four scenarios here: 56462306a36Sopenharmony_ci * 56562306a36Sopenharmony_ci * a) sample_len -> We got the correct length 56662306a36Sopenharmony_ci * b) sample_len + 2 -> 2 bytes added around bin[31] 56762306a36Sopenharmony_ci * c) sample_len - 1 -> The first byte is missing 56862306a36Sopenharmony_ci * d) sample_len + 1 -> b + c at the same time 56962306a36Sopenharmony_ci * 57062306a36Sopenharmony_ci * When MAC adds 2 extra bytes, bin[31] and bin[32] 57162306a36Sopenharmony_ci * have the same value, so we can use that for further 57262306a36Sopenharmony_ci * verification in cases b and d. 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Did we go too far ? If so we couldn't determine 57662306a36Sopenharmony_ci * this sample's boundaries, discard any further 57762306a36Sopenharmony_ci * data 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ci if ((sample_bytes > sample_len + 2) || 58062306a36Sopenharmony_ci ((sample_bytes > sample_len) && 58162306a36Sopenharmony_ci (sample_start[31] != sample_start[32]))) 58262306a36Sopenharmony_ci break; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci /* See if we got a valid frame by checking the 58562306a36Sopenharmony_ci * consistency of mag_info fields. This is to 58662306a36Sopenharmony_ci * prevent from "fixing" a correct frame. 58762306a36Sopenharmony_ci * Failure is non-fatal, later frames may 58862306a36Sopenharmony_ci * be valid. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci if (!fft_idx_validator(&vdata[i], i)) { 59162306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 59262306a36Sopenharmony_ci "Found valid fft frame at %i\n", i); 59362306a36Sopenharmony_ci got_slen = 1; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* We expect 1 - 2 more bytes */ 59762306a36Sopenharmony_ci else if ((sample_start[31] == sample_start[32]) && 59862306a36Sopenharmony_ci (sample_bytes >= sample_len) && 59962306a36Sopenharmony_ci (sample_bytes < sample_len + 2) && 60062306a36Sopenharmony_ci (vdata[i + 1] <= 0x7)) 60162306a36Sopenharmony_ci continue; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* Try to distinguish cases a and c */ 60462306a36Sopenharmony_ci else if ((sample_bytes == sample_len - 1) && 60562306a36Sopenharmony_ci (vdata[i + 1] <= 0x7)) 60662306a36Sopenharmony_ci continue; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci got_slen = 1; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (got_slen) { 61262306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "FFT frame len: %i\n", 61362306a36Sopenharmony_ci sample_bytes); 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* Only try to fix a frame if it's the only one 61662306a36Sopenharmony_ci * on the report, else just skip it. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ci if (sample_bytes != sample_len && len <= fft_len + 2) { 61962306a36Sopenharmony_ci ath_cmn_copy_fft_frame(sample_start, 62062306a36Sopenharmony_ci sample_buf, sample_len, 62162306a36Sopenharmony_ci sample_bytes); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci ret = fft_handler(rs, spec_priv, sample_buf, 62462306a36Sopenharmony_ci tsf, freq, chan_type); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (ret == 0) 62762306a36Sopenharmony_ci RX_STAT_INC(sc, rx_spectral_sample_good); 62862306a36Sopenharmony_ci else 62962306a36Sopenharmony_ci RX_STAT_INC(sc, rx_spectral_sample_err); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Mix the received bins to the /dev/random 63462306a36Sopenharmony_ci * pool 63562306a36Sopenharmony_ci */ 63662306a36Sopenharmony_ci add_device_randomness(sample_buf, num_bins); 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* Process a normal frame */ 64062306a36Sopenharmony_ci if (sample_bytes == sample_len) { 64162306a36Sopenharmony_ci ret = fft_handler(rs, spec_priv, sample_start, 64262306a36Sopenharmony_ci tsf, freq, chan_type); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (ret == 0) 64562306a36Sopenharmony_ci RX_STAT_INC(sc, rx_spectral_sample_good); 64662306a36Sopenharmony_ci else 64762306a36Sopenharmony_ci RX_STAT_INC(sc, rx_spectral_sample_err); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* Mix the received bins to the /dev/random 65062306a36Sopenharmony_ci * pool 65162306a36Sopenharmony_ci */ 65262306a36Sopenharmony_ci add_device_randomness(sample_start, num_bins); 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* Short report processed, break out of the 65662306a36Sopenharmony_ci * loop. 65762306a36Sopenharmony_ci */ 65862306a36Sopenharmony_ci if (len <= fft_len + 2) 65962306a36Sopenharmony_ci return 1; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci sample_start = &vdata[i + 1]; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* -1 to grab sample_len -1, -2 since 66462306a36Sopenharmony_ci * they 'll get increased by one. In case 66562306a36Sopenharmony_ci * of failure try to recover by going byte 66662306a36Sopenharmony_ci * by byte instead. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci if (ret == 0) { 66962306a36Sopenharmony_ci i += num_bins - 2; 67062306a36Sopenharmony_ci sample_bytes = num_bins - 2; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci got_slen = 0; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci i -= num_bins - 2; 67762306a36Sopenharmony_ci if (len - i != sizeof(struct ath_radar_info)) 67862306a36Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "FFT report truncated" 67962306a36Sopenharmony_ci "(bytes left: %i)\n", 68062306a36Sopenharmony_ci len - i); 68162306a36Sopenharmony_ci return 1; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ciEXPORT_SYMBOL(ath_cmn_process_fft); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/*********************/ 68662306a36Sopenharmony_ci/* spectral_scan_ctl */ 68762306a36Sopenharmony_ci/*********************/ 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, 69062306a36Sopenharmony_ci size_t count, loff_t *ppos) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 69362306a36Sopenharmony_ci char *mode = ""; 69462306a36Sopenharmony_ci unsigned int len; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci switch (spec_priv->spectral_mode) { 69762306a36Sopenharmony_ci case SPECTRAL_DISABLED: 69862306a36Sopenharmony_ci mode = "disable"; 69962306a36Sopenharmony_ci break; 70062306a36Sopenharmony_ci case SPECTRAL_BACKGROUND: 70162306a36Sopenharmony_ci mode = "background"; 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci case SPECTRAL_CHANSCAN: 70462306a36Sopenharmony_ci mode = "chanscan"; 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci case SPECTRAL_MANUAL: 70762306a36Sopenharmony_ci mode = "manual"; 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci len = strlen(mode); 71162306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, mode, len); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_civoid ath9k_cmn_spectral_scan_trigger(struct ath_common *common, 71562306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct ath_hw *ah = spec_priv->ah; 71862306a36Sopenharmony_ci u32 rxfilter; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ATH9K_TX99)) 72162306a36Sopenharmony_ci return; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { 72462306a36Sopenharmony_ci ath_err(common, "spectrum analyzer not implemented on this hardware\n"); 72562306a36Sopenharmony_ci return; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (!spec_priv->spec_config.enabled) 72962306a36Sopenharmony_ci return; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci ath_ps_ops(common)->wakeup(common); 73262306a36Sopenharmony_ci rxfilter = ath9k_hw_getrxfilter(ah); 73362306a36Sopenharmony_ci ath9k_hw_setrxfilter(ah, rxfilter | 73462306a36Sopenharmony_ci ATH9K_RX_FILTER_PHYRADAR | 73562306a36Sopenharmony_ci ATH9K_RX_FILTER_PHYERR); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* TODO: usually this should not be neccesary, but for some reason 73862306a36Sopenharmony_ci * (or in some mode?) the trigger must be called after the 73962306a36Sopenharmony_ci * configuration, otherwise the register will have its values reset 74062306a36Sopenharmony_ci * (on my ar9220 to value 0x01002310) 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_ci ath9k_cmn_spectral_scan_config(common, spec_priv, spec_priv->spectral_mode); 74362306a36Sopenharmony_ci ath9k_hw_ops(ah)->spectral_scan_trigger(ah); 74462306a36Sopenharmony_ci ath_ps_ops(common)->restore(common); 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ciint ath9k_cmn_spectral_scan_config(struct ath_common *common, 74962306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv, 75062306a36Sopenharmony_ci enum spectral_mode spectral_mode) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct ath_hw *ah = spec_priv->ah; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { 75562306a36Sopenharmony_ci ath_err(common, "spectrum analyzer not implemented on this hardware\n"); 75662306a36Sopenharmony_ci return -1; 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci switch (spectral_mode) { 76062306a36Sopenharmony_ci case SPECTRAL_DISABLED: 76162306a36Sopenharmony_ci spec_priv->spec_config.enabled = 0; 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci case SPECTRAL_BACKGROUND: 76462306a36Sopenharmony_ci /* send endless samples. 76562306a36Sopenharmony_ci * TODO: is this really useful for "background"? 76662306a36Sopenharmony_ci */ 76762306a36Sopenharmony_ci spec_priv->spec_config.endless = 1; 76862306a36Sopenharmony_ci spec_priv->spec_config.enabled = 1; 76962306a36Sopenharmony_ci break; 77062306a36Sopenharmony_ci case SPECTRAL_CHANSCAN: 77162306a36Sopenharmony_ci case SPECTRAL_MANUAL: 77262306a36Sopenharmony_ci spec_priv->spec_config.endless = 0; 77362306a36Sopenharmony_ci spec_priv->spec_config.enabled = 1; 77462306a36Sopenharmony_ci break; 77562306a36Sopenharmony_ci default: 77662306a36Sopenharmony_ci return -1; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci ath_ps_ops(common)->wakeup(common); 78062306a36Sopenharmony_ci ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config); 78162306a36Sopenharmony_ci ath_ps_ops(common)->restore(common); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci spec_priv->spectral_mode = spectral_mode; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_spectral_scan_config); 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cistatic ssize_t write_file_spec_scan_ctl(struct file *file, 79062306a36Sopenharmony_ci const char __user *user_buf, 79162306a36Sopenharmony_ci size_t count, loff_t *ppos) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 79462306a36Sopenharmony_ci struct ath_common *common = ath9k_hw_common(spec_priv->ah); 79562306a36Sopenharmony_ci char buf[32]; 79662306a36Sopenharmony_ci ssize_t len; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_ATH9K_TX99)) 79962306a36Sopenharmony_ci return -EOPNOTSUPP; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci len = min(count, sizeof(buf) - 1); 80262306a36Sopenharmony_ci if (copy_from_user(buf, user_buf, len)) 80362306a36Sopenharmony_ci return -EFAULT; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci buf[len] = '\0'; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci if (strncmp("trigger", buf, 7) == 0) { 80862306a36Sopenharmony_ci ath9k_cmn_spectral_scan_trigger(common, spec_priv); 80962306a36Sopenharmony_ci } else if (strncmp("background", buf, 10) == 0) { 81062306a36Sopenharmony_ci ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_BACKGROUND); 81162306a36Sopenharmony_ci ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n"); 81262306a36Sopenharmony_ci } else if (strncmp("chanscan", buf, 8) == 0) { 81362306a36Sopenharmony_ci ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_CHANSCAN); 81462306a36Sopenharmony_ci ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n"); 81562306a36Sopenharmony_ci } else if (strncmp("manual", buf, 6) == 0) { 81662306a36Sopenharmony_ci ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_MANUAL); 81762306a36Sopenharmony_ci ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n"); 81862306a36Sopenharmony_ci } else if (strncmp("disable", buf, 7) == 0) { 81962306a36Sopenharmony_ci ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_DISABLED); 82062306a36Sopenharmony_ci ath_dbg(common, CONFIG, "spectral scan: disabled\n"); 82162306a36Sopenharmony_ci } else { 82262306a36Sopenharmony_ci return -EINVAL; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci return count; 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic const struct file_operations fops_spec_scan_ctl = { 82962306a36Sopenharmony_ci .read = read_file_spec_scan_ctl, 83062306a36Sopenharmony_ci .write = write_file_spec_scan_ctl, 83162306a36Sopenharmony_ci .open = simple_open, 83262306a36Sopenharmony_ci .owner = THIS_MODULE, 83362306a36Sopenharmony_ci .llseek = default_llseek, 83462306a36Sopenharmony_ci}; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci/*************************/ 83762306a36Sopenharmony_ci/* spectral_short_repeat */ 83862306a36Sopenharmony_ci/*************************/ 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic ssize_t read_file_spectral_short_repeat(struct file *file, 84162306a36Sopenharmony_ci char __user *user_buf, 84262306a36Sopenharmony_ci size_t count, loff_t *ppos) 84362306a36Sopenharmony_ci{ 84462306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 84562306a36Sopenharmony_ci char buf[32]; 84662306a36Sopenharmony_ci unsigned int len; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci len = sprintf(buf, "%d\n", spec_priv->spec_config.short_repeat); 84962306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, len); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic ssize_t write_file_spectral_short_repeat(struct file *file, 85362306a36Sopenharmony_ci const char __user *user_buf, 85462306a36Sopenharmony_ci size_t count, loff_t *ppos) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 85762306a36Sopenharmony_ci unsigned long val; 85862306a36Sopenharmony_ci ssize_t ret; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci ret = kstrtoul_from_user(user_buf, count, 0, &val); 86162306a36Sopenharmony_ci if (ret) 86262306a36Sopenharmony_ci return ret; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci if (val > 1) 86562306a36Sopenharmony_ci return -EINVAL; 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci spec_priv->spec_config.short_repeat = val; 86862306a36Sopenharmony_ci return count; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic const struct file_operations fops_spectral_short_repeat = { 87262306a36Sopenharmony_ci .read = read_file_spectral_short_repeat, 87362306a36Sopenharmony_ci .write = write_file_spectral_short_repeat, 87462306a36Sopenharmony_ci .open = simple_open, 87562306a36Sopenharmony_ci .owner = THIS_MODULE, 87662306a36Sopenharmony_ci .llseek = default_llseek, 87762306a36Sopenharmony_ci}; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci/******************/ 88062306a36Sopenharmony_ci/* spectral_count */ 88162306a36Sopenharmony_ci/******************/ 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic ssize_t read_file_spectral_count(struct file *file, 88462306a36Sopenharmony_ci char __user *user_buf, 88562306a36Sopenharmony_ci size_t count, loff_t *ppos) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 88862306a36Sopenharmony_ci char buf[32]; 88962306a36Sopenharmony_ci unsigned int len; 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci len = sprintf(buf, "%d\n", spec_priv->spec_config.count); 89262306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, len); 89362306a36Sopenharmony_ci} 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic ssize_t write_file_spectral_count(struct file *file, 89662306a36Sopenharmony_ci const char __user *user_buf, 89762306a36Sopenharmony_ci size_t count, loff_t *ppos) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 90062306a36Sopenharmony_ci unsigned long val; 90162306a36Sopenharmony_ci ssize_t ret; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci ret = kstrtoul_from_user(user_buf, count, 0, &val); 90462306a36Sopenharmony_ci if (ret) 90562306a36Sopenharmony_ci return ret; 90662306a36Sopenharmony_ci if (val > 255) 90762306a36Sopenharmony_ci return -EINVAL; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci spec_priv->spec_config.count = val; 91062306a36Sopenharmony_ci return count; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic const struct file_operations fops_spectral_count = { 91462306a36Sopenharmony_ci .read = read_file_spectral_count, 91562306a36Sopenharmony_ci .write = write_file_spectral_count, 91662306a36Sopenharmony_ci .open = simple_open, 91762306a36Sopenharmony_ci .owner = THIS_MODULE, 91862306a36Sopenharmony_ci .llseek = default_llseek, 91962306a36Sopenharmony_ci}; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/*******************/ 92262306a36Sopenharmony_ci/* spectral_period */ 92362306a36Sopenharmony_ci/*******************/ 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic ssize_t read_file_spectral_period(struct file *file, 92662306a36Sopenharmony_ci char __user *user_buf, 92762306a36Sopenharmony_ci size_t count, loff_t *ppos) 92862306a36Sopenharmony_ci{ 92962306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 93062306a36Sopenharmony_ci char buf[32]; 93162306a36Sopenharmony_ci unsigned int len; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci len = sprintf(buf, "%d\n", spec_priv->spec_config.period); 93462306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, len); 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_cistatic ssize_t write_file_spectral_period(struct file *file, 93862306a36Sopenharmony_ci const char __user *user_buf, 93962306a36Sopenharmony_ci size_t count, loff_t *ppos) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 94262306a36Sopenharmony_ci unsigned long val; 94362306a36Sopenharmony_ci ssize_t ret; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci ret = kstrtoul_from_user(user_buf, count, 0, &val); 94662306a36Sopenharmony_ci if (ret) 94762306a36Sopenharmony_ci return ret; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci if (val > 255) 95062306a36Sopenharmony_ci return -EINVAL; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci spec_priv->spec_config.period = val; 95362306a36Sopenharmony_ci return count; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic const struct file_operations fops_spectral_period = { 95762306a36Sopenharmony_ci .read = read_file_spectral_period, 95862306a36Sopenharmony_ci .write = write_file_spectral_period, 95962306a36Sopenharmony_ci .open = simple_open, 96062306a36Sopenharmony_ci .owner = THIS_MODULE, 96162306a36Sopenharmony_ci .llseek = default_llseek, 96262306a36Sopenharmony_ci}; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci/***********************/ 96562306a36Sopenharmony_ci/* spectral_fft_period */ 96662306a36Sopenharmony_ci/***********************/ 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_cistatic ssize_t read_file_spectral_fft_period(struct file *file, 96962306a36Sopenharmony_ci char __user *user_buf, 97062306a36Sopenharmony_ci size_t count, loff_t *ppos) 97162306a36Sopenharmony_ci{ 97262306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 97362306a36Sopenharmony_ci char buf[32]; 97462306a36Sopenharmony_ci unsigned int len; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci len = sprintf(buf, "%d\n", spec_priv->spec_config.fft_period); 97762306a36Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, len); 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_cistatic ssize_t write_file_spectral_fft_period(struct file *file, 98162306a36Sopenharmony_ci const char __user *user_buf, 98262306a36Sopenharmony_ci size_t count, loff_t *ppos) 98362306a36Sopenharmony_ci{ 98462306a36Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 98562306a36Sopenharmony_ci unsigned long val; 98662306a36Sopenharmony_ci ssize_t ret; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci ret = kstrtoul_from_user(user_buf, count, 0, &val); 98962306a36Sopenharmony_ci if (ret) 99062306a36Sopenharmony_ci return ret; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci if (val > 15) 99362306a36Sopenharmony_ci return -EINVAL; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci spec_priv->spec_config.fft_period = val; 99662306a36Sopenharmony_ci return count; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic const struct file_operations fops_spectral_fft_period = { 100062306a36Sopenharmony_ci .read = read_file_spectral_fft_period, 100162306a36Sopenharmony_ci .write = write_file_spectral_fft_period, 100262306a36Sopenharmony_ci .open = simple_open, 100362306a36Sopenharmony_ci .owner = THIS_MODULE, 100462306a36Sopenharmony_ci .llseek = default_llseek, 100562306a36Sopenharmony_ci}; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci/*******************/ 100862306a36Sopenharmony_ci/* Relay interface */ 100962306a36Sopenharmony_ci/*******************/ 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_cistatic struct dentry *create_buf_file_handler(const char *filename, 101262306a36Sopenharmony_ci struct dentry *parent, 101362306a36Sopenharmony_ci umode_t mode, 101462306a36Sopenharmony_ci struct rchan_buf *buf, 101562306a36Sopenharmony_ci int *is_global) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct dentry *buf_file; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci buf_file = debugfs_create_file(filename, mode, parent, buf, 102062306a36Sopenharmony_ci &relay_file_operations); 102162306a36Sopenharmony_ci if (IS_ERR(buf_file)) 102262306a36Sopenharmony_ci return NULL; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci *is_global = 1; 102562306a36Sopenharmony_ci return buf_file; 102662306a36Sopenharmony_ci} 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_cistatic int remove_buf_file_handler(struct dentry *dentry) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci debugfs_remove(dentry); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci return 0; 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic const struct rchan_callbacks rfs_spec_scan_cb = { 103662306a36Sopenharmony_ci .create_buf_file = create_buf_file_handler, 103762306a36Sopenharmony_ci .remove_buf_file = remove_buf_file_handler, 103862306a36Sopenharmony_ci}; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci/*********************/ 104162306a36Sopenharmony_ci/* Debug Init/Deinit */ 104262306a36Sopenharmony_ci/*********************/ 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_civoid ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci if (spec_priv->rfs_chan_spec_scan) { 104762306a36Sopenharmony_ci relay_close(spec_priv->rfs_chan_spec_scan); 104862306a36Sopenharmony_ci spec_priv->rfs_chan_spec_scan = NULL; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_spectral_deinit_debug); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_civoid ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv, 105462306a36Sopenharmony_ci struct dentry *debugfs_phy) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci spec_priv->rfs_chan_spec_scan = relay_open("spectral_scan", 105762306a36Sopenharmony_ci debugfs_phy, 105862306a36Sopenharmony_ci 1024, 256, &rfs_spec_scan_cb, 105962306a36Sopenharmony_ci NULL); 106062306a36Sopenharmony_ci if (!spec_priv->rfs_chan_spec_scan) 106162306a36Sopenharmony_ci return; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci debugfs_create_file("spectral_scan_ctl", 106462306a36Sopenharmony_ci 0600, 106562306a36Sopenharmony_ci debugfs_phy, spec_priv, 106662306a36Sopenharmony_ci &fops_spec_scan_ctl); 106762306a36Sopenharmony_ci debugfs_create_file("spectral_short_repeat", 106862306a36Sopenharmony_ci 0600, 106962306a36Sopenharmony_ci debugfs_phy, spec_priv, 107062306a36Sopenharmony_ci &fops_spectral_short_repeat); 107162306a36Sopenharmony_ci debugfs_create_file("spectral_count", 107262306a36Sopenharmony_ci 0600, 107362306a36Sopenharmony_ci debugfs_phy, spec_priv, 107462306a36Sopenharmony_ci &fops_spectral_count); 107562306a36Sopenharmony_ci debugfs_create_file("spectral_period", 107662306a36Sopenharmony_ci 0600, 107762306a36Sopenharmony_ci debugfs_phy, spec_priv, 107862306a36Sopenharmony_ci &fops_spectral_period); 107962306a36Sopenharmony_ci debugfs_create_file("spectral_fft_period", 108062306a36Sopenharmony_ci 0600, 108162306a36Sopenharmony_ci debugfs_phy, spec_priv, 108262306a36Sopenharmony_ci &fops_spectral_fft_period); 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_spectral_init_debug); 1085