18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2013 Qualcomm Atheros, Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 58c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 68c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 108c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 118c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 128c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 148c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/relay.h> 188c2ecf20Sopenharmony_ci#include <linux/random.h> 198c2ecf20Sopenharmony_ci#include "ath9k.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic s8 fix_rssi_inv_only(u8 rssi_val) 228c2ecf20Sopenharmony_ci{ 238c2ecf20Sopenharmony_ci if (rssi_val == 128) 248c2ecf20Sopenharmony_ci rssi_val = 0; 258c2ecf20Sopenharmony_ci return (s8) rssi_val; 268c2ecf20Sopenharmony_ci} 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void ath_debug_send_fft_sample(struct ath_spec_scan_priv *spec_priv, 298c2ecf20Sopenharmony_ci struct fft_sample_tlv *fft_sample_tlv) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci int length; 328c2ecf20Sopenharmony_ci if (!spec_priv->rfs_chan_spec_scan) 338c2ecf20Sopenharmony_ci return; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci length = __be16_to_cpu(fft_sample_tlv->length) + 368c2ecf20Sopenharmony_ci sizeof(*fft_sample_tlv); 378c2ecf20Sopenharmony_ci relay_write(spec_priv->rfs_chan_spec_scan, fft_sample_tlv, length); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_citypedef int (ath_cmn_fft_idx_validator) (u8 *sample_end, int bytes_read); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int 438c2ecf20Sopenharmony_ciath_cmn_max_idx_verify_ht20_fft(u8 *sample_end, int bytes_read) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct ath_ht20_mag_info *mag_info; 468c2ecf20Sopenharmony_ci u8 *sample; 478c2ecf20Sopenharmony_ci u16 max_magnitude; 488c2ecf20Sopenharmony_ci u8 max_index; 498c2ecf20Sopenharmony_ci u8 max_exp; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci /* Sanity check so that we don't read outside the read 528c2ecf20Sopenharmony_ci * buffer 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN - 1) 558c2ecf20Sopenharmony_ci return -1; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci mag_info = (struct ath_ht20_mag_info *) (sample_end - 588c2ecf20Sopenharmony_ci sizeof(struct ath_ht20_mag_info) + 1); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci sample = sample_end - SPECTRAL_HT20_SAMPLE_LEN + 1; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci max_index = spectral_max_index_ht20(mag_info->all_bins); 638c2ecf20Sopenharmony_ci max_magnitude = spectral_max_magnitude(mag_info->all_bins); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci max_exp = mag_info->max_exp & 0xf; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* Don't try to read something outside the read buffer 688c2ecf20Sopenharmony_ci * in case of a missing byte (so bins[0] will be outside 698c2ecf20Sopenharmony_ci * the read buffer) 708c2ecf20Sopenharmony_ci */ 718c2ecf20Sopenharmony_ci if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN && max_index < 1) 728c2ecf20Sopenharmony_ci return -1; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if ((sample[max_index] & 0xf8) != ((max_magnitude >> max_exp) & 0xf8)) 758c2ecf20Sopenharmony_ci return -1; 768c2ecf20Sopenharmony_ci else 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int 818c2ecf20Sopenharmony_ciath_cmn_max_idx_verify_ht20_40_fft(u8 *sample_end, int bytes_read) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci struct ath_ht20_40_mag_info *mag_info; 848c2ecf20Sopenharmony_ci u8 *sample; 858c2ecf20Sopenharmony_ci u16 lower_mag, upper_mag; 868c2ecf20Sopenharmony_ci u8 lower_max_index, upper_max_index; 878c2ecf20Sopenharmony_ci u8 max_exp; 888c2ecf20Sopenharmony_ci int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Sanity check so that we don't read outside the read 918c2ecf20Sopenharmony_ci * buffer 928c2ecf20Sopenharmony_ci */ 938c2ecf20Sopenharmony_ci if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN - 1) 948c2ecf20Sopenharmony_ci return -1; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci mag_info = (struct ath_ht20_40_mag_info *) (sample_end - 978c2ecf20Sopenharmony_ci sizeof(struct ath_ht20_40_mag_info) + 1); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci sample = sample_end - SPECTRAL_HT20_40_SAMPLE_LEN + 1; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci lower_mag = spectral_max_magnitude(mag_info->lower_bins); 1028c2ecf20Sopenharmony_ci lower_max_index = spectral_max_index_ht40(mag_info->lower_bins); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci upper_mag = spectral_max_magnitude(mag_info->upper_bins); 1058c2ecf20Sopenharmony_ci upper_max_index = spectral_max_index_ht40(mag_info->upper_bins); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci max_exp = mag_info->max_exp & 0xf; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* Don't try to read something outside the read buffer 1108c2ecf20Sopenharmony_ci * in case of a missing byte (so bins[0] will be outside 1118c2ecf20Sopenharmony_ci * the read buffer) 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN && 1148c2ecf20Sopenharmony_ci ((upper_max_index < 1) || (lower_max_index < 1))) 1158c2ecf20Sopenharmony_ci return -1; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (((sample[upper_max_index + dc_pos] & 0xf8) != 1188c2ecf20Sopenharmony_ci ((upper_mag >> max_exp) & 0xf8)) || 1198c2ecf20Sopenharmony_ci ((sample[lower_max_index] & 0xf8) != 1208c2ecf20Sopenharmony_ci ((lower_mag >> max_exp) & 0xf8))) 1218c2ecf20Sopenharmony_ci return -1; 1228c2ecf20Sopenharmony_ci else 1238c2ecf20Sopenharmony_ci return 0; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_citypedef int (ath_cmn_fft_sample_handler) (struct ath_rx_status *rs, 1278c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv, 1288c2ecf20Sopenharmony_ci u8 *sample_buf, u64 tsf, u16 freq, int chan_type); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int 1318c2ecf20Sopenharmony_ciath_cmn_process_ht20_fft(struct ath_rx_status *rs, 1328c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv, 1338c2ecf20Sopenharmony_ci u8 *sample_buf, 1348c2ecf20Sopenharmony_ci u64 tsf, u16 freq, int chan_type) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci struct fft_sample_ht20 fft_sample_20; 1378c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(spec_priv->ah); 1388c2ecf20Sopenharmony_ci struct ath_hw *ah = spec_priv->ah; 1398c2ecf20Sopenharmony_ci struct ath_ht20_mag_info *mag_info; 1408c2ecf20Sopenharmony_ci struct fft_sample_tlv *tlv; 1418c2ecf20Sopenharmony_ci int i = 0; 1428c2ecf20Sopenharmony_ci int ret = 0; 1438c2ecf20Sopenharmony_ci int dc_pos = SPECTRAL_HT20_NUM_BINS / 2; 1448c2ecf20Sopenharmony_ci u16 magnitude, tmp_mag, length; 1458c2ecf20Sopenharmony_ci u8 max_index, bitmap_w, max_exp; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv); 1488c2ecf20Sopenharmony_ci fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20; 1498c2ecf20Sopenharmony_ci fft_sample_20.tlv.length = __cpu_to_be16(length); 1508c2ecf20Sopenharmony_ci fft_sample_20.freq = __cpu_to_be16(freq); 1518c2ecf20Sopenharmony_ci fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); 1528c2ecf20Sopenharmony_ci fft_sample_20.noise = ah->noise; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci mag_info = (struct ath_ht20_mag_info *) (sample_buf + 1558c2ecf20Sopenharmony_ci SPECTRAL_HT20_NUM_BINS); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci magnitude = spectral_max_magnitude(mag_info->all_bins); 1588c2ecf20Sopenharmony_ci fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci max_index = spectral_max_index_ht20(mag_info->all_bins); 1618c2ecf20Sopenharmony_ci fft_sample_20.max_index = max_index; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci bitmap_w = spectral_bitmap_weight(mag_info->all_bins); 1648c2ecf20Sopenharmony_ci fft_sample_20.bitmap_weight = bitmap_w; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci max_exp = mag_info->max_exp & 0xf; 1678c2ecf20Sopenharmony_ci fft_sample_20.max_exp = max_exp; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci fft_sample_20.tsf = __cpu_to_be64(tsf); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci memcpy(fft_sample_20.data, sample_buf, SPECTRAL_HT20_NUM_BINS); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "FFT HT20 frame: max mag 0x%X," 1748c2ecf20Sopenharmony_ci "max_mag_idx %i\n", 1758c2ecf20Sopenharmony_ci magnitude >> max_exp, 1768c2ecf20Sopenharmony_ci max_index); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if ((fft_sample_20.data[max_index] & 0xf8) != 1798c2ecf20Sopenharmony_ci ((magnitude >> max_exp) & 0xf8)) { 1808c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n"); 1818c2ecf20Sopenharmony_ci ret = -1; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* DC value (value in the middle) is the blind spot of the spectral 1858c2ecf20Sopenharmony_ci * sample and invalid, interpolate it. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci fft_sample_20.data[dc_pos] = (fft_sample_20.data[dc_pos + 1] + 1888c2ecf20Sopenharmony_ci fft_sample_20.data[dc_pos - 1]) / 2; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* Check if the maximum magnitude is indeed maximum, 1918c2ecf20Sopenharmony_ci * also if the maximum value was at dc_pos, calculate 1928c2ecf20Sopenharmony_ci * a new one (since value at dc_pos is invalid). 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci if (max_index == dc_pos) { 1958c2ecf20Sopenharmony_ci tmp_mag = 0; 1968c2ecf20Sopenharmony_ci for (i = 0; i < dc_pos; i++) { 1978c2ecf20Sopenharmony_ci if (fft_sample_20.data[i] > tmp_mag) { 1988c2ecf20Sopenharmony_ci tmp_mag = fft_sample_20.data[i]; 1998c2ecf20Sopenharmony_ci fft_sample_20.max_index = i; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci magnitude = tmp_mag << max_exp; 2048c2ecf20Sopenharmony_ci fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 2078c2ecf20Sopenharmony_ci "Calculated new lower max 0x%X at %i\n", 2088c2ecf20Sopenharmony_ci tmp_mag, fft_sample_20.max_index); 2098c2ecf20Sopenharmony_ci } else 2108c2ecf20Sopenharmony_ci for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++) { 2118c2ecf20Sopenharmony_ci if (fft_sample_20.data[i] == (magnitude >> max_exp)) 2128c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 2138c2ecf20Sopenharmony_ci "Got max: 0x%X at index %i\n", 2148c2ecf20Sopenharmony_ci fft_sample_20.data[i], i); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci if (fft_sample_20.data[i] > (magnitude >> max_exp)) { 2178c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 2188c2ecf20Sopenharmony_ci "Got bin %i greater than max: 0x%X\n", 2198c2ecf20Sopenharmony_ci i, fft_sample_20.data[i]); 2208c2ecf20Sopenharmony_ci ret = -1; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (ret < 0) 2258c2ecf20Sopenharmony_ci return ret; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci tlv = (struct fft_sample_tlv *)&fft_sample_20; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ath_debug_send_fft_sample(spec_priv, tlv); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic int 2358c2ecf20Sopenharmony_ciath_cmn_process_ht20_40_fft(struct ath_rx_status *rs, 2368c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv, 2378c2ecf20Sopenharmony_ci u8 *sample_buf, 2388c2ecf20Sopenharmony_ci u64 tsf, u16 freq, int chan_type) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci struct fft_sample_ht20_40 fft_sample_40; 2418c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(spec_priv->ah); 2428c2ecf20Sopenharmony_ci struct ath_hw *ah = spec_priv->ah; 2438c2ecf20Sopenharmony_ci struct ath9k_hw_cal_data *caldata = ah->caldata; 2448c2ecf20Sopenharmony_ci struct ath_ht20_40_mag_info *mag_info; 2458c2ecf20Sopenharmony_ci struct fft_sample_tlv *tlv; 2468c2ecf20Sopenharmony_ci int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2; 2478c2ecf20Sopenharmony_ci int i = 0; 2488c2ecf20Sopenharmony_ci int ret = 0; 2498c2ecf20Sopenharmony_ci s16 ext_nf; 2508c2ecf20Sopenharmony_ci u16 lower_mag, upper_mag, tmp_mag, length; 2518c2ecf20Sopenharmony_ci s8 lower_rssi, upper_rssi; 2528c2ecf20Sopenharmony_ci u8 lower_max_index, upper_max_index; 2538c2ecf20Sopenharmony_ci u8 lower_bitmap_w, upper_bitmap_w, max_exp; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (caldata) 2568c2ecf20Sopenharmony_ci ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan, 2578c2ecf20Sopenharmony_ci caldata->nfCalHist[3].privNF); 2588c2ecf20Sopenharmony_ci else 2598c2ecf20Sopenharmony_ci ext_nf = ATH_DEFAULT_NOISE_FLOOR; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv); 2628c2ecf20Sopenharmony_ci fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40; 2638c2ecf20Sopenharmony_ci fft_sample_40.tlv.length = __cpu_to_be16(length); 2648c2ecf20Sopenharmony_ci fft_sample_40.freq = __cpu_to_be16(freq); 2658c2ecf20Sopenharmony_ci fft_sample_40.channel_type = chan_type; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (chan_type == NL80211_CHAN_HT40PLUS) { 2688c2ecf20Sopenharmony_ci lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); 2698c2ecf20Sopenharmony_ci upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci fft_sample_40.lower_noise = ah->noise; 2728c2ecf20Sopenharmony_ci fft_sample_40.upper_noise = ext_nf; 2738c2ecf20Sopenharmony_ci } else { 2748c2ecf20Sopenharmony_ci lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]); 2758c2ecf20Sopenharmony_ci upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci fft_sample_40.lower_noise = ext_nf; 2788c2ecf20Sopenharmony_ci fft_sample_40.upper_noise = ah->noise; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci fft_sample_40.lower_rssi = lower_rssi; 2828c2ecf20Sopenharmony_ci fft_sample_40.upper_rssi = upper_rssi; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci mag_info = (struct ath_ht20_40_mag_info *) (sample_buf + 2858c2ecf20Sopenharmony_ci SPECTRAL_HT20_40_NUM_BINS); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci lower_mag = spectral_max_magnitude(mag_info->lower_bins); 2888c2ecf20Sopenharmony_ci fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci upper_mag = spectral_max_magnitude(mag_info->upper_bins); 2918c2ecf20Sopenharmony_ci fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci lower_max_index = spectral_max_index_ht40(mag_info->lower_bins); 2948c2ecf20Sopenharmony_ci fft_sample_40.lower_max_index = lower_max_index; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci upper_max_index = spectral_max_index_ht40(mag_info->upper_bins); 2978c2ecf20Sopenharmony_ci fft_sample_40.upper_max_index = upper_max_index; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins); 3008c2ecf20Sopenharmony_ci fft_sample_40.lower_bitmap_weight = lower_bitmap_w; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins); 3038c2ecf20Sopenharmony_ci fft_sample_40.upper_bitmap_weight = upper_bitmap_w; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci max_exp = mag_info->max_exp & 0xf; 3068c2ecf20Sopenharmony_ci fft_sample_40.max_exp = max_exp; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci fft_sample_40.tsf = __cpu_to_be64(tsf); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci memcpy(fft_sample_40.data, sample_buf, SPECTRAL_HT20_40_NUM_BINS); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "FFT HT20/40 frame: lower mag 0x%X," 3138c2ecf20Sopenharmony_ci "lower_mag_idx %i, upper mag 0x%X," 3148c2ecf20Sopenharmony_ci "upper_mag_idx %i\n", 3158c2ecf20Sopenharmony_ci lower_mag >> max_exp, 3168c2ecf20Sopenharmony_ci lower_max_index, 3178c2ecf20Sopenharmony_ci upper_mag >> max_exp, 3188c2ecf20Sopenharmony_ci upper_max_index); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* Check if we got the expected magnitude values at 3218c2ecf20Sopenharmony_ci * the expected bins 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci if (((fft_sample_40.data[upper_max_index + dc_pos] & 0xf8) 3248c2ecf20Sopenharmony_ci != ((upper_mag >> max_exp) & 0xf8)) || 3258c2ecf20Sopenharmony_ci ((fft_sample_40.data[lower_max_index] & 0xf8) 3268c2ecf20Sopenharmony_ci != ((lower_mag >> max_exp) & 0xf8))) { 3278c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n"); 3288c2ecf20Sopenharmony_ci ret = -1; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* DC value (value in the middle) is the blind spot of the spectral 3328c2ecf20Sopenharmony_ci * sample and invalid, interpolate it. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_ci fft_sample_40.data[dc_pos] = (fft_sample_40.data[dc_pos + 1] + 3358c2ecf20Sopenharmony_ci fft_sample_40.data[dc_pos - 1]) / 2; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Check if the maximum magnitudes are indeed maximum, 3388c2ecf20Sopenharmony_ci * also if the maximum value was at dc_pos, calculate 3398c2ecf20Sopenharmony_ci * a new one (since value at dc_pos is invalid). 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci if (lower_max_index == dc_pos) { 3428c2ecf20Sopenharmony_ci tmp_mag = 0; 3438c2ecf20Sopenharmony_ci for (i = 0; i < dc_pos; i++) { 3448c2ecf20Sopenharmony_ci if (fft_sample_40.data[i] > tmp_mag) { 3458c2ecf20Sopenharmony_ci tmp_mag = fft_sample_40.data[i]; 3468c2ecf20Sopenharmony_ci fft_sample_40.lower_max_index = i; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci lower_mag = tmp_mag << max_exp; 3518c2ecf20Sopenharmony_ci fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 3548c2ecf20Sopenharmony_ci "Calculated new lower max 0x%X at %i\n", 3558c2ecf20Sopenharmony_ci tmp_mag, fft_sample_40.lower_max_index); 3568c2ecf20Sopenharmony_ci } else 3578c2ecf20Sopenharmony_ci for (i = 0; i < dc_pos; i++) { 3588c2ecf20Sopenharmony_ci if (fft_sample_40.data[i] == (lower_mag >> max_exp)) 3598c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 3608c2ecf20Sopenharmony_ci "Got lower mag: 0x%X at index %i\n", 3618c2ecf20Sopenharmony_ci fft_sample_40.data[i], i); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (fft_sample_40.data[i] > (lower_mag >> max_exp)) { 3648c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 3658c2ecf20Sopenharmony_ci "Got lower bin %i higher than max: 0x%X\n", 3668c2ecf20Sopenharmony_ci i, fft_sample_40.data[i]); 3678c2ecf20Sopenharmony_ci ret = -1; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (upper_max_index == dc_pos) { 3728c2ecf20Sopenharmony_ci tmp_mag = 0; 3738c2ecf20Sopenharmony_ci for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { 3748c2ecf20Sopenharmony_ci if (fft_sample_40.data[i] > tmp_mag) { 3758c2ecf20Sopenharmony_ci tmp_mag = fft_sample_40.data[i]; 3768c2ecf20Sopenharmony_ci fft_sample_40.upper_max_index = i; 3778c2ecf20Sopenharmony_ci } 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci upper_mag = tmp_mag << max_exp; 3808c2ecf20Sopenharmony_ci fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 3838c2ecf20Sopenharmony_ci "Calculated new upper max 0x%X at %i\n", 3848c2ecf20Sopenharmony_ci tmp_mag, fft_sample_40.upper_max_index); 3858c2ecf20Sopenharmony_ci } else 3868c2ecf20Sopenharmony_ci for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { 3878c2ecf20Sopenharmony_ci if (fft_sample_40.data[i] == (upper_mag >> max_exp)) 3888c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 3898c2ecf20Sopenharmony_ci "Got upper mag: 0x%X at index %i\n", 3908c2ecf20Sopenharmony_ci fft_sample_40.data[i], i); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (fft_sample_40.data[i] > (upper_mag >> max_exp)) { 3938c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 3948c2ecf20Sopenharmony_ci "Got upper bin %i higher than max: 0x%X\n", 3958c2ecf20Sopenharmony_ci i, fft_sample_40.data[i]); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci ret = -1; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (ret < 0) 4028c2ecf20Sopenharmony_ci return ret; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci tlv = (struct fft_sample_tlv *)&fft_sample_40; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci ath_debug_send_fft_sample(spec_priv, tlv); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic inline void 4128c2ecf20Sopenharmony_ciath_cmn_copy_fft_frame(u8 *in, u8 *out, int sample_len, int sample_bytes) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci switch (sample_bytes - sample_len) { 4158c2ecf20Sopenharmony_ci case -1: 4168c2ecf20Sopenharmony_ci /* First byte missing */ 4178c2ecf20Sopenharmony_ci memcpy(&out[1], in, 4188c2ecf20Sopenharmony_ci sample_len - 1); 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci case 0: 4218c2ecf20Sopenharmony_ci /* Length correct, nothing to do. */ 4228c2ecf20Sopenharmony_ci memcpy(out, in, sample_len); 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci case 1: 4258c2ecf20Sopenharmony_ci /* MAC added 2 extra bytes AND first byte 4268c2ecf20Sopenharmony_ci * is missing. 4278c2ecf20Sopenharmony_ci */ 4288c2ecf20Sopenharmony_ci memcpy(&out[1], in, 30); 4298c2ecf20Sopenharmony_ci out[31] = in[31]; 4308c2ecf20Sopenharmony_ci memcpy(&out[32], &in[33], 4318c2ecf20Sopenharmony_ci sample_len - 32); 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci case 2: 4348c2ecf20Sopenharmony_ci /* MAC added 2 extra bytes at bin 30 and 32, 4358c2ecf20Sopenharmony_ci * remove them. 4368c2ecf20Sopenharmony_ci */ 4378c2ecf20Sopenharmony_ci memcpy(out, in, 30); 4388c2ecf20Sopenharmony_ci out[30] = in[31]; 4398c2ecf20Sopenharmony_ci memcpy(&out[31], &in[33], 4408c2ecf20Sopenharmony_ci sample_len - 31); 4418c2ecf20Sopenharmony_ci break; 4428c2ecf20Sopenharmony_ci default: 4438c2ecf20Sopenharmony_ci break; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int 4488c2ecf20Sopenharmony_ciath_cmn_is_fft_buf_full(struct ath_spec_scan_priv *spec_priv) 4498c2ecf20Sopenharmony_ci{ 4508c2ecf20Sopenharmony_ci int i = 0; 4518c2ecf20Sopenharmony_ci int ret = 0; 4528c2ecf20Sopenharmony_ci struct rchan_buf *buf; 4538c2ecf20Sopenharmony_ci struct rchan *rc = spec_priv->rfs_chan_spec_scan; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci for_each_possible_cpu(i) { 4568c2ecf20Sopenharmony_ci if ((buf = *per_cpu_ptr(rc->buf, i))) { 4578c2ecf20Sopenharmony_ci ret += relay_buf_full(buf); 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (ret) 4628c2ecf20Sopenharmony_ci return 1; 4638c2ecf20Sopenharmony_ci else 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/* returns 1 if this was a spectral frame, even if not handled. */ 4688c2ecf20Sopenharmony_ciint ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr, 4698c2ecf20Sopenharmony_ci struct ath_rx_status *rs, u64 tsf) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci u8 sample_buf[SPECTRAL_SAMPLE_MAX_LEN] = {0}; 4728c2ecf20Sopenharmony_ci struct ath_hw *ah = spec_priv->ah; 4738c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(spec_priv->ah); 4748c2ecf20Sopenharmony_ci struct ath_softc *sc = (struct ath_softc *)common->priv; 4758c2ecf20Sopenharmony_ci u8 num_bins, *vdata = (u8 *)hdr; 4768c2ecf20Sopenharmony_ci struct ath_radar_info *radar_info; 4778c2ecf20Sopenharmony_ci int len = rs->rs_datalen; 4788c2ecf20Sopenharmony_ci int i; 4798c2ecf20Sopenharmony_ci int got_slen = 0; 4808c2ecf20Sopenharmony_ci u8 *sample_start; 4818c2ecf20Sopenharmony_ci int sample_bytes = 0; 4828c2ecf20Sopenharmony_ci int ret = 0; 4838c2ecf20Sopenharmony_ci u16 fft_len, sample_len, freq = ah->curchan->chan->center_freq; 4848c2ecf20Sopenharmony_ci enum nl80211_channel_type chan_type; 4858c2ecf20Sopenharmony_ci ath_cmn_fft_idx_validator *fft_idx_validator; 4868c2ecf20Sopenharmony_ci ath_cmn_fft_sample_handler *fft_handler; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer 4898c2ecf20Sopenharmony_ci * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT 4908c2ecf20Sopenharmony_ci * yet, but this is supposed to be possible as well. 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ci if (rs->rs_phyerr != ATH9K_PHYERR_RADAR && 4938c2ecf20Sopenharmony_ci rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT && 4948c2ecf20Sopenharmony_ci rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* check if spectral scan bit is set. This does not have to be checked 4988c2ecf20Sopenharmony_ci * if received through a SPECTRAL phy error, but shouldn't hurt. 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_ci radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; 5018c2ecf20Sopenharmony_ci if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (!spec_priv->rfs_chan_spec_scan) 5058c2ecf20Sopenharmony_ci return 1; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci /* Output buffers are full, no need to process anything 5088c2ecf20Sopenharmony_ci * since there is no space to put the result anyway 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci ret = ath_cmn_is_fft_buf_full(spec_priv); 5118c2ecf20Sopenharmony_ci if (ret == 1) { 5128c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "FFT report ignored, no space " 5138c2ecf20Sopenharmony_ci "left on output buffers\n"); 5148c2ecf20Sopenharmony_ci return 1; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci chan_type = cfg80211_get_chandef_type(&common->hw->conf.chandef); 5188c2ecf20Sopenharmony_ci if ((chan_type == NL80211_CHAN_HT40MINUS) || 5198c2ecf20Sopenharmony_ci (chan_type == NL80211_CHAN_HT40PLUS)) { 5208c2ecf20Sopenharmony_ci fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; 5218c2ecf20Sopenharmony_ci sample_len = SPECTRAL_HT20_40_SAMPLE_LEN; 5228c2ecf20Sopenharmony_ci num_bins = SPECTRAL_HT20_40_NUM_BINS; 5238c2ecf20Sopenharmony_ci fft_idx_validator = &ath_cmn_max_idx_verify_ht20_40_fft; 5248c2ecf20Sopenharmony_ci fft_handler = &ath_cmn_process_ht20_40_fft; 5258c2ecf20Sopenharmony_ci } else { 5268c2ecf20Sopenharmony_ci fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; 5278c2ecf20Sopenharmony_ci sample_len = SPECTRAL_HT20_SAMPLE_LEN; 5288c2ecf20Sopenharmony_ci num_bins = SPECTRAL_HT20_NUM_BINS; 5298c2ecf20Sopenharmony_ci fft_idx_validator = ath_cmn_max_idx_verify_ht20_fft; 5308c2ecf20Sopenharmony_ci fft_handler = &ath_cmn_process_ht20_fft; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "Got radar dump bw_info: 0x%X," 5348c2ecf20Sopenharmony_ci "len: %i fft_len: %i\n", 5358c2ecf20Sopenharmony_ci radar_info->pulse_bw_info, 5368c2ecf20Sopenharmony_ci len, 5378c2ecf20Sopenharmony_ci fft_len); 5388c2ecf20Sopenharmony_ci sample_start = vdata; 5398c2ecf20Sopenharmony_ci for (i = 0; i < len - 2; i++) { 5408c2ecf20Sopenharmony_ci sample_bytes++; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci /* Only a single sample received, no need to look 5438c2ecf20Sopenharmony_ci * for the sample's end, do the correction based 5448c2ecf20Sopenharmony_ci * on the packet's length instead. Note that hw 5458c2ecf20Sopenharmony_ci * will always put the radar_info structure on 5468c2ecf20Sopenharmony_ci * the end. 5478c2ecf20Sopenharmony_ci */ 5488c2ecf20Sopenharmony_ci if (len <= fft_len + 2) { 5498c2ecf20Sopenharmony_ci sample_bytes = len - sizeof(struct ath_radar_info); 5508c2ecf20Sopenharmony_ci got_slen = 1; 5518c2ecf20Sopenharmony_ci } 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* Search for the end of the FFT frame between 5548c2ecf20Sopenharmony_ci * sample_len - 1 and sample_len + 2. exp_max is 3 5558c2ecf20Sopenharmony_ci * bits long and it's the only value on the last 5568c2ecf20Sopenharmony_ci * byte of the frame so since it'll be smaller than 5578c2ecf20Sopenharmony_ci * the next byte (the first bin of the next sample) 5588c2ecf20Sopenharmony_ci * 90% of the time, we can use it as a separator. 5598c2ecf20Sopenharmony_ci */ 5608c2ecf20Sopenharmony_ci if (vdata[i] <= 0x7 && sample_bytes >= sample_len - 1) { 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* Got a frame length within boundaries, there are 5638c2ecf20Sopenharmony_ci * four scenarios here: 5648c2ecf20Sopenharmony_ci * 5658c2ecf20Sopenharmony_ci * a) sample_len -> We got the correct length 5668c2ecf20Sopenharmony_ci * b) sample_len + 2 -> 2 bytes added around bin[31] 5678c2ecf20Sopenharmony_ci * c) sample_len - 1 -> The first byte is missing 5688c2ecf20Sopenharmony_ci * d) sample_len + 1 -> b + c at the same time 5698c2ecf20Sopenharmony_ci * 5708c2ecf20Sopenharmony_ci * When MAC adds 2 extra bytes, bin[31] and bin[32] 5718c2ecf20Sopenharmony_ci * have the same value, so we can use that for further 5728c2ecf20Sopenharmony_ci * verification in cases b and d. 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci /* Did we go too far ? If so we couldn't determine 5768c2ecf20Sopenharmony_ci * this sample's boundaries, discard any further 5778c2ecf20Sopenharmony_ci * data 5788c2ecf20Sopenharmony_ci */ 5798c2ecf20Sopenharmony_ci if ((sample_bytes > sample_len + 2) || 5808c2ecf20Sopenharmony_ci ((sample_bytes > sample_len) && 5818c2ecf20Sopenharmony_ci (sample_start[31] != sample_start[32]))) 5828c2ecf20Sopenharmony_ci break; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* See if we got a valid frame by checking the 5858c2ecf20Sopenharmony_ci * consistency of mag_info fields. This is to 5868c2ecf20Sopenharmony_ci * prevent from "fixing" a correct frame. 5878c2ecf20Sopenharmony_ci * Failure is non-fatal, later frames may 5888c2ecf20Sopenharmony_ci * be valid. 5898c2ecf20Sopenharmony_ci */ 5908c2ecf20Sopenharmony_ci if (!fft_idx_validator(&vdata[i], i)) { 5918c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, 5928c2ecf20Sopenharmony_ci "Found valid fft frame at %i\n", i); 5938c2ecf20Sopenharmony_ci got_slen = 1; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci /* We expect 1 - 2 more bytes */ 5978c2ecf20Sopenharmony_ci else if ((sample_start[31] == sample_start[32]) && 5988c2ecf20Sopenharmony_ci (sample_bytes >= sample_len) && 5998c2ecf20Sopenharmony_ci (sample_bytes < sample_len + 2) && 6008c2ecf20Sopenharmony_ci (vdata[i + 1] <= 0x7)) 6018c2ecf20Sopenharmony_ci continue; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* Try to distinguish cases a and c */ 6048c2ecf20Sopenharmony_ci else if ((sample_bytes == sample_len - 1) && 6058c2ecf20Sopenharmony_ci (vdata[i + 1] <= 0x7)) 6068c2ecf20Sopenharmony_ci continue; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci got_slen = 1; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (got_slen) { 6128c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "FFT frame len: %i\n", 6138c2ecf20Sopenharmony_ci sample_bytes); 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci /* Only try to fix a frame if it's the only one 6168c2ecf20Sopenharmony_ci * on the report, else just skip it. 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_ci if (sample_bytes != sample_len && len <= fft_len + 2) { 6198c2ecf20Sopenharmony_ci ath_cmn_copy_fft_frame(sample_start, 6208c2ecf20Sopenharmony_ci sample_buf, sample_len, 6218c2ecf20Sopenharmony_ci sample_bytes); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci ret = fft_handler(rs, spec_priv, sample_buf, 6248c2ecf20Sopenharmony_ci tsf, freq, chan_type); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (ret == 0) 6278c2ecf20Sopenharmony_ci RX_STAT_INC(sc, rx_spectral_sample_good); 6288c2ecf20Sopenharmony_ci else 6298c2ecf20Sopenharmony_ci RX_STAT_INC(sc, rx_spectral_sample_err); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci /* Mix the received bins to the /dev/random 6348c2ecf20Sopenharmony_ci * pool 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci add_device_randomness(sample_buf, num_bins); 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* Process a normal frame */ 6408c2ecf20Sopenharmony_ci if (sample_bytes == sample_len) { 6418c2ecf20Sopenharmony_ci ret = fft_handler(rs, spec_priv, sample_start, 6428c2ecf20Sopenharmony_ci tsf, freq, chan_type); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci if (ret == 0) 6458c2ecf20Sopenharmony_ci RX_STAT_INC(sc, rx_spectral_sample_good); 6468c2ecf20Sopenharmony_ci else 6478c2ecf20Sopenharmony_ci RX_STAT_INC(sc, rx_spectral_sample_err); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci /* Mix the received bins to the /dev/random 6508c2ecf20Sopenharmony_ci * pool 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_ci add_device_randomness(sample_start, num_bins); 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* Short report processed, break out of the 6568c2ecf20Sopenharmony_ci * loop. 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_ci if (len <= fft_len + 2) 6598c2ecf20Sopenharmony_ci return 1; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci sample_start = &vdata[i + 1]; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci /* -1 to grab sample_len -1, -2 since 6648c2ecf20Sopenharmony_ci * they 'll get increased by one. In case 6658c2ecf20Sopenharmony_ci * of failure try to recover by going byte 6668c2ecf20Sopenharmony_ci * by byte instead. 6678c2ecf20Sopenharmony_ci */ 6688c2ecf20Sopenharmony_ci if (ret == 0) { 6698c2ecf20Sopenharmony_ci i += num_bins - 2; 6708c2ecf20Sopenharmony_ci sample_bytes = num_bins - 2; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci got_slen = 0; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci i -= num_bins - 2; 6778c2ecf20Sopenharmony_ci if (len - i != sizeof(struct ath_radar_info)) 6788c2ecf20Sopenharmony_ci ath_dbg(common, SPECTRAL_SCAN, "FFT report truncated" 6798c2ecf20Sopenharmony_ci "(bytes left: %i)\n", 6808c2ecf20Sopenharmony_ci len - i); 6818c2ecf20Sopenharmony_ci return 1; 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath_cmn_process_fft); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci/*********************/ 6868c2ecf20Sopenharmony_ci/* spectral_scan_ctl */ 6878c2ecf20Sopenharmony_ci/*********************/ 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, 6908c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6918c2ecf20Sopenharmony_ci{ 6928c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 6938c2ecf20Sopenharmony_ci char *mode = ""; 6948c2ecf20Sopenharmony_ci unsigned int len; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci switch (spec_priv->spectral_mode) { 6978c2ecf20Sopenharmony_ci case SPECTRAL_DISABLED: 6988c2ecf20Sopenharmony_ci mode = "disable"; 6998c2ecf20Sopenharmony_ci break; 7008c2ecf20Sopenharmony_ci case SPECTRAL_BACKGROUND: 7018c2ecf20Sopenharmony_ci mode = "background"; 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci case SPECTRAL_CHANSCAN: 7048c2ecf20Sopenharmony_ci mode = "chanscan"; 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci case SPECTRAL_MANUAL: 7078c2ecf20Sopenharmony_ci mode = "manual"; 7088c2ecf20Sopenharmony_ci break; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci len = strlen(mode); 7118c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, mode, len); 7128c2ecf20Sopenharmony_ci} 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_civoid ath9k_cmn_spectral_scan_trigger(struct ath_common *common, 7158c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv) 7168c2ecf20Sopenharmony_ci{ 7178c2ecf20Sopenharmony_ci struct ath_hw *ah = spec_priv->ah; 7188c2ecf20Sopenharmony_ci u32 rxfilter; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ATH9K_TX99)) 7218c2ecf20Sopenharmony_ci return; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { 7248c2ecf20Sopenharmony_ci ath_err(common, "spectrum analyzer not implemented on this hardware\n"); 7258c2ecf20Sopenharmony_ci return; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (!spec_priv->spec_config.enabled) 7298c2ecf20Sopenharmony_ci return; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci ath_ps_ops(common)->wakeup(common); 7328c2ecf20Sopenharmony_ci rxfilter = ath9k_hw_getrxfilter(ah); 7338c2ecf20Sopenharmony_ci ath9k_hw_setrxfilter(ah, rxfilter | 7348c2ecf20Sopenharmony_ci ATH9K_RX_FILTER_PHYRADAR | 7358c2ecf20Sopenharmony_ci ATH9K_RX_FILTER_PHYERR); 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci /* TODO: usually this should not be neccesary, but for some reason 7388c2ecf20Sopenharmony_ci * (or in some mode?) the trigger must be called after the 7398c2ecf20Sopenharmony_ci * configuration, otherwise the register will have its values reset 7408c2ecf20Sopenharmony_ci * (on my ar9220 to value 0x01002310) 7418c2ecf20Sopenharmony_ci */ 7428c2ecf20Sopenharmony_ci ath9k_cmn_spectral_scan_config(common, spec_priv, spec_priv->spectral_mode); 7438c2ecf20Sopenharmony_ci ath9k_hw_ops(ah)->spectral_scan_trigger(ah); 7448c2ecf20Sopenharmony_ci ath_ps_ops(common)->restore(common); 7458c2ecf20Sopenharmony_ci} 7468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ciint ath9k_cmn_spectral_scan_config(struct ath_common *common, 7498c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv, 7508c2ecf20Sopenharmony_ci enum spectral_mode spectral_mode) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci struct ath_hw *ah = spec_priv->ah; 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { 7558c2ecf20Sopenharmony_ci ath_err(common, "spectrum analyzer not implemented on this hardware\n"); 7568c2ecf20Sopenharmony_ci return -1; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci switch (spectral_mode) { 7608c2ecf20Sopenharmony_ci case SPECTRAL_DISABLED: 7618c2ecf20Sopenharmony_ci spec_priv->spec_config.enabled = 0; 7628c2ecf20Sopenharmony_ci break; 7638c2ecf20Sopenharmony_ci case SPECTRAL_BACKGROUND: 7648c2ecf20Sopenharmony_ci /* send endless samples. 7658c2ecf20Sopenharmony_ci * TODO: is this really useful for "background"? 7668c2ecf20Sopenharmony_ci */ 7678c2ecf20Sopenharmony_ci spec_priv->spec_config.endless = 1; 7688c2ecf20Sopenharmony_ci spec_priv->spec_config.enabled = 1; 7698c2ecf20Sopenharmony_ci break; 7708c2ecf20Sopenharmony_ci case SPECTRAL_CHANSCAN: 7718c2ecf20Sopenharmony_ci case SPECTRAL_MANUAL: 7728c2ecf20Sopenharmony_ci spec_priv->spec_config.endless = 0; 7738c2ecf20Sopenharmony_ci spec_priv->spec_config.enabled = 1; 7748c2ecf20Sopenharmony_ci break; 7758c2ecf20Sopenharmony_ci default: 7768c2ecf20Sopenharmony_ci return -1; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci ath_ps_ops(common)->wakeup(common); 7808c2ecf20Sopenharmony_ci ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config); 7818c2ecf20Sopenharmony_ci ath_ps_ops(common)->restore(common); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci spec_priv->spectral_mode = spectral_mode; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci return 0; 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_spectral_scan_config); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cistatic ssize_t write_file_spec_scan_ctl(struct file *file, 7908c2ecf20Sopenharmony_ci const char __user *user_buf, 7918c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 7948c2ecf20Sopenharmony_ci struct ath_common *common = ath9k_hw_common(spec_priv->ah); 7958c2ecf20Sopenharmony_ci char buf[32]; 7968c2ecf20Sopenharmony_ci ssize_t len; 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_ATH9K_TX99)) 7998c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci len = min(count, sizeof(buf) - 1); 8028c2ecf20Sopenharmony_ci if (copy_from_user(buf, user_buf, len)) 8038c2ecf20Sopenharmony_ci return -EFAULT; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci buf[len] = '\0'; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (strncmp("trigger", buf, 7) == 0) { 8088c2ecf20Sopenharmony_ci ath9k_cmn_spectral_scan_trigger(common, spec_priv); 8098c2ecf20Sopenharmony_ci } else if (strncmp("background", buf, 10) == 0) { 8108c2ecf20Sopenharmony_ci ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_BACKGROUND); 8118c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n"); 8128c2ecf20Sopenharmony_ci } else if (strncmp("chanscan", buf, 8) == 0) { 8138c2ecf20Sopenharmony_ci ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_CHANSCAN); 8148c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n"); 8158c2ecf20Sopenharmony_ci } else if (strncmp("manual", buf, 6) == 0) { 8168c2ecf20Sopenharmony_ci ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_MANUAL); 8178c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n"); 8188c2ecf20Sopenharmony_ci } else if (strncmp("disable", buf, 7) == 0) { 8198c2ecf20Sopenharmony_ci ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_DISABLED); 8208c2ecf20Sopenharmony_ci ath_dbg(common, CONFIG, "spectral scan: disabled\n"); 8218c2ecf20Sopenharmony_ci } else { 8228c2ecf20Sopenharmony_ci return -EINVAL; 8238c2ecf20Sopenharmony_ci } 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci return count; 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic const struct file_operations fops_spec_scan_ctl = { 8298c2ecf20Sopenharmony_ci .read = read_file_spec_scan_ctl, 8308c2ecf20Sopenharmony_ci .write = write_file_spec_scan_ctl, 8318c2ecf20Sopenharmony_ci .open = simple_open, 8328c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 8338c2ecf20Sopenharmony_ci .llseek = default_llseek, 8348c2ecf20Sopenharmony_ci}; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci/*************************/ 8378c2ecf20Sopenharmony_ci/* spectral_short_repeat */ 8388c2ecf20Sopenharmony_ci/*************************/ 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_cistatic ssize_t read_file_spectral_short_repeat(struct file *file, 8418c2ecf20Sopenharmony_ci char __user *user_buf, 8428c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 8458c2ecf20Sopenharmony_ci char buf[32]; 8468c2ecf20Sopenharmony_ci unsigned int len; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci len = sprintf(buf, "%d\n", spec_priv->spec_config.short_repeat); 8498c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, len); 8508c2ecf20Sopenharmony_ci} 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_cistatic ssize_t write_file_spectral_short_repeat(struct file *file, 8538c2ecf20Sopenharmony_ci const char __user *user_buf, 8548c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 8558c2ecf20Sopenharmony_ci{ 8568c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 8578c2ecf20Sopenharmony_ci unsigned long val; 8588c2ecf20Sopenharmony_ci char buf[32]; 8598c2ecf20Sopenharmony_ci ssize_t len; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci len = min(count, sizeof(buf) - 1); 8628c2ecf20Sopenharmony_ci if (copy_from_user(buf, user_buf, len)) 8638c2ecf20Sopenharmony_ci return -EFAULT; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci buf[len] = '\0'; 8668c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val)) 8678c2ecf20Sopenharmony_ci return -EINVAL; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (val > 1) 8708c2ecf20Sopenharmony_ci return -EINVAL; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci spec_priv->spec_config.short_repeat = val; 8738c2ecf20Sopenharmony_ci return count; 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic const struct file_operations fops_spectral_short_repeat = { 8778c2ecf20Sopenharmony_ci .read = read_file_spectral_short_repeat, 8788c2ecf20Sopenharmony_ci .write = write_file_spectral_short_repeat, 8798c2ecf20Sopenharmony_ci .open = simple_open, 8808c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 8818c2ecf20Sopenharmony_ci .llseek = default_llseek, 8828c2ecf20Sopenharmony_ci}; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci/******************/ 8858c2ecf20Sopenharmony_ci/* spectral_count */ 8868c2ecf20Sopenharmony_ci/******************/ 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cistatic ssize_t read_file_spectral_count(struct file *file, 8898c2ecf20Sopenharmony_ci char __user *user_buf, 8908c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 8938c2ecf20Sopenharmony_ci char buf[32]; 8948c2ecf20Sopenharmony_ci unsigned int len; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci len = sprintf(buf, "%d\n", spec_priv->spec_config.count); 8978c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, len); 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic ssize_t write_file_spectral_count(struct file *file, 9018c2ecf20Sopenharmony_ci const char __user *user_buf, 9028c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 9058c2ecf20Sopenharmony_ci unsigned long val; 9068c2ecf20Sopenharmony_ci char buf[32]; 9078c2ecf20Sopenharmony_ci ssize_t len; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci len = min(count, sizeof(buf) - 1); 9108c2ecf20Sopenharmony_ci if (copy_from_user(buf, user_buf, len)) 9118c2ecf20Sopenharmony_ci return -EFAULT; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci buf[len] = '\0'; 9148c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val)) 9158c2ecf20Sopenharmony_ci return -EINVAL; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (val > 255) 9188c2ecf20Sopenharmony_ci return -EINVAL; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci spec_priv->spec_config.count = val; 9218c2ecf20Sopenharmony_ci return count; 9228c2ecf20Sopenharmony_ci} 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_cistatic const struct file_operations fops_spectral_count = { 9258c2ecf20Sopenharmony_ci .read = read_file_spectral_count, 9268c2ecf20Sopenharmony_ci .write = write_file_spectral_count, 9278c2ecf20Sopenharmony_ci .open = simple_open, 9288c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9298c2ecf20Sopenharmony_ci .llseek = default_llseek, 9308c2ecf20Sopenharmony_ci}; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci/*******************/ 9338c2ecf20Sopenharmony_ci/* spectral_period */ 9348c2ecf20Sopenharmony_ci/*******************/ 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_cistatic ssize_t read_file_spectral_period(struct file *file, 9378c2ecf20Sopenharmony_ci char __user *user_buf, 9388c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 9398c2ecf20Sopenharmony_ci{ 9408c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 9418c2ecf20Sopenharmony_ci char buf[32]; 9428c2ecf20Sopenharmony_ci unsigned int len; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci len = sprintf(buf, "%d\n", spec_priv->spec_config.period); 9458c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, len); 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic ssize_t write_file_spectral_period(struct file *file, 9498c2ecf20Sopenharmony_ci const char __user *user_buf, 9508c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 9518c2ecf20Sopenharmony_ci{ 9528c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 9538c2ecf20Sopenharmony_ci unsigned long val; 9548c2ecf20Sopenharmony_ci char buf[32]; 9558c2ecf20Sopenharmony_ci ssize_t len; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci len = min(count, sizeof(buf) - 1); 9588c2ecf20Sopenharmony_ci if (copy_from_user(buf, user_buf, len)) 9598c2ecf20Sopenharmony_ci return -EFAULT; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci buf[len] = '\0'; 9628c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val)) 9638c2ecf20Sopenharmony_ci return -EINVAL; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci if (val > 255) 9668c2ecf20Sopenharmony_ci return -EINVAL; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci spec_priv->spec_config.period = val; 9698c2ecf20Sopenharmony_ci return count; 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_cistatic const struct file_operations fops_spectral_period = { 9738c2ecf20Sopenharmony_ci .read = read_file_spectral_period, 9748c2ecf20Sopenharmony_ci .write = write_file_spectral_period, 9758c2ecf20Sopenharmony_ci .open = simple_open, 9768c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9778c2ecf20Sopenharmony_ci .llseek = default_llseek, 9788c2ecf20Sopenharmony_ci}; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci/***********************/ 9818c2ecf20Sopenharmony_ci/* spectral_fft_period */ 9828c2ecf20Sopenharmony_ci/***********************/ 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_cistatic ssize_t read_file_spectral_fft_period(struct file *file, 9858c2ecf20Sopenharmony_ci char __user *user_buf, 9868c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 9898c2ecf20Sopenharmony_ci char buf[32]; 9908c2ecf20Sopenharmony_ci unsigned int len; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci len = sprintf(buf, "%d\n", spec_priv->spec_config.fft_period); 9938c2ecf20Sopenharmony_ci return simple_read_from_buffer(user_buf, count, ppos, buf, len); 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic ssize_t write_file_spectral_fft_period(struct file *file, 9978c2ecf20Sopenharmony_ci const char __user *user_buf, 9988c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci struct ath_spec_scan_priv *spec_priv = file->private_data; 10018c2ecf20Sopenharmony_ci unsigned long val; 10028c2ecf20Sopenharmony_ci char buf[32]; 10038c2ecf20Sopenharmony_ci ssize_t len; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci len = min(count, sizeof(buf) - 1); 10068c2ecf20Sopenharmony_ci if (copy_from_user(buf, user_buf, len)) 10078c2ecf20Sopenharmony_ci return -EFAULT; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci buf[len] = '\0'; 10108c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val)) 10118c2ecf20Sopenharmony_ci return -EINVAL; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci if (val > 15) 10148c2ecf20Sopenharmony_ci return -EINVAL; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci spec_priv->spec_config.fft_period = val; 10178c2ecf20Sopenharmony_ci return count; 10188c2ecf20Sopenharmony_ci} 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic const struct file_operations fops_spectral_fft_period = { 10218c2ecf20Sopenharmony_ci .read = read_file_spectral_fft_period, 10228c2ecf20Sopenharmony_ci .write = write_file_spectral_fft_period, 10238c2ecf20Sopenharmony_ci .open = simple_open, 10248c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 10258c2ecf20Sopenharmony_ci .llseek = default_llseek, 10268c2ecf20Sopenharmony_ci}; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci/*******************/ 10298c2ecf20Sopenharmony_ci/* Relay interface */ 10308c2ecf20Sopenharmony_ci/*******************/ 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_cistatic struct dentry *create_buf_file_handler(const char *filename, 10338c2ecf20Sopenharmony_ci struct dentry *parent, 10348c2ecf20Sopenharmony_ci umode_t mode, 10358c2ecf20Sopenharmony_ci struct rchan_buf *buf, 10368c2ecf20Sopenharmony_ci int *is_global) 10378c2ecf20Sopenharmony_ci{ 10388c2ecf20Sopenharmony_ci struct dentry *buf_file; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci buf_file = debugfs_create_file(filename, mode, parent, buf, 10418c2ecf20Sopenharmony_ci &relay_file_operations); 10428c2ecf20Sopenharmony_ci if (IS_ERR(buf_file)) 10438c2ecf20Sopenharmony_ci return NULL; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci *is_global = 1; 10468c2ecf20Sopenharmony_ci return buf_file; 10478c2ecf20Sopenharmony_ci} 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_cistatic int remove_buf_file_handler(struct dentry *dentry) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci debugfs_remove(dentry); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci return 0; 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic struct rchan_callbacks rfs_spec_scan_cb = { 10578c2ecf20Sopenharmony_ci .create_buf_file = create_buf_file_handler, 10588c2ecf20Sopenharmony_ci .remove_buf_file = remove_buf_file_handler, 10598c2ecf20Sopenharmony_ci}; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci/*********************/ 10628c2ecf20Sopenharmony_ci/* Debug Init/Deinit */ 10638c2ecf20Sopenharmony_ci/*********************/ 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_civoid ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci if (spec_priv->rfs_chan_spec_scan) { 10688c2ecf20Sopenharmony_ci relay_close(spec_priv->rfs_chan_spec_scan); 10698c2ecf20Sopenharmony_ci spec_priv->rfs_chan_spec_scan = NULL; 10708c2ecf20Sopenharmony_ci } 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_spectral_deinit_debug); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_civoid ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv, 10758c2ecf20Sopenharmony_ci struct dentry *debugfs_phy) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci spec_priv->rfs_chan_spec_scan = relay_open("spectral_scan", 10788c2ecf20Sopenharmony_ci debugfs_phy, 10798c2ecf20Sopenharmony_ci 1024, 256, &rfs_spec_scan_cb, 10808c2ecf20Sopenharmony_ci NULL); 10818c2ecf20Sopenharmony_ci if (!spec_priv->rfs_chan_spec_scan) 10828c2ecf20Sopenharmony_ci return; 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci debugfs_create_file("spectral_scan_ctl", 10858c2ecf20Sopenharmony_ci 0600, 10868c2ecf20Sopenharmony_ci debugfs_phy, spec_priv, 10878c2ecf20Sopenharmony_ci &fops_spec_scan_ctl); 10888c2ecf20Sopenharmony_ci debugfs_create_file("spectral_short_repeat", 10898c2ecf20Sopenharmony_ci 0600, 10908c2ecf20Sopenharmony_ci debugfs_phy, spec_priv, 10918c2ecf20Sopenharmony_ci &fops_spectral_short_repeat); 10928c2ecf20Sopenharmony_ci debugfs_create_file("spectral_count", 10938c2ecf20Sopenharmony_ci 0600, 10948c2ecf20Sopenharmony_ci debugfs_phy, spec_priv, 10958c2ecf20Sopenharmony_ci &fops_spectral_count); 10968c2ecf20Sopenharmony_ci debugfs_create_file("spectral_period", 10978c2ecf20Sopenharmony_ci 0600, 10988c2ecf20Sopenharmony_ci debugfs_phy, spec_priv, 10998c2ecf20Sopenharmony_ci &fops_spectral_period); 11008c2ecf20Sopenharmony_ci debugfs_create_file("spectral_fft_period", 11018c2ecf20Sopenharmony_ci 0600, 11028c2ecf20Sopenharmony_ci debugfs_phy, spec_priv, 11038c2ecf20Sopenharmony_ci &fops_spectral_fft_period); 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ath9k_cmn_spectral_init_debug); 1106