1f65c0825SSujith Manoharan /* 2f65c0825SSujith Manoharan * Copyright (c) 2013 Qualcomm Atheros, Inc. 3f65c0825SSujith Manoharan * 4f65c0825SSujith Manoharan * Permission to use, copy, modify, and/or distribute this software for any 5f65c0825SSujith Manoharan * purpose with or without fee is hereby granted, provided that the above 6f65c0825SSujith Manoharan * copyright notice and this permission notice appear in all copies. 7f65c0825SSujith Manoharan * 8f65c0825SSujith Manoharan * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9f65c0825SSujith Manoharan * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10f65c0825SSujith Manoharan * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11f65c0825SSujith Manoharan * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12f65c0825SSujith Manoharan * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13f65c0825SSujith Manoharan * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14f65c0825SSujith Manoharan * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15f65c0825SSujith Manoharan */ 16f65c0825SSujith Manoharan 17f65c0825SSujith Manoharan #include <linux/relay.h> 182aa56ccaSNick Kossifidis #include <linux/random.h> 19f65c0825SSujith Manoharan #include "ath9k.h" 20f65c0825SSujith Manoharan 21f65c0825SSujith Manoharan static s8 fix_rssi_inv_only(u8 rssi_val) 22f65c0825SSujith Manoharan { 23f65c0825SSujith Manoharan if (rssi_val == 128) 24f65c0825SSujith Manoharan rssi_val = 0; 25f65c0825SSujith Manoharan return (s8) rssi_val; 26f65c0825SSujith Manoharan } 27f65c0825SSujith Manoharan 281111d426SOleksij Rempel static void ath_debug_send_fft_sample(struct ath_spec_scan_priv *spec_priv, 29f65c0825SSujith Manoharan struct fft_sample_tlv *fft_sample_tlv) 30f65c0825SSujith Manoharan { 31f65c0825SSujith Manoharan int length; 321111d426SOleksij Rempel if (!spec_priv->rfs_chan_spec_scan) 33f65c0825SSujith Manoharan return; 34f65c0825SSujith Manoharan 35f65c0825SSujith Manoharan length = __be16_to_cpu(fft_sample_tlv->length) + 36f65c0825SSujith Manoharan sizeof(*fft_sample_tlv); 371111d426SOleksij Rempel relay_write(spec_priv->rfs_chan_spec_scan, fft_sample_tlv, length); 38f65c0825SSujith Manoharan } 39f65c0825SSujith Manoharan 4072dd2cdaSNick Kossifidis typedef int (ath_cmn_fft_idx_validator) (u8 *sample_end, int bytes_read); 4172dd2cdaSNick Kossifidis 4272dd2cdaSNick Kossifidis static int 4372dd2cdaSNick Kossifidis ath_cmn_max_idx_verify_ht20_fft(u8 *sample_end, int bytes_read) 4472dd2cdaSNick Kossifidis { 4572dd2cdaSNick Kossifidis struct ath_ht20_mag_info *mag_info; 4672dd2cdaSNick Kossifidis u8 *sample; 4772dd2cdaSNick Kossifidis u16 max_magnitude; 4872dd2cdaSNick Kossifidis u8 max_index; 4972dd2cdaSNick Kossifidis u8 max_exp; 5072dd2cdaSNick Kossifidis 5172dd2cdaSNick Kossifidis /* Sanity check so that we don't read outside the read 5272dd2cdaSNick Kossifidis * buffer 5372dd2cdaSNick Kossifidis */ 5472dd2cdaSNick Kossifidis if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN - 1) 5572dd2cdaSNick Kossifidis return -1; 5672dd2cdaSNick Kossifidis 5772dd2cdaSNick Kossifidis mag_info = (struct ath_ht20_mag_info *) (sample_end - 5872dd2cdaSNick Kossifidis sizeof(struct ath_ht20_mag_info) + 1); 5972dd2cdaSNick Kossifidis 6072dd2cdaSNick Kossifidis sample = sample_end - SPECTRAL_HT20_SAMPLE_LEN + 1; 6172dd2cdaSNick Kossifidis 622f85786bSSimon Wunderlich max_index = spectral_max_index_ht20(mag_info->all_bins); 6372dd2cdaSNick Kossifidis max_magnitude = spectral_max_magnitude(mag_info->all_bins); 6472dd2cdaSNick Kossifidis 6572dd2cdaSNick Kossifidis max_exp = mag_info->max_exp & 0xf; 6672dd2cdaSNick Kossifidis 6772dd2cdaSNick Kossifidis /* Don't try to read something outside the read buffer 6872dd2cdaSNick Kossifidis * in case of a missing byte (so bins[0] will be outside 6972dd2cdaSNick Kossifidis * the read buffer) 7072dd2cdaSNick Kossifidis */ 7172dd2cdaSNick Kossifidis if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN && max_index < 1) 7272dd2cdaSNick Kossifidis return -1; 7372dd2cdaSNick Kossifidis 744e7a3fa5SSimon Wunderlich if ((sample[max_index] & 0xf8) != ((max_magnitude >> max_exp) & 0xf8)) 7572dd2cdaSNick Kossifidis return -1; 7672dd2cdaSNick Kossifidis else 7772dd2cdaSNick Kossifidis return 0; 7872dd2cdaSNick Kossifidis } 7972dd2cdaSNick Kossifidis 8072dd2cdaSNick Kossifidis static int 8172dd2cdaSNick Kossifidis ath_cmn_max_idx_verify_ht20_40_fft(u8 *sample_end, int bytes_read) 8272dd2cdaSNick Kossifidis { 8372dd2cdaSNick Kossifidis struct ath_ht20_40_mag_info *mag_info; 8472dd2cdaSNick Kossifidis u8 *sample; 8572dd2cdaSNick Kossifidis u16 lower_mag, upper_mag; 8672dd2cdaSNick Kossifidis u8 lower_max_index, upper_max_index; 8772dd2cdaSNick Kossifidis u8 max_exp; 8872dd2cdaSNick Kossifidis int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2; 8972dd2cdaSNick Kossifidis 9072dd2cdaSNick Kossifidis /* Sanity check so that we don't read outside the read 9172dd2cdaSNick Kossifidis * buffer 9272dd2cdaSNick Kossifidis */ 9372dd2cdaSNick Kossifidis if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN - 1) 9472dd2cdaSNick Kossifidis return -1; 9572dd2cdaSNick Kossifidis 9672dd2cdaSNick Kossifidis mag_info = (struct ath_ht20_40_mag_info *) (sample_end - 9772dd2cdaSNick Kossifidis sizeof(struct ath_ht20_40_mag_info) + 1); 9872dd2cdaSNick Kossifidis 9972dd2cdaSNick Kossifidis sample = sample_end - SPECTRAL_HT20_40_SAMPLE_LEN + 1; 10072dd2cdaSNick Kossifidis 10172dd2cdaSNick Kossifidis lower_mag = spectral_max_magnitude(mag_info->lower_bins); 1022f85786bSSimon Wunderlich lower_max_index = spectral_max_index_ht40(mag_info->lower_bins); 10372dd2cdaSNick Kossifidis 10472dd2cdaSNick Kossifidis upper_mag = spectral_max_magnitude(mag_info->upper_bins); 1052f85786bSSimon Wunderlich upper_max_index = spectral_max_index_ht40(mag_info->upper_bins); 10672dd2cdaSNick Kossifidis 10772dd2cdaSNick Kossifidis max_exp = mag_info->max_exp & 0xf; 10872dd2cdaSNick Kossifidis 10972dd2cdaSNick Kossifidis /* Don't try to read something outside the read buffer 11072dd2cdaSNick Kossifidis * in case of a missing byte (so bins[0] will be outside 11172dd2cdaSNick Kossifidis * the read buffer) 11272dd2cdaSNick Kossifidis */ 11372dd2cdaSNick Kossifidis if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN && 11472dd2cdaSNick Kossifidis ((upper_max_index < 1) || (lower_max_index < 1))) 11572dd2cdaSNick Kossifidis return -1; 11672dd2cdaSNick Kossifidis 1174e7a3fa5SSimon Wunderlich if (((sample[upper_max_index + dc_pos] & 0xf8) != 1184e7a3fa5SSimon Wunderlich ((upper_mag >> max_exp) & 0xf8)) || 1194e7a3fa5SSimon Wunderlich ((sample[lower_max_index] & 0xf8) != 1204e7a3fa5SSimon Wunderlich ((lower_mag >> max_exp) & 0xf8))) 12172dd2cdaSNick Kossifidis return -1; 12272dd2cdaSNick Kossifidis else 12372dd2cdaSNick Kossifidis return 0; 12472dd2cdaSNick Kossifidis } 12572dd2cdaSNick Kossifidis 12658b5e4c7SNick Kossifidis typedef int (ath_cmn_fft_sample_handler) (struct ath_rx_status *rs, 12758b5e4c7SNick Kossifidis struct ath_spec_scan_priv *spec_priv, 12858b5e4c7SNick Kossifidis u8 *sample_buf, u64 tsf, u16 freq, int chan_type); 12958b5e4c7SNick Kossifidis 13058b5e4c7SNick Kossifidis static int 13158b5e4c7SNick Kossifidis ath_cmn_process_ht20_fft(struct ath_rx_status *rs, 13258b5e4c7SNick Kossifidis struct ath_spec_scan_priv *spec_priv, 13358b5e4c7SNick Kossifidis u8 *sample_buf, 13458b5e4c7SNick Kossifidis u64 tsf, u16 freq, int chan_type) 135f65c0825SSujith Manoharan { 136f65c0825SSujith Manoharan struct fft_sample_ht20 fft_sample_20; 1377fa580c1SNick Kossifidis struct ath_common *common = ath9k_hw_common(spec_priv->ah); 13858b5e4c7SNick Kossifidis struct ath_hw *ah = spec_priv->ah; 13958b5e4c7SNick Kossifidis struct ath_ht20_mag_info *mag_info; 140f65c0825SSujith Manoharan struct fft_sample_tlv *tlv; 1417fa580c1SNick Kossifidis int i = 0; 1427fa580c1SNick Kossifidis int ret = 0; 14358b5e4c7SNick Kossifidis int dc_pos = SPECTRAL_HT20_NUM_BINS / 2; 1447fa580c1SNick Kossifidis u16 magnitude, tmp_mag, length; 1457fa580c1SNick Kossifidis u8 max_index, bitmap_w, max_exp; 146f65c0825SSujith Manoharan 14758b5e4c7SNick Kossifidis length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv); 14858b5e4c7SNick Kossifidis fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20; 14958b5e4c7SNick Kossifidis fft_sample_20.tlv.length = __cpu_to_be16(length); 15058b5e4c7SNick Kossifidis fft_sample_20.freq = __cpu_to_be16(freq); 15158b5e4c7SNick Kossifidis fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); 15258b5e4c7SNick Kossifidis fft_sample_20.noise = ah->noise; 153f65c0825SSujith Manoharan 15458b5e4c7SNick Kossifidis mag_info = (struct ath_ht20_mag_info *) (sample_buf + 15558b5e4c7SNick Kossifidis SPECTRAL_HT20_NUM_BINS); 156f65c0825SSujith Manoharan 15758b5e4c7SNick Kossifidis magnitude = spectral_max_magnitude(mag_info->all_bins); 15858b5e4c7SNick Kossifidis fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); 159f65c0825SSujith Manoharan 1602f85786bSSimon Wunderlich max_index = spectral_max_index_ht20(mag_info->all_bins); 16158b5e4c7SNick Kossifidis fft_sample_20.max_index = max_index; 162f65c0825SSujith Manoharan 16358b5e4c7SNick Kossifidis bitmap_w = spectral_bitmap_weight(mag_info->all_bins); 16458b5e4c7SNick Kossifidis fft_sample_20.bitmap_weight = bitmap_w; 16558b5e4c7SNick Kossifidis 1667fa580c1SNick Kossifidis max_exp = mag_info->max_exp & 0xf; 1677fa580c1SNick Kossifidis fft_sample_20.max_exp = max_exp; 16858b5e4c7SNick Kossifidis 16958b5e4c7SNick Kossifidis fft_sample_20.tsf = __cpu_to_be64(tsf); 17058b5e4c7SNick Kossifidis 17158b5e4c7SNick Kossifidis memcpy(fft_sample_20.data, sample_buf, SPECTRAL_HT20_NUM_BINS); 172f65c0825SSujith Manoharan 1737fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, "FFT HT20 frame: max mag 0x%X," 1747fa580c1SNick Kossifidis "max_mag_idx %i\n", 1757fa580c1SNick Kossifidis magnitude >> max_exp, 1767fa580c1SNick Kossifidis max_index); 1777fa580c1SNick Kossifidis 1784e7a3fa5SSimon Wunderlich if ((fft_sample_20.data[max_index] & 0xf8) != 1794e7a3fa5SSimon Wunderlich ((magnitude >> max_exp) & 0xf8)) { 1807fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n"); 1817fa580c1SNick Kossifidis ret = -1; 1827fa580c1SNick Kossifidis } 1837fa580c1SNick Kossifidis 184f65c0825SSujith Manoharan /* DC value (value in the middle) is the blind spot of the spectral 185f65c0825SSujith Manoharan * sample and invalid, interpolate it. 186f65c0825SSujith Manoharan */ 18758b5e4c7SNick Kossifidis fft_sample_20.data[dc_pos] = (fft_sample_20.data[dc_pos + 1] + 18858b5e4c7SNick Kossifidis fft_sample_20.data[dc_pos - 1]) / 2; 189f65c0825SSujith Manoharan 1907fa580c1SNick Kossifidis /* Check if the maximum magnitude is indeed maximum, 1917fa580c1SNick Kossifidis * also if the maximum value was at dc_pos, calculate 1927fa580c1SNick Kossifidis * a new one (since value at dc_pos is invalid). 1937fa580c1SNick Kossifidis */ 1947fa580c1SNick Kossifidis if (max_index == dc_pos) { 1957fa580c1SNick Kossifidis tmp_mag = 0; 1967fa580c1SNick Kossifidis for (i = 0; i < dc_pos; i++) { 1977fa580c1SNick Kossifidis if (fft_sample_20.data[i] > tmp_mag) { 1987fa580c1SNick Kossifidis tmp_mag = fft_sample_20.data[i]; 1997fa580c1SNick Kossifidis fft_sample_20.max_index = i; 2007fa580c1SNick Kossifidis } 2017fa580c1SNick Kossifidis } 2027fa580c1SNick Kossifidis 2037fa580c1SNick Kossifidis magnitude = tmp_mag << max_exp; 2047fa580c1SNick Kossifidis fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); 2057fa580c1SNick Kossifidis 2067fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, 2077fa580c1SNick Kossifidis "Calculated new lower max 0x%X at %i\n", 2087fa580c1SNick Kossifidis tmp_mag, fft_sample_20.max_index); 2097fa580c1SNick Kossifidis } else 2107fa580c1SNick Kossifidis for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++) { 2117fa580c1SNick Kossifidis if (fft_sample_20.data[i] == (magnitude >> max_exp)) 2127fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, 2137fa580c1SNick Kossifidis "Got max: 0x%X at index %i\n", 2147fa580c1SNick Kossifidis fft_sample_20.data[i], i); 2157fa580c1SNick Kossifidis 2167fa580c1SNick Kossifidis if (fft_sample_20.data[i] > (magnitude >> max_exp)) { 2177fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, 2187fa580c1SNick Kossifidis "Got bin %i greater than max: 0x%X\n", 2197fa580c1SNick Kossifidis i, fft_sample_20.data[i]); 2207fa580c1SNick Kossifidis ret = -1; 2217fa580c1SNick Kossifidis } 2227fa580c1SNick Kossifidis } 2237fa580c1SNick Kossifidis 2247fa580c1SNick Kossifidis if (ret < 0) 2257fa580c1SNick Kossifidis return ret; 2267fa580c1SNick Kossifidis 22758b5e4c7SNick Kossifidis tlv = (struct fft_sample_tlv *)&fft_sample_20; 22858b5e4c7SNick Kossifidis 22958b5e4c7SNick Kossifidis ath_debug_send_fft_sample(spec_priv, tlv); 23058b5e4c7SNick Kossifidis 23158b5e4c7SNick Kossifidis return 0; 23258b5e4c7SNick Kossifidis } 23358b5e4c7SNick Kossifidis 23458b5e4c7SNick Kossifidis static int 23558b5e4c7SNick Kossifidis ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs, 23658b5e4c7SNick Kossifidis struct ath_spec_scan_priv *spec_priv, 23758b5e4c7SNick Kossifidis u8 *sample_buf, 23858b5e4c7SNick Kossifidis u64 tsf, u16 freq, int chan_type) 23958b5e4c7SNick Kossifidis { 24058b5e4c7SNick Kossifidis struct fft_sample_ht20_40 fft_sample_40; 2417fa580c1SNick Kossifidis struct ath_common *common = ath9k_hw_common(spec_priv->ah); 24258b5e4c7SNick Kossifidis struct ath_hw *ah = spec_priv->ah; 243f65c0825SSujith Manoharan struct ath9k_hw_cal_data *caldata = ah->caldata; 244f65c0825SSujith Manoharan struct ath_ht20_40_mag_info *mag_info; 24558b5e4c7SNick Kossifidis struct fft_sample_tlv *tlv; 24658b5e4c7SNick Kossifidis int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2; 2477fa580c1SNick Kossifidis int i = 0; 2487fa580c1SNick Kossifidis int ret = 0; 24958b5e4c7SNick Kossifidis s16 ext_nf; 2507fa580c1SNick Kossifidis u16 lower_mag, upper_mag, tmp_mag, length; 25158b5e4c7SNick Kossifidis s8 lower_rssi, upper_rssi; 25258b5e4c7SNick Kossifidis u8 lower_max_index, upper_max_index; 2537fa580c1SNick Kossifidis u8 lower_bitmap_w, upper_bitmap_w, max_exp; 254f65c0825SSujith Manoharan 255f65c0825SSujith Manoharan if (caldata) 256f65c0825SSujith Manoharan ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan, 257f65c0825SSujith Manoharan caldata->nfCalHist[3].privNF); 258f65c0825SSujith Manoharan else 259f65c0825SSujith Manoharan ext_nf = ATH_DEFAULT_NOISE_FLOOR; 260f65c0825SSujith Manoharan 261f65c0825SSujith Manoharan length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv); 262f65c0825SSujith Manoharan fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40; 263f65c0825SSujith Manoharan fft_sample_40.tlv.length = __cpu_to_be16(length); 264f65c0825SSujith Manoharan fft_sample_40.freq = __cpu_to_be16(freq); 265f65c0825SSujith Manoharan fft_sample_40.channel_type = chan_type; 266f65c0825SSujith Manoharan 267f65c0825SSujith Manoharan if (chan_type == NL80211_CHAN_HT40PLUS) { 268f65c0825SSujith Manoharan lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); 269f65c0825SSujith Manoharan upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]); 270f65c0825SSujith Manoharan 271f65c0825SSujith Manoharan fft_sample_40.lower_noise = ah->noise; 272f65c0825SSujith Manoharan fft_sample_40.upper_noise = ext_nf; 273f65c0825SSujith Manoharan } else { 274f65c0825SSujith Manoharan lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]); 275f65c0825SSujith Manoharan upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); 276f65c0825SSujith Manoharan 277f65c0825SSujith Manoharan fft_sample_40.lower_noise = ext_nf; 278f65c0825SSujith Manoharan fft_sample_40.upper_noise = ah->noise; 279f65c0825SSujith Manoharan } 28058b5e4c7SNick Kossifidis 281f65c0825SSujith Manoharan fft_sample_40.lower_rssi = lower_rssi; 282f65c0825SSujith Manoharan fft_sample_40.upper_rssi = upper_rssi; 283f65c0825SSujith Manoharan 28458b5e4c7SNick Kossifidis mag_info = (struct ath_ht20_40_mag_info *) (sample_buf + 28558b5e4c7SNick Kossifidis SPECTRAL_HT20_40_NUM_BINS); 28658b5e4c7SNick Kossifidis 287f65c0825SSujith Manoharan lower_mag = spectral_max_magnitude(mag_info->lower_bins); 288f65c0825SSujith Manoharan fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); 28958b5e4c7SNick Kossifidis 29058b5e4c7SNick Kossifidis upper_mag = spectral_max_magnitude(mag_info->upper_bins); 291f65c0825SSujith Manoharan fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); 29258b5e4c7SNick Kossifidis 2932f85786bSSimon Wunderlich lower_max_index = spectral_max_index_ht40(mag_info->lower_bins); 294f65c0825SSujith Manoharan fft_sample_40.lower_max_index = lower_max_index; 29558b5e4c7SNick Kossifidis 2962f85786bSSimon Wunderlich upper_max_index = spectral_max_index_ht40(mag_info->upper_bins); 297f65c0825SSujith Manoharan fft_sample_40.upper_max_index = upper_max_index; 29858b5e4c7SNick Kossifidis 299f65c0825SSujith Manoharan lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins); 300f65c0825SSujith Manoharan fft_sample_40.lower_bitmap_weight = lower_bitmap_w; 30158b5e4c7SNick Kossifidis 30258b5e4c7SNick Kossifidis upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins); 303f65c0825SSujith Manoharan fft_sample_40.upper_bitmap_weight = upper_bitmap_w; 30458b5e4c7SNick Kossifidis 3057fa580c1SNick Kossifidis max_exp = mag_info->max_exp & 0xf; 3067fa580c1SNick Kossifidis fft_sample_40.max_exp = max_exp; 307f65c0825SSujith Manoharan 308f65c0825SSujith Manoharan fft_sample_40.tsf = __cpu_to_be64(tsf); 309f65c0825SSujith Manoharan 31058b5e4c7SNick Kossifidis memcpy(fft_sample_40.data, sample_buf, SPECTRAL_HT20_40_NUM_BINS); 31158b5e4c7SNick Kossifidis 3127fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, "FFT HT20/40 frame: lower mag 0x%X," 3137fa580c1SNick Kossifidis "lower_mag_idx %i, upper mag 0x%X," 3147fa580c1SNick Kossifidis "upper_mag_idx %i\n", 3157fa580c1SNick Kossifidis lower_mag >> max_exp, 3167fa580c1SNick Kossifidis lower_max_index, 3177fa580c1SNick Kossifidis upper_mag >> max_exp, 3187fa580c1SNick Kossifidis upper_max_index); 3197fa580c1SNick Kossifidis 3207fa580c1SNick Kossifidis /* Check if we got the expected magnitude values at 3217fa580c1SNick Kossifidis * the expected bins 3227fa580c1SNick Kossifidis */ 3234e7a3fa5SSimon Wunderlich if (((fft_sample_40.data[upper_max_index + dc_pos] & 0xf8) 3244e7a3fa5SSimon Wunderlich != ((upper_mag >> max_exp) & 0xf8)) || 3254e7a3fa5SSimon Wunderlich ((fft_sample_40.data[lower_max_index] & 0xf8) 3264e7a3fa5SSimon Wunderlich != ((lower_mag >> max_exp) & 0xf8))) { 3277fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n"); 3287fa580c1SNick Kossifidis ret = -1; 3297fa580c1SNick Kossifidis } 3307fa580c1SNick Kossifidis 33158b5e4c7SNick Kossifidis /* DC value (value in the middle) is the blind spot of the spectral 33258b5e4c7SNick Kossifidis * sample and invalid, interpolate it. 33358b5e4c7SNick Kossifidis */ 33458b5e4c7SNick Kossifidis fft_sample_40.data[dc_pos] = (fft_sample_40.data[dc_pos + 1] + 33558b5e4c7SNick Kossifidis fft_sample_40.data[dc_pos - 1]) / 2; 33658b5e4c7SNick Kossifidis 3377fa580c1SNick Kossifidis /* Check if the maximum magnitudes are indeed maximum, 3387fa580c1SNick Kossifidis * also if the maximum value was at dc_pos, calculate 3397fa580c1SNick Kossifidis * a new one (since value at dc_pos is invalid). 3407fa580c1SNick Kossifidis */ 3417fa580c1SNick Kossifidis if (lower_max_index == dc_pos) { 3427fa580c1SNick Kossifidis tmp_mag = 0; 3437fa580c1SNick Kossifidis for (i = 0; i < dc_pos; i++) { 3447fa580c1SNick Kossifidis if (fft_sample_40.data[i] > tmp_mag) { 3457fa580c1SNick Kossifidis tmp_mag = fft_sample_40.data[i]; 3467fa580c1SNick Kossifidis fft_sample_40.lower_max_index = i; 3477fa580c1SNick Kossifidis } 3487fa580c1SNick Kossifidis } 3497fa580c1SNick Kossifidis 3507fa580c1SNick Kossifidis lower_mag = tmp_mag << max_exp; 3517fa580c1SNick Kossifidis fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); 3527fa580c1SNick Kossifidis 3537fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, 3547fa580c1SNick Kossifidis "Calculated new lower max 0x%X at %i\n", 3557fa580c1SNick Kossifidis tmp_mag, fft_sample_40.lower_max_index); 3567fa580c1SNick Kossifidis } else 3577fa580c1SNick Kossifidis for (i = 0; i < dc_pos; i++) { 3587fa580c1SNick Kossifidis if (fft_sample_40.data[i] == (lower_mag >> max_exp)) 3597fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, 3607fa580c1SNick Kossifidis "Got lower mag: 0x%X at index %i\n", 3617fa580c1SNick Kossifidis fft_sample_40.data[i], i); 3627fa580c1SNick Kossifidis 3637fa580c1SNick Kossifidis if (fft_sample_40.data[i] > (lower_mag >> max_exp)) { 3647fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, 3657fa580c1SNick Kossifidis "Got lower bin %i higher than max: 0x%X\n", 3667fa580c1SNick Kossifidis i, fft_sample_40.data[i]); 3677fa580c1SNick Kossifidis ret = -1; 3687fa580c1SNick Kossifidis } 3697fa580c1SNick Kossifidis } 3707fa580c1SNick Kossifidis 3717fa580c1SNick Kossifidis if (upper_max_index == dc_pos) { 3727fa580c1SNick Kossifidis tmp_mag = 0; 3737fa580c1SNick Kossifidis for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { 3747fa580c1SNick Kossifidis if (fft_sample_40.data[i] > tmp_mag) { 3757fa580c1SNick Kossifidis tmp_mag = fft_sample_40.data[i]; 3767fa580c1SNick Kossifidis fft_sample_40.upper_max_index = i; 3777fa580c1SNick Kossifidis } 3787fa580c1SNick Kossifidis } 3797fa580c1SNick Kossifidis upper_mag = tmp_mag << max_exp; 3807fa580c1SNick Kossifidis fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); 3817fa580c1SNick Kossifidis 3827fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, 3837fa580c1SNick Kossifidis "Calculated new upper max 0x%X at %i\n", 3844fb5837aSSimon Wunderlich tmp_mag, fft_sample_40.upper_max_index); 3857fa580c1SNick Kossifidis } else 3867fa580c1SNick Kossifidis for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { 3877fa580c1SNick Kossifidis if (fft_sample_40.data[i] == (upper_mag >> max_exp)) 3887fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, 3897fa580c1SNick Kossifidis "Got upper mag: 0x%X at index %i\n", 3907fa580c1SNick Kossifidis fft_sample_40.data[i], i); 3917fa580c1SNick Kossifidis 3927fa580c1SNick Kossifidis if (fft_sample_40.data[i] > (upper_mag >> max_exp)) { 3937fa580c1SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, 3947fa580c1SNick Kossifidis "Got upper bin %i higher than max: 0x%X\n", 3957fa580c1SNick Kossifidis i, fft_sample_40.data[i]); 3967fa580c1SNick Kossifidis 3977fa580c1SNick Kossifidis ret = -1; 3987fa580c1SNick Kossifidis } 3997fa580c1SNick Kossifidis } 4007fa580c1SNick Kossifidis 4017fa580c1SNick Kossifidis if (ret < 0) 4027fa580c1SNick Kossifidis return ret; 4037fa580c1SNick Kossifidis 404f65c0825SSujith Manoharan tlv = (struct fft_sample_tlv *)&fft_sample_40; 405f65c0825SSujith Manoharan 4061111d426SOleksij Rempel ath_debug_send_fft_sample(spec_priv, tlv); 407f65c0825SSujith Manoharan 40858b5e4c7SNick Kossifidis return 0; 40958b5e4c7SNick Kossifidis } 41058b5e4c7SNick Kossifidis 4110f2c75deSNick Kossifidis static inline void 4120f2c75deSNick Kossifidis ath_cmn_copy_fft_frame(u8 *in, u8 *out, int sample_len, int sample_bytes) 4130f2c75deSNick Kossifidis { 4140f2c75deSNick Kossifidis switch (sample_bytes - sample_len) { 4150f2c75deSNick Kossifidis case -1: 4160f2c75deSNick Kossifidis /* First byte missing */ 4170f2c75deSNick Kossifidis memcpy(&out[1], in, 4180f2c75deSNick Kossifidis sample_len - 1); 4190f2c75deSNick Kossifidis break; 4200f2c75deSNick Kossifidis case 0: 4210f2c75deSNick Kossifidis /* Length correct, nothing to do. */ 4220f2c75deSNick Kossifidis memcpy(out, in, sample_len); 4230f2c75deSNick Kossifidis break; 4240f2c75deSNick Kossifidis case 1: 4250f2c75deSNick Kossifidis /* MAC added 2 extra bytes AND first byte 4260f2c75deSNick Kossifidis * is missing. 4270f2c75deSNick Kossifidis */ 4280f2c75deSNick Kossifidis memcpy(&out[1], in, 30); 4290f2c75deSNick Kossifidis out[31] = in[31]; 4300f2c75deSNick Kossifidis memcpy(&out[32], &in[33], 4310f2c75deSNick Kossifidis sample_len - 32); 4320f2c75deSNick Kossifidis break; 4330f2c75deSNick Kossifidis case 2: 4340f2c75deSNick Kossifidis /* MAC added 2 extra bytes at bin 30 and 32, 4350f2c75deSNick Kossifidis * remove them. 4360f2c75deSNick Kossifidis */ 4370f2c75deSNick Kossifidis memcpy(out, in, 30); 4380f2c75deSNick Kossifidis out[30] = in[31]; 4390f2c75deSNick Kossifidis memcpy(&out[31], &in[33], 4400f2c75deSNick Kossifidis sample_len - 31); 4410f2c75deSNick Kossifidis break; 4420f2c75deSNick Kossifidis default: 4430f2c75deSNick Kossifidis break; 4440f2c75deSNick Kossifidis } 4450f2c75deSNick Kossifidis } 4460f2c75deSNick Kossifidis 4476b8f85a9SNick Kossifidis static int 4486b8f85a9SNick Kossifidis ath_cmn_is_fft_buf_full(struct ath_spec_scan_priv *spec_priv) 4496b8f85a9SNick Kossifidis { 4506b8f85a9SNick Kossifidis int i = 0; 4516b8f85a9SNick Kossifidis int ret = 0; 452221b6ec6SSebastian Gottschall struct rchan_buf *buf; 4536b8f85a9SNick Kossifidis struct rchan *rc = spec_priv->rfs_chan_spec_scan; 4546b8f85a9SNick Kossifidis 455221b6ec6SSebastian Gottschall for_each_possible_cpu(i) { 456221b6ec6SSebastian Gottschall if ((buf = *per_cpu_ptr(rc->buf, i))) { 457221b6ec6SSebastian Gottschall ret += relay_buf_full(buf); 458221b6ec6SSebastian Gottschall } 459221b6ec6SSebastian Gottschall } 4606b8f85a9SNick Kossifidis 461221b6ec6SSebastian Gottschall if (ret) 4626b8f85a9SNick Kossifidis return 1; 4636b8f85a9SNick Kossifidis else 4646b8f85a9SNick Kossifidis return 0; 4656b8f85a9SNick Kossifidis } 4666b8f85a9SNick Kossifidis 46758b5e4c7SNick Kossifidis /* returns 1 if this was a spectral frame, even if not handled. */ 46858b5e4c7SNick Kossifidis int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr, 46958b5e4c7SNick Kossifidis struct ath_rx_status *rs, u64 tsf) 47058b5e4c7SNick Kossifidis { 47158b5e4c7SNick Kossifidis u8 sample_buf[SPECTRAL_SAMPLE_MAX_LEN] = {0}; 47258b5e4c7SNick Kossifidis struct ath_hw *ah = spec_priv->ah; 47358b5e4c7SNick Kossifidis struct ath_common *common = ath9k_hw_common(spec_priv->ah); 47403224678SSimon Wunderlich struct ath_softc *sc = (struct ath_softc *)common->priv; 47558b5e4c7SNick Kossifidis u8 num_bins, *vdata = (u8 *)hdr; 47658b5e4c7SNick Kossifidis struct ath_radar_info *radar_info; 47758b5e4c7SNick Kossifidis int len = rs->rs_datalen; 47872dd2cdaSNick Kossifidis int i; 47972dd2cdaSNick Kossifidis int got_slen = 0; 48072dd2cdaSNick Kossifidis u8 *sample_start; 48172dd2cdaSNick Kossifidis int sample_bytes = 0; 48272dd2cdaSNick Kossifidis int ret = 0; 48358b5e4c7SNick Kossifidis u16 fft_len, sample_len, freq = ah->curchan->chan->center_freq; 48458b5e4c7SNick Kossifidis enum nl80211_channel_type chan_type; 48572dd2cdaSNick Kossifidis ath_cmn_fft_idx_validator *fft_idx_validator; 48658b5e4c7SNick Kossifidis ath_cmn_fft_sample_handler *fft_handler; 48758b5e4c7SNick Kossifidis 48858b5e4c7SNick Kossifidis /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer 48958b5e4c7SNick Kossifidis * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT 49058b5e4c7SNick Kossifidis * yet, but this is supposed to be possible as well. 49158b5e4c7SNick Kossifidis */ 49258b5e4c7SNick Kossifidis if (rs->rs_phyerr != ATH9K_PHYERR_RADAR && 49358b5e4c7SNick Kossifidis rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT && 49458b5e4c7SNick Kossifidis rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) 49558b5e4c7SNick Kossifidis return 0; 49658b5e4c7SNick Kossifidis 49758b5e4c7SNick Kossifidis /* check if spectral scan bit is set. This does not have to be checked 49858b5e4c7SNick Kossifidis * if received through a SPECTRAL phy error, but shouldn't hurt. 49958b5e4c7SNick Kossifidis */ 50058b5e4c7SNick Kossifidis radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; 50158b5e4c7SNick Kossifidis if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) 50258b5e4c7SNick Kossifidis return 0; 50358b5e4c7SNick Kossifidis 50440bea976SMiaoqing Pan if (!spec_priv->rfs_chan_spec_scan) 50540bea976SMiaoqing Pan return 1; 50640bea976SMiaoqing Pan 5076b8f85a9SNick Kossifidis /* Output buffers are full, no need to process anything 5086b8f85a9SNick Kossifidis * since there is no space to put the result anyway 5096b8f85a9SNick Kossifidis */ 5106b8f85a9SNick Kossifidis ret = ath_cmn_is_fft_buf_full(spec_priv); 5116b8f85a9SNick Kossifidis if (ret == 1) { 5126b8f85a9SNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, "FFT report ignored, no space " 5136b8f85a9SNick Kossifidis "left on output buffers\n"); 5146b8f85a9SNick Kossifidis return 1; 5156b8f85a9SNick Kossifidis } 5166b8f85a9SNick Kossifidis 51758b5e4c7SNick Kossifidis chan_type = cfg80211_get_chandef_type(&common->hw->conf.chandef); 51858b5e4c7SNick Kossifidis if ((chan_type == NL80211_CHAN_HT40MINUS) || 51958b5e4c7SNick Kossifidis (chan_type == NL80211_CHAN_HT40PLUS)) { 52058b5e4c7SNick Kossifidis fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; 52158b5e4c7SNick Kossifidis sample_len = SPECTRAL_HT20_40_SAMPLE_LEN; 52258b5e4c7SNick Kossifidis num_bins = SPECTRAL_HT20_40_NUM_BINS; 52372dd2cdaSNick Kossifidis fft_idx_validator = &ath_cmn_max_idx_verify_ht20_40_fft; 52458b5e4c7SNick Kossifidis fft_handler = &ath_cmn_process_ht20_40_fft; 52558b5e4c7SNick Kossifidis } else { 52658b5e4c7SNick Kossifidis fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; 52758b5e4c7SNick Kossifidis sample_len = SPECTRAL_HT20_SAMPLE_LEN; 52858b5e4c7SNick Kossifidis num_bins = SPECTRAL_HT20_NUM_BINS; 52972dd2cdaSNick Kossifidis fft_idx_validator = ath_cmn_max_idx_verify_ht20_fft; 53058b5e4c7SNick Kossifidis fft_handler = &ath_cmn_process_ht20_fft; 53158b5e4c7SNick Kossifidis } 53258b5e4c7SNick Kossifidis 53372dd2cdaSNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, "Got radar dump bw_info: 0x%X," 53472dd2cdaSNick Kossifidis "len: %i fft_len: %i\n", 53572dd2cdaSNick Kossifidis radar_info->pulse_bw_info, 53672dd2cdaSNick Kossifidis len, 53772dd2cdaSNick Kossifidis fft_len); 53872dd2cdaSNick Kossifidis sample_start = vdata; 53972dd2cdaSNick Kossifidis for (i = 0; i < len - 2; i++) { 54072dd2cdaSNick Kossifidis sample_bytes++; 54158b5e4c7SNick Kossifidis 54272dd2cdaSNick Kossifidis /* Only a single sample received, no need to look 54372dd2cdaSNick Kossifidis * for the sample's end, do the correction based 54472dd2cdaSNick Kossifidis * on the packet's length instead. Note that hw 54572dd2cdaSNick Kossifidis * will always put the radar_info structure on 54672dd2cdaSNick Kossifidis * the end. 54772dd2cdaSNick Kossifidis */ 54872dd2cdaSNick Kossifidis if (len <= fft_len + 2) { 54972dd2cdaSNick Kossifidis sample_bytes = len - sizeof(struct ath_radar_info); 55072dd2cdaSNick Kossifidis got_slen = 1; 55158b5e4c7SNick Kossifidis } 55258b5e4c7SNick Kossifidis 55372dd2cdaSNick Kossifidis /* Search for the end of the FFT frame between 55472dd2cdaSNick Kossifidis * sample_len - 1 and sample_len + 2. exp_max is 3 55572dd2cdaSNick Kossifidis * bits long and it's the only value on the last 55672dd2cdaSNick Kossifidis * byte of the frame so since it'll be smaller than 55772dd2cdaSNick Kossifidis * the next byte (the first bin of the next sample) 55872dd2cdaSNick Kossifidis * 90% of the time, we can use it as a separator. 55972dd2cdaSNick Kossifidis */ 56072dd2cdaSNick Kossifidis if (vdata[i] <= 0x7 && sample_bytes >= sample_len - 1) { 56158b5e4c7SNick Kossifidis 56272dd2cdaSNick Kossifidis /* Got a frame length within boundaries, there are 56372dd2cdaSNick Kossifidis * four scenarios here: 56472dd2cdaSNick Kossifidis * 56572dd2cdaSNick Kossifidis * a) sample_len -> We got the correct length 56672dd2cdaSNick Kossifidis * b) sample_len + 2 -> 2 bytes added around bin[31] 56772dd2cdaSNick Kossifidis * c) sample_len - 1 -> The first byte is missing 56872dd2cdaSNick Kossifidis * d) sample_len + 1 -> b + c at the same time 56972dd2cdaSNick Kossifidis * 57072dd2cdaSNick Kossifidis * When MAC adds 2 extra bytes, bin[31] and bin[32] 57172dd2cdaSNick Kossifidis * have the same value, so we can use that for further 57272dd2cdaSNick Kossifidis * verification in cases b and d. 57372dd2cdaSNick Kossifidis */ 57472dd2cdaSNick Kossifidis 57572dd2cdaSNick Kossifidis /* Did we go too far ? If so we couldn't determine 57672dd2cdaSNick Kossifidis * this sample's boundaries, discard any further 57772dd2cdaSNick Kossifidis * data 57872dd2cdaSNick Kossifidis */ 57972dd2cdaSNick Kossifidis if ((sample_bytes > sample_len + 2) || 58072dd2cdaSNick Kossifidis ((sample_bytes > sample_len) && 58172dd2cdaSNick Kossifidis (sample_start[31] != sample_start[32]))) 58272dd2cdaSNick Kossifidis break; 58372dd2cdaSNick Kossifidis 58472dd2cdaSNick Kossifidis /* See if we got a valid frame by checking the 58572dd2cdaSNick Kossifidis * consistency of mag_info fields. This is to 58672dd2cdaSNick Kossifidis * prevent from "fixing" a correct frame. 58772dd2cdaSNick Kossifidis * Failure is non-fatal, later frames may 58872dd2cdaSNick Kossifidis * be valid. 58972dd2cdaSNick Kossifidis */ 59072dd2cdaSNick Kossifidis if (!fft_idx_validator(&vdata[i], i)) { 59172dd2cdaSNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, 59272dd2cdaSNick Kossifidis "Found valid fft frame at %i\n", i); 59372dd2cdaSNick Kossifidis got_slen = 1; 59472dd2cdaSNick Kossifidis } 59572dd2cdaSNick Kossifidis 59672dd2cdaSNick Kossifidis /* We expect 1 - 2 more bytes */ 59772dd2cdaSNick Kossifidis else if ((sample_start[31] == sample_start[32]) && 59872dd2cdaSNick Kossifidis (sample_bytes >= sample_len) && 59972dd2cdaSNick Kossifidis (sample_bytes < sample_len + 2) && 60072dd2cdaSNick Kossifidis (vdata[i + 1] <= 0x7)) 60172dd2cdaSNick Kossifidis continue; 60272dd2cdaSNick Kossifidis 60372dd2cdaSNick Kossifidis /* Try to distinguish cases a and c */ 60472dd2cdaSNick Kossifidis else if ((sample_bytes == sample_len - 1) && 60572dd2cdaSNick Kossifidis (vdata[i + 1] <= 0x7)) 60672dd2cdaSNick Kossifidis continue; 60772dd2cdaSNick Kossifidis 60872dd2cdaSNick Kossifidis got_slen = 1; 60972dd2cdaSNick Kossifidis } 61072dd2cdaSNick Kossifidis 61172dd2cdaSNick Kossifidis if (got_slen) { 61272dd2cdaSNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, "FFT frame len: %i\n", 61372dd2cdaSNick Kossifidis sample_bytes); 6140f2c75deSNick Kossifidis 6150f2c75deSNick Kossifidis /* Only try to fix a frame if it's the only one 6160f2c75deSNick Kossifidis * on the report, else just skip it. 61772dd2cdaSNick Kossifidis */ 6180f2c75deSNick Kossifidis if (sample_bytes != sample_len && len <= fft_len + 2) { 6190f2c75deSNick Kossifidis ath_cmn_copy_fft_frame(sample_start, 6200f2c75deSNick Kossifidis sample_buf, sample_len, 6210f2c75deSNick Kossifidis sample_bytes); 6220f2c75deSNick Kossifidis 62303224678SSimon Wunderlich ret = fft_handler(rs, spec_priv, sample_buf, 6240f2c75deSNick Kossifidis tsf, freq, chan_type); 6253ea2ce3fSNick Kossifidis 62603224678SSimon Wunderlich if (ret == 0) 62772569b7bSArnd Bergmann RX_STAT_INC(sc, rx_spectral_sample_good); 62803224678SSimon Wunderlich else 62972569b7bSArnd Bergmann RX_STAT_INC(sc, rx_spectral_sample_err); 63003224678SSimon Wunderlich 6313ea2ce3fSNick Kossifidis memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); 6322aa56ccaSNick Kossifidis 6332aa56ccaSNick Kossifidis /* Mix the received bins to the /dev/random 6342aa56ccaSNick Kossifidis * pool 6352aa56ccaSNick Kossifidis */ 6362aa56ccaSNick Kossifidis add_device_randomness(sample_buf, num_bins); 63772dd2cdaSNick Kossifidis } 63872dd2cdaSNick Kossifidis 6390f2c75deSNick Kossifidis /* Process a normal frame */ 6402aa56ccaSNick Kossifidis if (sample_bytes == sample_len) { 6419acc98b9SNick Kossifidis ret = fft_handler(rs, spec_priv, sample_start, 6420f2c75deSNick Kossifidis tsf, freq, chan_type); 6430f2c75deSNick Kossifidis 64403224678SSimon Wunderlich if (ret == 0) 64572569b7bSArnd Bergmann RX_STAT_INC(sc, rx_spectral_sample_good); 64603224678SSimon Wunderlich else 64772569b7bSArnd Bergmann RX_STAT_INC(sc, rx_spectral_sample_err); 64803224678SSimon Wunderlich 6492aa56ccaSNick Kossifidis /* Mix the received bins to the /dev/random 6502aa56ccaSNick Kossifidis * pool 6512aa56ccaSNick Kossifidis */ 6522aa56ccaSNick Kossifidis add_device_randomness(sample_start, num_bins); 6532aa56ccaSNick Kossifidis } 6542aa56ccaSNick Kossifidis 6550f2c75deSNick Kossifidis /* Short report processed, break out of the 6560f2c75deSNick Kossifidis * loop. 6570f2c75deSNick Kossifidis */ 6580f2c75deSNick Kossifidis if (len <= fft_len + 2) 659b796a6c0SSimon Wunderlich return 1; 6600f2c75deSNick Kossifidis 66172dd2cdaSNick Kossifidis sample_start = &vdata[i + 1]; 6620f2c75deSNick Kossifidis 66372dd2cdaSNick Kossifidis /* -1 to grab sample_len -1, -2 since 66472dd2cdaSNick Kossifidis * they 'll get increased by one. In case 66572dd2cdaSNick Kossifidis * of failure try to recover by going byte 6660f2c75deSNick Kossifidis * by byte instead. 6670f2c75deSNick Kossifidis */ 66872dd2cdaSNick Kossifidis if (ret == 0) { 66972dd2cdaSNick Kossifidis i += num_bins - 2; 67072dd2cdaSNick Kossifidis sample_bytes = num_bins - 2; 67172dd2cdaSNick Kossifidis } 67272dd2cdaSNick Kossifidis got_slen = 0; 67372dd2cdaSNick Kossifidis } 67472dd2cdaSNick Kossifidis } 67572dd2cdaSNick Kossifidis 67672dd2cdaSNick Kossifidis i -= num_bins - 2; 67772dd2cdaSNick Kossifidis if (len - i != sizeof(struct ath_radar_info)) 67872dd2cdaSNick Kossifidis ath_dbg(common, SPECTRAL_SCAN, "FFT report truncated" 67972dd2cdaSNick Kossifidis "(bytes left: %i)\n", 68072dd2cdaSNick Kossifidis len - i); 681f65c0825SSujith Manoharan return 1; 682f65c0825SSujith Manoharan } 68367dc74f1SOleksij Rempel EXPORT_SYMBOL(ath_cmn_process_fft); 684f65c0825SSujith Manoharan 685f65c0825SSujith Manoharan /*********************/ 686f65c0825SSujith Manoharan /* spectral_scan_ctl */ 687f65c0825SSujith Manoharan /*********************/ 688f65c0825SSujith Manoharan 689f65c0825SSujith Manoharan static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, 690f65c0825SSujith Manoharan size_t count, loff_t *ppos) 691f65c0825SSujith Manoharan { 6921111d426SOleksij Rempel struct ath_spec_scan_priv *spec_priv = file->private_data; 693f65c0825SSujith Manoharan char *mode = ""; 694f65c0825SSujith Manoharan unsigned int len; 695f65c0825SSujith Manoharan 6961111d426SOleksij Rempel switch (spec_priv->spectral_mode) { 697f65c0825SSujith Manoharan case SPECTRAL_DISABLED: 698f65c0825SSujith Manoharan mode = "disable"; 699f65c0825SSujith Manoharan break; 700f65c0825SSujith Manoharan case SPECTRAL_BACKGROUND: 701f65c0825SSujith Manoharan mode = "background"; 702f65c0825SSujith Manoharan break; 703f65c0825SSujith Manoharan case SPECTRAL_CHANSCAN: 704f65c0825SSujith Manoharan mode = "chanscan"; 705f65c0825SSujith Manoharan break; 706f65c0825SSujith Manoharan case SPECTRAL_MANUAL: 707f65c0825SSujith Manoharan mode = "manual"; 708f65c0825SSujith Manoharan break; 709f65c0825SSujith Manoharan } 710f65c0825SSujith Manoharan len = strlen(mode); 711f65c0825SSujith Manoharan return simple_read_from_buffer(user_buf, count, ppos, mode, len); 712f65c0825SSujith Manoharan } 713f65c0825SSujith Manoharan 71467dc74f1SOleksij Rempel void ath9k_cmn_spectral_scan_trigger(struct ath_common *common, 715f00a422cSOleksij Rempel struct ath_spec_scan_priv *spec_priv) 716f00a422cSOleksij Rempel { 717f00a422cSOleksij Rempel struct ath_hw *ah = spec_priv->ah; 718f00a422cSOleksij Rempel u32 rxfilter; 719f00a422cSOleksij Rempel 72097f2645fSMasahiro Yamada if (IS_ENABLED(CONFIG_ATH9K_TX99)) 721f00a422cSOleksij Rempel return; 722f00a422cSOleksij Rempel 723f00a422cSOleksij Rempel if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { 724f00a422cSOleksij Rempel ath_err(common, "spectrum analyzer not implemented on this hardware\n"); 725f00a422cSOleksij Rempel return; 726f00a422cSOleksij Rempel } 727f00a422cSOleksij Rempel 72845c3d550SZefir Kurtisi if (!spec_priv->spec_config.enabled) 72945c3d550SZefir Kurtisi return; 73045c3d550SZefir Kurtisi 731f00a422cSOleksij Rempel ath_ps_ops(common)->wakeup(common); 732f00a422cSOleksij Rempel rxfilter = ath9k_hw_getrxfilter(ah); 733f00a422cSOleksij Rempel ath9k_hw_setrxfilter(ah, rxfilter | 734f00a422cSOleksij Rempel ATH9K_RX_FILTER_PHYRADAR | 735f00a422cSOleksij Rempel ATH9K_RX_FILTER_PHYERR); 736f00a422cSOleksij Rempel 737f00a422cSOleksij Rempel /* TODO: usually this should not be neccesary, but for some reason 738f00a422cSOleksij Rempel * (or in some mode?) the trigger must be called after the 739f00a422cSOleksij Rempel * configuration, otherwise the register will have its values reset 740f00a422cSOleksij Rempel * (on my ar9220 to value 0x01002310) 741f00a422cSOleksij Rempel */ 74267dc74f1SOleksij Rempel ath9k_cmn_spectral_scan_config(common, spec_priv, spec_priv->spectral_mode); 743f00a422cSOleksij Rempel ath9k_hw_ops(ah)->spectral_scan_trigger(ah); 744f00a422cSOleksij Rempel ath_ps_ops(common)->restore(common); 745f00a422cSOleksij Rempel } 74667dc74f1SOleksij Rempel EXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger); 747f00a422cSOleksij Rempel 74867dc74f1SOleksij Rempel int ath9k_cmn_spectral_scan_config(struct ath_common *common, 749f00a422cSOleksij Rempel struct ath_spec_scan_priv *spec_priv, 750f00a422cSOleksij Rempel enum spectral_mode spectral_mode) 751f00a422cSOleksij Rempel { 752f00a422cSOleksij Rempel struct ath_hw *ah = spec_priv->ah; 753f00a422cSOleksij Rempel 754f00a422cSOleksij Rempel if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { 755f00a422cSOleksij Rempel ath_err(common, "spectrum analyzer not implemented on this hardware\n"); 756f00a422cSOleksij Rempel return -1; 757f00a422cSOleksij Rempel } 758f00a422cSOleksij Rempel 759f00a422cSOleksij Rempel switch (spectral_mode) { 760f00a422cSOleksij Rempel case SPECTRAL_DISABLED: 761f00a422cSOleksij Rempel spec_priv->spec_config.enabled = 0; 762f00a422cSOleksij Rempel break; 763f00a422cSOleksij Rempel case SPECTRAL_BACKGROUND: 764f00a422cSOleksij Rempel /* send endless samples. 765f00a422cSOleksij Rempel * TODO: is this really useful for "background"? 766f00a422cSOleksij Rempel */ 767f00a422cSOleksij Rempel spec_priv->spec_config.endless = 1; 768f00a422cSOleksij Rempel spec_priv->spec_config.enabled = 1; 769f00a422cSOleksij Rempel break; 770f00a422cSOleksij Rempel case SPECTRAL_CHANSCAN: 771f00a422cSOleksij Rempel case SPECTRAL_MANUAL: 772f00a422cSOleksij Rempel spec_priv->spec_config.endless = 0; 773f00a422cSOleksij Rempel spec_priv->spec_config.enabled = 1; 774f00a422cSOleksij Rempel break; 775f00a422cSOleksij Rempel default: 776f00a422cSOleksij Rempel return -1; 777f00a422cSOleksij Rempel } 778f00a422cSOleksij Rempel 779f00a422cSOleksij Rempel ath_ps_ops(common)->wakeup(common); 780f00a422cSOleksij Rempel ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config); 781f00a422cSOleksij Rempel ath_ps_ops(common)->restore(common); 782f00a422cSOleksij Rempel 783f00a422cSOleksij Rempel spec_priv->spectral_mode = spectral_mode; 784f00a422cSOleksij Rempel 785f00a422cSOleksij Rempel return 0; 786f00a422cSOleksij Rempel } 78767dc74f1SOleksij Rempel EXPORT_SYMBOL(ath9k_cmn_spectral_scan_config); 788f00a422cSOleksij Rempel 789f65c0825SSujith Manoharan static ssize_t write_file_spec_scan_ctl(struct file *file, 790f65c0825SSujith Manoharan const char __user *user_buf, 791f65c0825SSujith Manoharan size_t count, loff_t *ppos) 792f65c0825SSujith Manoharan { 7931111d426SOleksij Rempel struct ath_spec_scan_priv *spec_priv = file->private_data; 7941111d426SOleksij Rempel struct ath_common *common = ath9k_hw_common(spec_priv->ah); 795f65c0825SSujith Manoharan char buf[32]; 796f65c0825SSujith Manoharan ssize_t len; 797f65c0825SSujith Manoharan 79897f2645fSMasahiro Yamada if (IS_ENABLED(CONFIG_ATH9K_TX99)) 799f65c0825SSujith Manoharan return -EOPNOTSUPP; 800f65c0825SSujith Manoharan 801f65c0825SSujith Manoharan len = min(count, sizeof(buf) - 1); 802f65c0825SSujith Manoharan if (copy_from_user(buf, user_buf, len)) 803f65c0825SSujith Manoharan return -EFAULT; 804f65c0825SSujith Manoharan 805f65c0825SSujith Manoharan buf[len] = '\0'; 806f65c0825SSujith Manoharan 807f65c0825SSujith Manoharan if (strncmp("trigger", buf, 7) == 0) { 80867dc74f1SOleksij Rempel ath9k_cmn_spectral_scan_trigger(common, spec_priv); 809ded3fb4cSMaks Naumov } else if (strncmp("background", buf, 10) == 0) { 81067dc74f1SOleksij Rempel ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_BACKGROUND); 811f65c0825SSujith Manoharan ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n"); 812f65c0825SSujith Manoharan } else if (strncmp("chanscan", buf, 8) == 0) { 81367dc74f1SOleksij Rempel ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_CHANSCAN); 814f65c0825SSujith Manoharan ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n"); 815f65c0825SSujith Manoharan } else if (strncmp("manual", buf, 6) == 0) { 81667dc74f1SOleksij Rempel ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_MANUAL); 817f65c0825SSujith Manoharan ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n"); 818f65c0825SSujith Manoharan } else if (strncmp("disable", buf, 7) == 0) { 81967dc74f1SOleksij Rempel ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_DISABLED); 820f65c0825SSujith Manoharan ath_dbg(common, CONFIG, "spectral scan: disabled\n"); 821f65c0825SSujith Manoharan } else { 822f65c0825SSujith Manoharan return -EINVAL; 823f65c0825SSujith Manoharan } 824f65c0825SSujith Manoharan 825f65c0825SSujith Manoharan return count; 826f65c0825SSujith Manoharan } 827f65c0825SSujith Manoharan 828f65c0825SSujith Manoharan static const struct file_operations fops_spec_scan_ctl = { 829f65c0825SSujith Manoharan .read = read_file_spec_scan_ctl, 830f65c0825SSujith Manoharan .write = write_file_spec_scan_ctl, 831f65c0825SSujith Manoharan .open = simple_open, 832f65c0825SSujith Manoharan .owner = THIS_MODULE, 833f65c0825SSujith Manoharan .llseek = default_llseek, 834f65c0825SSujith Manoharan }; 835f65c0825SSujith Manoharan 836f65c0825SSujith Manoharan /*************************/ 837f65c0825SSujith Manoharan /* spectral_short_repeat */ 838f65c0825SSujith Manoharan /*************************/ 839f65c0825SSujith Manoharan 840f65c0825SSujith Manoharan static ssize_t read_file_spectral_short_repeat(struct file *file, 841f65c0825SSujith Manoharan char __user *user_buf, 842f65c0825SSujith Manoharan size_t count, loff_t *ppos) 843f65c0825SSujith Manoharan { 8441111d426SOleksij Rempel struct ath_spec_scan_priv *spec_priv = file->private_data; 845f65c0825SSujith Manoharan char buf[32]; 846f65c0825SSujith Manoharan unsigned int len; 847f65c0825SSujith Manoharan 8481111d426SOleksij Rempel len = sprintf(buf, "%d\n", spec_priv->spec_config.short_repeat); 849f65c0825SSujith Manoharan return simple_read_from_buffer(user_buf, count, ppos, buf, len); 850f65c0825SSujith Manoharan } 851f65c0825SSujith Manoharan 852f65c0825SSujith Manoharan static ssize_t write_file_spectral_short_repeat(struct file *file, 853f65c0825SSujith Manoharan const char __user *user_buf, 854f65c0825SSujith Manoharan size_t count, loff_t *ppos) 855f65c0825SSujith Manoharan { 8561111d426SOleksij Rempel struct ath_spec_scan_priv *spec_priv = file->private_data; 857f65c0825SSujith Manoharan unsigned long val; 858f65c0825SSujith Manoharan char buf[32]; 859f65c0825SSujith Manoharan ssize_t len; 860f65c0825SSujith Manoharan 861f65c0825SSujith Manoharan len = min(count, sizeof(buf) - 1); 862f65c0825SSujith Manoharan if (copy_from_user(buf, user_buf, len)) 863f65c0825SSujith Manoharan return -EFAULT; 864f65c0825SSujith Manoharan 865f65c0825SSujith Manoharan buf[len] = '\0'; 866f65c0825SSujith Manoharan if (kstrtoul(buf, 0, &val)) 867f65c0825SSujith Manoharan return -EINVAL; 868f65c0825SSujith Manoharan 8693f557202SAndrey Utkin if (val > 1) 870f65c0825SSujith Manoharan return -EINVAL; 871f65c0825SSujith Manoharan 8721111d426SOleksij Rempel spec_priv->spec_config.short_repeat = val; 873f65c0825SSujith Manoharan return count; 874f65c0825SSujith Manoharan } 875f65c0825SSujith Manoharan 876f65c0825SSujith Manoharan static const struct file_operations fops_spectral_short_repeat = { 877f65c0825SSujith Manoharan .read = read_file_spectral_short_repeat, 878f65c0825SSujith Manoharan .write = write_file_spectral_short_repeat, 879f65c0825SSujith Manoharan .open = simple_open, 880f65c0825SSujith Manoharan .owner = THIS_MODULE, 881f65c0825SSujith Manoharan .llseek = default_llseek, 882f65c0825SSujith Manoharan }; 883f65c0825SSujith Manoharan 884f65c0825SSujith Manoharan /******************/ 885f65c0825SSujith Manoharan /* spectral_count */ 886f65c0825SSujith Manoharan /******************/ 887f65c0825SSujith Manoharan 888f65c0825SSujith Manoharan static ssize_t read_file_spectral_count(struct file *file, 889f65c0825SSujith Manoharan char __user *user_buf, 890f65c0825SSujith Manoharan size_t count, loff_t *ppos) 891f65c0825SSujith Manoharan { 8921111d426SOleksij Rempel struct ath_spec_scan_priv *spec_priv = file->private_data; 893f65c0825SSujith Manoharan char buf[32]; 894f65c0825SSujith Manoharan unsigned int len; 895f65c0825SSujith Manoharan 8961111d426SOleksij Rempel len = sprintf(buf, "%d\n", spec_priv->spec_config.count); 897f65c0825SSujith Manoharan return simple_read_from_buffer(user_buf, count, ppos, buf, len); 898f65c0825SSujith Manoharan } 899f65c0825SSujith Manoharan 900f65c0825SSujith Manoharan static ssize_t write_file_spectral_count(struct file *file, 901f65c0825SSujith Manoharan const char __user *user_buf, 902f65c0825SSujith Manoharan size_t count, loff_t *ppos) 903f65c0825SSujith Manoharan { 9041111d426SOleksij Rempel struct ath_spec_scan_priv *spec_priv = file->private_data; 905f65c0825SSujith Manoharan unsigned long val; 906f65c0825SSujith Manoharan char buf[32]; 907f65c0825SSujith Manoharan ssize_t len; 908f65c0825SSujith Manoharan 909f65c0825SSujith Manoharan len = min(count, sizeof(buf) - 1); 910f65c0825SSujith Manoharan if (copy_from_user(buf, user_buf, len)) 911f65c0825SSujith Manoharan return -EFAULT; 912f65c0825SSujith Manoharan 913f65c0825SSujith Manoharan buf[len] = '\0'; 914f65c0825SSujith Manoharan if (kstrtoul(buf, 0, &val)) 915f65c0825SSujith Manoharan return -EINVAL; 916f65c0825SSujith Manoharan 9173f557202SAndrey Utkin if (val > 255) 918f65c0825SSujith Manoharan return -EINVAL; 919f65c0825SSujith Manoharan 9201111d426SOleksij Rempel spec_priv->spec_config.count = val; 921f65c0825SSujith Manoharan return count; 922f65c0825SSujith Manoharan } 923f65c0825SSujith Manoharan 924f65c0825SSujith Manoharan static const struct file_operations fops_spectral_count = { 925f65c0825SSujith Manoharan .read = read_file_spectral_count, 926f65c0825SSujith Manoharan .write = write_file_spectral_count, 927f65c0825SSujith Manoharan .open = simple_open, 928f65c0825SSujith Manoharan .owner = THIS_MODULE, 929f65c0825SSujith Manoharan .llseek = default_llseek, 930f65c0825SSujith Manoharan }; 931f65c0825SSujith Manoharan 932f65c0825SSujith Manoharan /*******************/ 933f65c0825SSujith Manoharan /* spectral_period */ 934f65c0825SSujith Manoharan /*******************/ 935f65c0825SSujith Manoharan 936f65c0825SSujith Manoharan static ssize_t read_file_spectral_period(struct file *file, 937f65c0825SSujith Manoharan char __user *user_buf, 938f65c0825SSujith Manoharan size_t count, loff_t *ppos) 939f65c0825SSujith Manoharan { 9401111d426SOleksij Rempel struct ath_spec_scan_priv *spec_priv = file->private_data; 941f65c0825SSujith Manoharan char buf[32]; 942f65c0825SSujith Manoharan unsigned int len; 943f65c0825SSujith Manoharan 9441111d426SOleksij Rempel len = sprintf(buf, "%d\n", spec_priv->spec_config.period); 945f65c0825SSujith Manoharan return simple_read_from_buffer(user_buf, count, ppos, buf, len); 946f65c0825SSujith Manoharan } 947f65c0825SSujith Manoharan 948f65c0825SSujith Manoharan static ssize_t write_file_spectral_period(struct file *file, 949f65c0825SSujith Manoharan const char __user *user_buf, 950f65c0825SSujith Manoharan size_t count, loff_t *ppos) 951f65c0825SSujith Manoharan { 9521111d426SOleksij Rempel struct ath_spec_scan_priv *spec_priv = file->private_data; 953f65c0825SSujith Manoharan unsigned long val; 954f65c0825SSujith Manoharan char buf[32]; 955f65c0825SSujith Manoharan ssize_t len; 956f65c0825SSujith Manoharan 957f65c0825SSujith Manoharan len = min(count, sizeof(buf) - 1); 958f65c0825SSujith Manoharan if (copy_from_user(buf, user_buf, len)) 959f65c0825SSujith Manoharan return -EFAULT; 960f65c0825SSujith Manoharan 961f65c0825SSujith Manoharan buf[len] = '\0'; 962f65c0825SSujith Manoharan if (kstrtoul(buf, 0, &val)) 963f65c0825SSujith Manoharan return -EINVAL; 964f65c0825SSujith Manoharan 9653f557202SAndrey Utkin if (val > 255) 966f65c0825SSujith Manoharan return -EINVAL; 967f65c0825SSujith Manoharan 9681111d426SOleksij Rempel spec_priv->spec_config.period = val; 969f65c0825SSujith Manoharan return count; 970f65c0825SSujith Manoharan } 971f65c0825SSujith Manoharan 972f65c0825SSujith Manoharan static const struct file_operations fops_spectral_period = { 973f65c0825SSujith Manoharan .read = read_file_spectral_period, 974f65c0825SSujith Manoharan .write = write_file_spectral_period, 975f65c0825SSujith Manoharan .open = simple_open, 976f65c0825SSujith Manoharan .owner = THIS_MODULE, 977f65c0825SSujith Manoharan .llseek = default_llseek, 978f65c0825SSujith Manoharan }; 979f65c0825SSujith Manoharan 980f65c0825SSujith Manoharan /***********************/ 981f65c0825SSujith Manoharan /* spectral_fft_period */ 982f65c0825SSujith Manoharan /***********************/ 983f65c0825SSujith Manoharan 984f65c0825SSujith Manoharan static ssize_t read_file_spectral_fft_period(struct file *file, 985f65c0825SSujith Manoharan char __user *user_buf, 986f65c0825SSujith Manoharan size_t count, loff_t *ppos) 987f65c0825SSujith Manoharan { 9881111d426SOleksij Rempel struct ath_spec_scan_priv *spec_priv = file->private_data; 989f65c0825SSujith Manoharan char buf[32]; 990f65c0825SSujith Manoharan unsigned int len; 991f65c0825SSujith Manoharan 9921111d426SOleksij Rempel len = sprintf(buf, "%d\n", spec_priv->spec_config.fft_period); 993f65c0825SSujith Manoharan return simple_read_from_buffer(user_buf, count, ppos, buf, len); 994f65c0825SSujith Manoharan } 995f65c0825SSujith Manoharan 996f65c0825SSujith Manoharan static ssize_t write_file_spectral_fft_period(struct file *file, 997f65c0825SSujith Manoharan const char __user *user_buf, 998f65c0825SSujith Manoharan size_t count, loff_t *ppos) 999f65c0825SSujith Manoharan { 10001111d426SOleksij Rempel struct ath_spec_scan_priv *spec_priv = file->private_data; 1001f65c0825SSujith Manoharan unsigned long val; 1002f65c0825SSujith Manoharan char buf[32]; 1003f65c0825SSujith Manoharan ssize_t len; 1004f65c0825SSujith Manoharan 1005f65c0825SSujith Manoharan len = min(count, sizeof(buf) - 1); 1006f65c0825SSujith Manoharan if (copy_from_user(buf, user_buf, len)) 1007f65c0825SSujith Manoharan return -EFAULT; 1008f65c0825SSujith Manoharan 1009f65c0825SSujith Manoharan buf[len] = '\0'; 1010f65c0825SSujith Manoharan if (kstrtoul(buf, 0, &val)) 1011f65c0825SSujith Manoharan return -EINVAL; 1012f65c0825SSujith Manoharan 10133f557202SAndrey Utkin if (val > 15) 1014f65c0825SSujith Manoharan return -EINVAL; 1015f65c0825SSujith Manoharan 10161111d426SOleksij Rempel spec_priv->spec_config.fft_period = val; 1017f65c0825SSujith Manoharan return count; 1018f65c0825SSujith Manoharan } 1019f65c0825SSujith Manoharan 1020f65c0825SSujith Manoharan static const struct file_operations fops_spectral_fft_period = { 1021f65c0825SSujith Manoharan .read = read_file_spectral_fft_period, 1022f65c0825SSujith Manoharan .write = write_file_spectral_fft_period, 1023f65c0825SSujith Manoharan .open = simple_open, 1024f65c0825SSujith Manoharan .owner = THIS_MODULE, 1025f65c0825SSujith Manoharan .llseek = default_llseek, 1026f65c0825SSujith Manoharan }; 1027f65c0825SSujith Manoharan 1028f65c0825SSujith Manoharan /*******************/ 1029f65c0825SSujith Manoharan /* Relay interface */ 1030f65c0825SSujith Manoharan /*******************/ 1031f65c0825SSujith Manoharan 1032f65c0825SSujith Manoharan static struct dentry *create_buf_file_handler(const char *filename, 1033f65c0825SSujith Manoharan struct dentry *parent, 1034f65c0825SSujith Manoharan umode_t mode, 1035f65c0825SSujith Manoharan struct rchan_buf *buf, 1036f65c0825SSujith Manoharan int *is_global) 1037f65c0825SSujith Manoharan { 1038f65c0825SSujith Manoharan struct dentry *buf_file; 1039f65c0825SSujith Manoharan 1040f65c0825SSujith Manoharan buf_file = debugfs_create_file(filename, mode, parent, buf, 1041f65c0825SSujith Manoharan &relay_file_operations); 1042*cd98625bSGreg Kroah-Hartman if (IS_ERR(buf_file)) 1043*cd98625bSGreg Kroah-Hartman return NULL; 1044*cd98625bSGreg Kroah-Hartman 1045f65c0825SSujith Manoharan *is_global = 1; 1046f65c0825SSujith Manoharan return buf_file; 1047f65c0825SSujith Manoharan } 1048f65c0825SSujith Manoharan 1049f65c0825SSujith Manoharan static int remove_buf_file_handler(struct dentry *dentry) 1050f65c0825SSujith Manoharan { 1051f65c0825SSujith Manoharan debugfs_remove(dentry); 1052f65c0825SSujith Manoharan 1053f65c0825SSujith Manoharan return 0; 1054f65c0825SSujith Manoharan } 1055f65c0825SSujith Manoharan 1056f84d1b49SWei Yongjun static struct rchan_callbacks rfs_spec_scan_cb = { 1057f65c0825SSujith Manoharan .create_buf_file = create_buf_file_handler, 1058f65c0825SSujith Manoharan .remove_buf_file = remove_buf_file_handler, 1059f65c0825SSujith Manoharan }; 1060f65c0825SSujith Manoharan 1061f65c0825SSujith Manoharan /*********************/ 1062f65c0825SSujith Manoharan /* Debug Init/Deinit */ 1063f65c0825SSujith Manoharan /*********************/ 1064f65c0825SSujith Manoharan 106567dc74f1SOleksij Rempel void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv) 1066f65c0825SSujith Manoharan { 10671077ec47SChristian Lamparter if (spec_priv->rfs_chan_spec_scan) { 10681111d426SOleksij Rempel relay_close(spec_priv->rfs_chan_spec_scan); 10691111d426SOleksij Rempel spec_priv->rfs_chan_spec_scan = NULL; 1070f65c0825SSujith Manoharan } 1071f65c0825SSujith Manoharan } 107267dc74f1SOleksij Rempel EXPORT_SYMBOL(ath9k_cmn_spectral_deinit_debug); 1073f65c0825SSujith Manoharan 107467dc74f1SOleksij Rempel void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv, 107567dc74f1SOleksij Rempel struct dentry *debugfs_phy) 1076f65c0825SSujith Manoharan { 10771111d426SOleksij Rempel spec_priv->rfs_chan_spec_scan = relay_open("spectral_scan", 1078c10b75afSOleksij Rempel debugfs_phy, 1079f65c0825SSujith Manoharan 1024, 256, &rfs_spec_scan_cb, 1080f65c0825SSujith Manoharan NULL); 108140bea976SMiaoqing Pan if (!spec_priv->rfs_chan_spec_scan) 108240bea976SMiaoqing Pan return; 108340bea976SMiaoqing Pan 1084f65c0825SSujith Manoharan debugfs_create_file("spectral_scan_ctl", 10852ef00c53SJoe Perches 0600, 10861111d426SOleksij Rempel debugfs_phy, spec_priv, 1087f65c0825SSujith Manoharan &fops_spec_scan_ctl); 1088f65c0825SSujith Manoharan debugfs_create_file("spectral_short_repeat", 10892ef00c53SJoe Perches 0600, 10901111d426SOleksij Rempel debugfs_phy, spec_priv, 1091f65c0825SSujith Manoharan &fops_spectral_short_repeat); 1092f65c0825SSujith Manoharan debugfs_create_file("spectral_count", 10932ef00c53SJoe Perches 0600, 10941111d426SOleksij Rempel debugfs_phy, spec_priv, 1095f65c0825SSujith Manoharan &fops_spectral_count); 1096f65c0825SSujith Manoharan debugfs_create_file("spectral_period", 10972ef00c53SJoe Perches 0600, 10981111d426SOleksij Rempel debugfs_phy, spec_priv, 1099f65c0825SSujith Manoharan &fops_spectral_period); 1100f65c0825SSujith Manoharan debugfs_create_file("spectral_fft_period", 11012ef00c53SJoe Perches 0600, 11021111d426SOleksij Rempel debugfs_phy, spec_priv, 1103f65c0825SSujith Manoharan &fops_spectral_fft_period); 1104f65c0825SSujith Manoharan } 110567dc74f1SOleksij Rempel EXPORT_SYMBOL(ath9k_cmn_spectral_init_debug); 1106