1085 lines · c
1/*2 * Copyright (c) 2013 Qualcomm Atheros, Inc.3 *4 * Permission to use, copy, modify, and/or distribute this software for any5 * purpose with or without fee is hereby granted, provided that the above6 * copyright notice and this permission notice appear in all copies.7 *8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.15 */16 17#include <linux/relay.h>18#include <linux/random.h>19#include "ath9k.h"20 21static s8 fix_rssi_inv_only(u8 rssi_val)22{23 if (rssi_val == 128)24 rssi_val = 0;25 return (s8) rssi_val;26}27 28static void ath_debug_send_fft_sample(struct ath_spec_scan_priv *spec_priv,29 struct fft_sample_tlv *fft_sample_tlv)30{31 int length;32 if (!spec_priv->rfs_chan_spec_scan)33 return;34 35 length = __be16_to_cpu(fft_sample_tlv->length) +36 sizeof(*fft_sample_tlv);37 relay_write(spec_priv->rfs_chan_spec_scan, fft_sample_tlv, length);38}39 40typedef int (ath_cmn_fft_idx_validator) (u8 *sample_end, int bytes_read);41 42static int43ath_cmn_max_idx_verify_ht20_fft(u8 *sample_end, int bytes_read)44{45 struct ath_ht20_mag_info *mag_info;46 u8 *sample;47 u16 max_magnitude;48 u8 max_index;49 u8 max_exp;50 51 /* Sanity check so that we don't read outside the read52 * buffer53 */54 if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN - 1)55 return -1;56 57 mag_info = (struct ath_ht20_mag_info *) (sample_end -58 sizeof(struct ath_ht20_mag_info) + 1);59 60 sample = sample_end - SPECTRAL_HT20_SAMPLE_LEN + 1;61 62 max_index = spectral_max_index_ht20(mag_info->all_bins);63 max_magnitude = spectral_max_magnitude(mag_info->all_bins);64 65 max_exp = mag_info->max_exp & 0xf;66 67 /* Don't try to read something outside the read buffer68 * in case of a missing byte (so bins[0] will be outside69 * the read buffer)70 */71 if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN && max_index < 1)72 return -1;73 74 if ((sample[max_index] & 0xf8) != ((max_magnitude >> max_exp) & 0xf8))75 return -1;76 else77 return 0;78}79 80static int81ath_cmn_max_idx_verify_ht20_40_fft(u8 *sample_end, int bytes_read)82{83 struct ath_ht20_40_mag_info *mag_info;84 u8 *sample;85 u16 lower_mag, upper_mag;86 u8 lower_max_index, upper_max_index;87 u8 max_exp;88 int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2;89 90 /* Sanity check so that we don't read outside the read91 * buffer92 */93 if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN - 1)94 return -1;95 96 mag_info = (struct ath_ht20_40_mag_info *) (sample_end -97 sizeof(struct ath_ht20_40_mag_info) + 1);98 99 sample = sample_end - SPECTRAL_HT20_40_SAMPLE_LEN + 1;100 101 lower_mag = spectral_max_magnitude(mag_info->lower_bins);102 lower_max_index = spectral_max_index_ht40(mag_info->lower_bins);103 104 upper_mag = spectral_max_magnitude(mag_info->upper_bins);105 upper_max_index = spectral_max_index_ht40(mag_info->upper_bins);106 107 max_exp = mag_info->max_exp & 0xf;108 109 /* Don't try to read something outside the read buffer110 * in case of a missing byte (so bins[0] will be outside111 * the read buffer)112 */113 if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN &&114 ((upper_max_index < 1) || (lower_max_index < 1)))115 return -1;116 117 if (((sample[upper_max_index + dc_pos] & 0xf8) !=118 ((upper_mag >> max_exp) & 0xf8)) ||119 ((sample[lower_max_index] & 0xf8) !=120 ((lower_mag >> max_exp) & 0xf8)))121 return -1;122 else123 return 0;124}125 126typedef int (ath_cmn_fft_sample_handler) (struct ath_rx_status *rs,127 struct ath_spec_scan_priv *spec_priv,128 u8 *sample_buf, u64 tsf, u16 freq, int chan_type);129 130static int131ath_cmn_process_ht20_fft(struct ath_rx_status *rs,132 struct ath_spec_scan_priv *spec_priv,133 u8 *sample_buf,134 u64 tsf, u16 freq, int chan_type)135{136 struct fft_sample_ht20 fft_sample_20;137 struct ath_common *common = ath9k_hw_common(spec_priv->ah);138 struct ath_hw *ah = spec_priv->ah;139 struct ath_ht20_mag_info *mag_info;140 struct fft_sample_tlv *tlv;141 int i = 0;142 int ret = 0;143 int dc_pos = SPECTRAL_HT20_NUM_BINS / 2;144 u16 magnitude, tmp_mag, length;145 u8 max_index, bitmap_w, max_exp;146 147 length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);148 fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;149 fft_sample_20.tlv.length = __cpu_to_be16(length);150 fft_sample_20.freq = __cpu_to_be16(freq);151 fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);152 fft_sample_20.noise = ah->noise;153 154 mag_info = (struct ath_ht20_mag_info *) (sample_buf +155 SPECTRAL_HT20_NUM_BINS);156 157 magnitude = spectral_max_magnitude(mag_info->all_bins);158 fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);159 160 max_index = spectral_max_index_ht20(mag_info->all_bins);161 fft_sample_20.max_index = max_index;162 163 bitmap_w = spectral_bitmap_weight(mag_info->all_bins);164 fft_sample_20.bitmap_weight = bitmap_w;165 166 max_exp = mag_info->max_exp & 0xf;167 fft_sample_20.max_exp = max_exp;168 169 fft_sample_20.tsf = __cpu_to_be64(tsf);170 171 memcpy(fft_sample_20.data, sample_buf, SPECTRAL_HT20_NUM_BINS);172 173 ath_dbg(common, SPECTRAL_SCAN, "FFT HT20 frame: max mag 0x%X,"174 "max_mag_idx %i\n",175 magnitude >> max_exp,176 max_index);177 178 if ((fft_sample_20.data[max_index] & 0xf8) !=179 ((magnitude >> max_exp) & 0xf8)) {180 ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n");181 ret = -1;182 }183 184 /* DC value (value in the middle) is the blind spot of the spectral185 * sample and invalid, interpolate it.186 */187 fft_sample_20.data[dc_pos] = (fft_sample_20.data[dc_pos + 1] +188 fft_sample_20.data[dc_pos - 1]) / 2;189 190 /* Check if the maximum magnitude is indeed maximum,191 * also if the maximum value was at dc_pos, calculate192 * a new one (since value at dc_pos is invalid).193 */194 if (max_index == dc_pos) {195 tmp_mag = 0;196 for (i = 0; i < dc_pos; i++) {197 if (fft_sample_20.data[i] > tmp_mag) {198 tmp_mag = fft_sample_20.data[i];199 fft_sample_20.max_index = i;200 }201 }202 203 magnitude = tmp_mag << max_exp;204 fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);205 206 ath_dbg(common, SPECTRAL_SCAN,207 "Calculated new lower max 0x%X at %i\n",208 tmp_mag, fft_sample_20.max_index);209 } else210 for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++) {211 if (fft_sample_20.data[i] == (magnitude >> max_exp))212 ath_dbg(common, SPECTRAL_SCAN,213 "Got max: 0x%X at index %i\n",214 fft_sample_20.data[i], i);215 216 if (fft_sample_20.data[i] > (magnitude >> max_exp)) {217 ath_dbg(common, SPECTRAL_SCAN,218 "Got bin %i greater than max: 0x%X\n",219 i, fft_sample_20.data[i]);220 ret = -1;221 }222 }223 224 if (ret < 0)225 return ret;226 227 tlv = (struct fft_sample_tlv *)&fft_sample_20;228 229 ath_debug_send_fft_sample(spec_priv, tlv);230 231 return 0;232}233 234static int235ath_cmn_process_ht20_40_fft(struct ath_rx_status *rs,236 struct ath_spec_scan_priv *spec_priv,237 u8 *sample_buf,238 u64 tsf, u16 freq, int chan_type)239{240 struct fft_sample_ht20_40 fft_sample_40;241 struct ath_common *common = ath9k_hw_common(spec_priv->ah);242 struct ath_hw *ah = spec_priv->ah;243 struct ath9k_hw_cal_data *caldata = ah->caldata;244 struct ath_ht20_40_mag_info *mag_info;245 struct fft_sample_tlv *tlv;246 int dc_pos = SPECTRAL_HT20_40_NUM_BINS / 2;247 int i = 0;248 int ret = 0;249 s16 ext_nf;250 u16 lower_mag, upper_mag, tmp_mag, length;251 s8 lower_rssi, upper_rssi;252 u8 lower_max_index, upper_max_index;253 u8 lower_bitmap_w, upper_bitmap_w, max_exp;254 255 if (caldata)256 ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,257 caldata->nfCalHist[3].privNF);258 else259 ext_nf = ATH_DEFAULT_NOISE_FLOOR;260 261 length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);262 fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;263 fft_sample_40.tlv.length = __cpu_to_be16(length);264 fft_sample_40.freq = __cpu_to_be16(freq);265 fft_sample_40.channel_type = chan_type;266 267 if (chan_type == NL80211_CHAN_HT40PLUS) {268 lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);269 upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);270 271 fft_sample_40.lower_noise = ah->noise;272 fft_sample_40.upper_noise = ext_nf;273 } else {274 lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);275 upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);276 277 fft_sample_40.lower_noise = ext_nf;278 fft_sample_40.upper_noise = ah->noise;279 }280 281 fft_sample_40.lower_rssi = lower_rssi;282 fft_sample_40.upper_rssi = upper_rssi;283 284 mag_info = (struct ath_ht20_40_mag_info *) (sample_buf +285 SPECTRAL_HT20_40_NUM_BINS);286 287 lower_mag = spectral_max_magnitude(mag_info->lower_bins);288 fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);289 290 upper_mag = spectral_max_magnitude(mag_info->upper_bins);291 fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);292 293 lower_max_index = spectral_max_index_ht40(mag_info->lower_bins);294 fft_sample_40.lower_max_index = lower_max_index;295 296 upper_max_index = spectral_max_index_ht40(mag_info->upper_bins);297 fft_sample_40.upper_max_index = upper_max_index;298 299 lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);300 fft_sample_40.lower_bitmap_weight = lower_bitmap_w;301 302 upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);303 fft_sample_40.upper_bitmap_weight = upper_bitmap_w;304 305 max_exp = mag_info->max_exp & 0xf;306 fft_sample_40.max_exp = max_exp;307 308 fft_sample_40.tsf = __cpu_to_be64(tsf);309 310 memcpy(fft_sample_40.data, sample_buf, SPECTRAL_HT20_40_NUM_BINS);311 312 ath_dbg(common, SPECTRAL_SCAN, "FFT HT20/40 frame: lower mag 0x%X,"313 "lower_mag_idx %i, upper mag 0x%X,"314 "upper_mag_idx %i\n",315 lower_mag >> max_exp,316 lower_max_index,317 upper_mag >> max_exp,318 upper_max_index);319 320 /* Check if we got the expected magnitude values at321 * the expected bins322 */323 if (((fft_sample_40.data[upper_max_index + dc_pos] & 0xf8)324 != ((upper_mag >> max_exp) & 0xf8)) ||325 ((fft_sample_40.data[lower_max_index] & 0xf8)326 != ((lower_mag >> max_exp) & 0xf8))) {327 ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n");328 ret = -1;329 }330 331 /* DC value (value in the middle) is the blind spot of the spectral332 * sample and invalid, interpolate it.333 */334 fft_sample_40.data[dc_pos] = (fft_sample_40.data[dc_pos + 1] +335 fft_sample_40.data[dc_pos - 1]) / 2;336 337 /* Check if the maximum magnitudes are indeed maximum,338 * also if the maximum value was at dc_pos, calculate339 * a new one (since value at dc_pos is invalid).340 */341 if (lower_max_index == dc_pos) {342 tmp_mag = 0;343 for (i = 0; i < dc_pos; i++) {344 if (fft_sample_40.data[i] > tmp_mag) {345 tmp_mag = fft_sample_40.data[i];346 fft_sample_40.lower_max_index = i;347 }348 }349 350 lower_mag = tmp_mag << max_exp;351 fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);352 353 ath_dbg(common, SPECTRAL_SCAN,354 "Calculated new lower max 0x%X at %i\n",355 tmp_mag, fft_sample_40.lower_max_index);356 } else357 for (i = 0; i < dc_pos; i++) {358 if (fft_sample_40.data[i] == (lower_mag >> max_exp))359 ath_dbg(common, SPECTRAL_SCAN,360 "Got lower mag: 0x%X at index %i\n",361 fft_sample_40.data[i], i);362 363 if (fft_sample_40.data[i] > (lower_mag >> max_exp)) {364 ath_dbg(common, SPECTRAL_SCAN,365 "Got lower bin %i higher than max: 0x%X\n",366 i, fft_sample_40.data[i]);367 ret = -1;368 }369 }370 371 if (upper_max_index == dc_pos) {372 tmp_mag = 0;373 for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) {374 if (fft_sample_40.data[i] > tmp_mag) {375 tmp_mag = fft_sample_40.data[i];376 fft_sample_40.upper_max_index = i;377 }378 }379 upper_mag = tmp_mag << max_exp;380 fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);381 382 ath_dbg(common, SPECTRAL_SCAN,383 "Calculated new upper max 0x%X at %i\n",384 tmp_mag, fft_sample_40.upper_max_index);385 } else386 for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) {387 if (fft_sample_40.data[i] == (upper_mag >> max_exp))388 ath_dbg(common, SPECTRAL_SCAN,389 "Got upper mag: 0x%X at index %i\n",390 fft_sample_40.data[i], i);391 392 if (fft_sample_40.data[i] > (upper_mag >> max_exp)) {393 ath_dbg(common, SPECTRAL_SCAN,394 "Got upper bin %i higher than max: 0x%X\n",395 i, fft_sample_40.data[i]);396 397 ret = -1;398 }399 }400 401 if (ret < 0)402 return ret;403 404 tlv = (struct fft_sample_tlv *)&fft_sample_40;405 406 ath_debug_send_fft_sample(spec_priv, tlv);407 408 return 0;409}410 411static inline void412ath_cmn_copy_fft_frame(u8 *in, u8 *out, int sample_len, int sample_bytes)413{414 switch (sample_bytes - sample_len) {415 case -1:416 /* First byte missing */417 memcpy(&out[1], in,418 sample_len - 1);419 break;420 case 0:421 /* Length correct, nothing to do. */422 memcpy(out, in, sample_len);423 break;424 case 1:425 /* MAC added 2 extra bytes AND first byte426 * is missing.427 */428 memcpy(&out[1], in, 30);429 out[31] = in[31];430 memcpy(&out[32], &in[33],431 sample_len - 32);432 break;433 case 2:434 /* MAC added 2 extra bytes at bin 30 and 32,435 * remove them.436 */437 memcpy(out, in, 30);438 out[30] = in[31];439 memcpy(&out[31], &in[33],440 sample_len - 31);441 break;442 default:443 break;444 }445}446 447static int448ath_cmn_is_fft_buf_full(struct ath_spec_scan_priv *spec_priv)449{450 int i = 0;451 int ret = 0;452 struct rchan_buf *buf;453 struct rchan *rc = spec_priv->rfs_chan_spec_scan;454 455 for_each_possible_cpu(i) {456 if ((buf = *per_cpu_ptr(rc->buf, i))) {457 ret += relay_buf_full(buf);458 }459 }460 461 if (ret)462 return 1;463 else464 return 0;465}466 467/* returns 1 if this was a spectral frame, even if not handled. */468int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr,469 struct ath_rx_status *rs, u64 tsf)470{471 u8 sample_buf[SPECTRAL_SAMPLE_MAX_LEN] = {0};472 struct ath_hw *ah = spec_priv->ah;473 struct ath_common *common = ath9k_hw_common(spec_priv->ah);474 struct ath_softc *sc = common->priv;475 u8 num_bins, *vdata = (u8 *)hdr;476 struct ath_radar_info *radar_info;477 int len = rs->rs_datalen;478 int i;479 int got_slen = 0;480 u8 *sample_start;481 int sample_bytes = 0;482 int ret = 0;483 u16 fft_len, sample_len, freq = ah->curchan->chan->center_freq;484 enum nl80211_channel_type chan_type;485 ath_cmn_fft_idx_validator *fft_idx_validator;486 ath_cmn_fft_sample_handler *fft_handler;487 488 /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer489 * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT490 * yet, but this is supposed to be possible as well.491 */492 if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&493 rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&494 rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)495 return 0;496 497 /* check if spectral scan bit is set. This does not have to be checked498 * if received through a SPECTRAL phy error, but shouldn't hurt.499 */500 radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;501 if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))502 return 0;503 504 if (!spec_priv->rfs_chan_spec_scan)505 return 1;506 507 /* Output buffers are full, no need to process anything508 * since there is no space to put the result anyway509 */510 ret = ath_cmn_is_fft_buf_full(spec_priv);511 if (ret == 1) {512 ath_dbg(common, SPECTRAL_SCAN, "FFT report ignored, no space "513 "left on output buffers\n");514 return 1;515 }516 517 chan_type = cfg80211_get_chandef_type(&common->hw->conf.chandef);518 if ((chan_type == NL80211_CHAN_HT40MINUS) ||519 (chan_type == NL80211_CHAN_HT40PLUS)) {520 fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;521 sample_len = SPECTRAL_HT20_40_SAMPLE_LEN;522 num_bins = SPECTRAL_HT20_40_NUM_BINS;523 fft_idx_validator = &ath_cmn_max_idx_verify_ht20_40_fft;524 fft_handler = &ath_cmn_process_ht20_40_fft;525 } else {526 fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;527 sample_len = SPECTRAL_HT20_SAMPLE_LEN;528 num_bins = SPECTRAL_HT20_NUM_BINS;529 fft_idx_validator = ath_cmn_max_idx_verify_ht20_fft;530 fft_handler = &ath_cmn_process_ht20_fft;531 }532 533 ath_dbg(common, SPECTRAL_SCAN, "Got radar dump bw_info: 0x%X,"534 "len: %i fft_len: %i\n",535 radar_info->pulse_bw_info,536 len,537 fft_len);538 sample_start = vdata;539 for (i = 0; i < len - 2; i++) {540 sample_bytes++;541 542 /* Only a single sample received, no need to look543 * for the sample's end, do the correction based544 * on the packet's length instead. Note that hw545 * will always put the radar_info structure on546 * the end.547 */548 if (len <= fft_len + 2) {549 sample_bytes = len - sizeof(struct ath_radar_info);550 got_slen = 1;551 }552 553 /* Search for the end of the FFT frame between554 * sample_len - 1 and sample_len + 2. exp_max is 3555 * bits long and it's the only value on the last556 * byte of the frame so since it'll be smaller than557 * the next byte (the first bin of the next sample)558 * 90% of the time, we can use it as a separator.559 */560 if (vdata[i] <= 0x7 && sample_bytes >= sample_len - 1) {561 562 /* Got a frame length within boundaries, there are563 * four scenarios here:564 *565 * a) sample_len -> We got the correct length566 * b) sample_len + 2 -> 2 bytes added around bin[31]567 * c) sample_len - 1 -> The first byte is missing568 * d) sample_len + 1 -> b + c at the same time569 *570 * When MAC adds 2 extra bytes, bin[31] and bin[32]571 * have the same value, so we can use that for further572 * verification in cases b and d.573 */574 575 /* Did we go too far ? If so we couldn't determine576 * this sample's boundaries, discard any further577 * data578 */579 if ((sample_bytes > sample_len + 2) ||580 ((sample_bytes > sample_len) &&581 (sample_start[31] != sample_start[32])))582 break;583 584 /* See if we got a valid frame by checking the585 * consistency of mag_info fields. This is to586 * prevent from "fixing" a correct frame.587 * Failure is non-fatal, later frames may588 * be valid.589 */590 if (!fft_idx_validator(&vdata[i], i)) {591 ath_dbg(common, SPECTRAL_SCAN,592 "Found valid fft frame at %i\n", i);593 got_slen = 1;594 }595 596 /* We expect 1 - 2 more bytes */597 else if ((sample_start[31] == sample_start[32]) &&598 (sample_bytes >= sample_len) &&599 (sample_bytes < sample_len + 2) &&600 (vdata[i + 1] <= 0x7))601 continue;602 603 /* Try to distinguish cases a and c */604 else if ((sample_bytes == sample_len - 1) &&605 (vdata[i + 1] <= 0x7))606 continue;607 608 got_slen = 1;609 }610 611 if (got_slen) {612 ath_dbg(common, SPECTRAL_SCAN, "FFT frame len: %i\n",613 sample_bytes);614 615 /* Only try to fix a frame if it's the only one616 * on the report, else just skip it.617 */618 if (sample_bytes != sample_len && len <= fft_len + 2) {619 ath_cmn_copy_fft_frame(sample_start,620 sample_buf, sample_len,621 sample_bytes);622 623 ret = fft_handler(rs, spec_priv, sample_buf,624 tsf, freq, chan_type);625 626 if (ret == 0)627 RX_STAT_INC(sc, rx_spectral_sample_good);628 else629 RX_STAT_INC(sc, rx_spectral_sample_err);630 631 memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN);632 633 /* Mix the received bins to the /dev/random634 * pool635 */636 add_device_randomness(sample_buf, num_bins);637 }638 639 /* Process a normal frame */640 if (sample_bytes == sample_len) {641 ret = fft_handler(rs, spec_priv, sample_start,642 tsf, freq, chan_type);643 644 if (ret == 0)645 RX_STAT_INC(sc, rx_spectral_sample_good);646 else647 RX_STAT_INC(sc, rx_spectral_sample_err);648 649 /* Mix the received bins to the /dev/random650 * pool651 */652 add_device_randomness(sample_start, num_bins);653 }654 655 /* Short report processed, break out of the656 * loop.657 */658 if (len <= fft_len + 2)659 return 1;660 661 sample_start = &vdata[i + 1];662 663 /* -1 to grab sample_len -1, -2 since664 * they 'll get increased by one. In case665 * of failure try to recover by going byte666 * by byte instead.667 */668 if (ret == 0) {669 i += num_bins - 2;670 sample_bytes = num_bins - 2;671 }672 got_slen = 0;673 }674 }675 676 i -= num_bins - 2;677 if (len - i != sizeof(struct ath_radar_info))678 ath_dbg(common, SPECTRAL_SCAN, "FFT report truncated"679 "(bytes left: %i)\n",680 len - i);681 return 1;682}683EXPORT_SYMBOL(ath_cmn_process_fft);684 685/*********************/686/* spectral_scan_ctl */687/*********************/688 689static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,690 size_t count, loff_t *ppos)691{692 struct ath_spec_scan_priv *spec_priv = file->private_data;693 char *mode = "";694 unsigned int len;695 696 switch (spec_priv->spectral_mode) {697 case SPECTRAL_DISABLED:698 mode = "disable";699 break;700 case SPECTRAL_BACKGROUND:701 mode = "background";702 break;703 case SPECTRAL_CHANSCAN:704 mode = "chanscan";705 break;706 case SPECTRAL_MANUAL:707 mode = "manual";708 break;709 }710 len = strlen(mode);711 return simple_read_from_buffer(user_buf, count, ppos, mode, len);712}713 714void ath9k_cmn_spectral_scan_trigger(struct ath_common *common,715 struct ath_spec_scan_priv *spec_priv)716{717 struct ath_hw *ah = spec_priv->ah;718 u32 rxfilter;719 720 if (IS_ENABLED(CONFIG_ATH9K_TX99))721 return;722 723 if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {724 ath_err(common, "spectrum analyzer not implemented on this hardware\n");725 return;726 }727 728 if (!spec_priv->spec_config.enabled)729 return;730 731 ath_ps_ops(common)->wakeup(common);732 rxfilter = ath9k_hw_getrxfilter(ah);733 ath9k_hw_setrxfilter(ah, rxfilter |734 ATH9K_RX_FILTER_PHYRADAR |735 ATH9K_RX_FILTER_PHYERR);736 737 /* TODO: usually this should not be neccesary, but for some reason738 * (or in some mode?) the trigger must be called after the739 * configuration, otherwise the register will have its values reset740 * (on my ar9220 to value 0x01002310)741 */742 ath9k_cmn_spectral_scan_config(common, spec_priv, spec_priv->spectral_mode);743 ath9k_hw_ops(ah)->spectral_scan_trigger(ah);744 ath_ps_ops(common)->restore(common);745}746EXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger);747 748int ath9k_cmn_spectral_scan_config(struct ath_common *common,749 struct ath_spec_scan_priv *spec_priv,750 enum spectral_mode spectral_mode)751{752 struct ath_hw *ah = spec_priv->ah;753 754 if (!ath9k_hw_ops(ah)->spectral_scan_trigger) {755 ath_err(common, "spectrum analyzer not implemented on this hardware\n");756 return -1;757 }758 759 switch (spectral_mode) {760 case SPECTRAL_DISABLED:761 spec_priv->spec_config.enabled = 0;762 break;763 case SPECTRAL_BACKGROUND:764 /* send endless samples.765 * TODO: is this really useful for "background"?766 */767 spec_priv->spec_config.endless = 1;768 spec_priv->spec_config.enabled = 1;769 break;770 case SPECTRAL_CHANSCAN:771 case SPECTRAL_MANUAL:772 spec_priv->spec_config.endless = 0;773 spec_priv->spec_config.enabled = 1;774 break;775 default:776 return -1;777 }778 779 ath_ps_ops(common)->wakeup(common);780 ath9k_hw_ops(ah)->spectral_scan_config(ah, &spec_priv->spec_config);781 ath_ps_ops(common)->restore(common);782 783 spec_priv->spectral_mode = spectral_mode;784 785 return 0;786}787EXPORT_SYMBOL(ath9k_cmn_spectral_scan_config);788 789static ssize_t write_file_spec_scan_ctl(struct file *file,790 const char __user *user_buf,791 size_t count, loff_t *ppos)792{793 struct ath_spec_scan_priv *spec_priv = file->private_data;794 struct ath_common *common = ath9k_hw_common(spec_priv->ah);795 char buf[32];796 ssize_t len;797 798 if (IS_ENABLED(CONFIG_ATH9K_TX99))799 return -EOPNOTSUPP;800 801 len = min(count, sizeof(buf) - 1);802 if (copy_from_user(buf, user_buf, len))803 return -EFAULT;804 805 buf[len] = '\0';806 807 if (strncmp("trigger", buf, 7) == 0) {808 ath9k_cmn_spectral_scan_trigger(common, spec_priv);809 } else if (strncmp("background", buf, 10) == 0) {810 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_BACKGROUND);811 ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");812 } else if (strncmp("chanscan", buf, 8) == 0) {813 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_CHANSCAN);814 ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");815 } else if (strncmp("manual", buf, 6) == 0) {816 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_MANUAL);817 ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");818 } else if (strncmp("disable", buf, 7) == 0) {819 ath9k_cmn_spectral_scan_config(common, spec_priv, SPECTRAL_DISABLED);820 ath_dbg(common, CONFIG, "spectral scan: disabled\n");821 } else {822 return -EINVAL;823 }824 825 return count;826}827 828static const struct file_operations fops_spec_scan_ctl = {829 .read = read_file_spec_scan_ctl,830 .write = write_file_spec_scan_ctl,831 .open = simple_open,832 .owner = THIS_MODULE,833 .llseek = default_llseek,834};835 836/*************************/837/* spectral_short_repeat */838/*************************/839 840static ssize_t read_file_spectral_short_repeat(struct file *file,841 char __user *user_buf,842 size_t count, loff_t *ppos)843{844 struct ath_spec_scan_priv *spec_priv = file->private_data;845 char buf[32];846 unsigned int len;847 848 len = sprintf(buf, "%d\n", spec_priv->spec_config.short_repeat);849 return simple_read_from_buffer(user_buf, count, ppos, buf, len);850}851 852static ssize_t write_file_spectral_short_repeat(struct file *file,853 const char __user *user_buf,854 size_t count, loff_t *ppos)855{856 struct ath_spec_scan_priv *spec_priv = file->private_data;857 unsigned long val;858 ssize_t ret;859 860 ret = kstrtoul_from_user(user_buf, count, 0, &val);861 if (ret)862 return ret;863 864 if (val > 1)865 return -EINVAL;866 867 spec_priv->spec_config.short_repeat = val;868 return count;869}870 871static const struct file_operations fops_spectral_short_repeat = {872 .read = read_file_spectral_short_repeat,873 .write = write_file_spectral_short_repeat,874 .open = simple_open,875 .owner = THIS_MODULE,876 .llseek = default_llseek,877};878 879/******************/880/* spectral_count */881/******************/882 883static ssize_t read_file_spectral_count(struct file *file,884 char __user *user_buf,885 size_t count, loff_t *ppos)886{887 struct ath_spec_scan_priv *spec_priv = file->private_data;888 char buf[32];889 unsigned int len;890 891 len = sprintf(buf, "%d\n", spec_priv->spec_config.count);892 return simple_read_from_buffer(user_buf, count, ppos, buf, len);893}894 895static ssize_t write_file_spectral_count(struct file *file,896 const char __user *user_buf,897 size_t count, loff_t *ppos)898{899 struct ath_spec_scan_priv *spec_priv = file->private_data;900 unsigned long val;901 ssize_t ret;902 903 ret = kstrtoul_from_user(user_buf, count, 0, &val);904 if (ret)905 return ret;906 if (val > 255)907 return -EINVAL;908 909 spec_priv->spec_config.count = val;910 return count;911}912 913static const struct file_operations fops_spectral_count = {914 .read = read_file_spectral_count,915 .write = write_file_spectral_count,916 .open = simple_open,917 .owner = THIS_MODULE,918 .llseek = default_llseek,919};920 921/*******************/922/* spectral_period */923/*******************/924 925static ssize_t read_file_spectral_period(struct file *file,926 char __user *user_buf,927 size_t count, loff_t *ppos)928{929 struct ath_spec_scan_priv *spec_priv = file->private_data;930 char buf[32];931 unsigned int len;932 933 len = sprintf(buf, "%d\n", spec_priv->spec_config.period);934 return simple_read_from_buffer(user_buf, count, ppos, buf, len);935}936 937static ssize_t write_file_spectral_period(struct file *file,938 const char __user *user_buf,939 size_t count, loff_t *ppos)940{941 struct ath_spec_scan_priv *spec_priv = file->private_data;942 unsigned long val;943 ssize_t ret;944 945 ret = kstrtoul_from_user(user_buf, count, 0, &val);946 if (ret)947 return ret;948 949 if (val > 255)950 return -EINVAL;951 952 spec_priv->spec_config.period = val;953 return count;954}955 956static const struct file_operations fops_spectral_period = {957 .read = read_file_spectral_period,958 .write = write_file_spectral_period,959 .open = simple_open,960 .owner = THIS_MODULE,961 .llseek = default_llseek,962};963 964/***********************/965/* spectral_fft_period */966/***********************/967 968static ssize_t read_file_spectral_fft_period(struct file *file,969 char __user *user_buf,970 size_t count, loff_t *ppos)971{972 struct ath_spec_scan_priv *spec_priv = file->private_data;973 char buf[32];974 unsigned int len;975 976 len = sprintf(buf, "%d\n", spec_priv->spec_config.fft_period);977 return simple_read_from_buffer(user_buf, count, ppos, buf, len);978}979 980static ssize_t write_file_spectral_fft_period(struct file *file,981 const char __user *user_buf,982 size_t count, loff_t *ppos)983{984 struct ath_spec_scan_priv *spec_priv = file->private_data;985 unsigned long val;986 ssize_t ret;987 988 ret = kstrtoul_from_user(user_buf, count, 0, &val);989 if (ret)990 return ret;991 992 if (val > 15)993 return -EINVAL;994 995 spec_priv->spec_config.fft_period = val;996 return count;997}998 999static const struct file_operations fops_spectral_fft_period = {1000 .read = read_file_spectral_fft_period,1001 .write = write_file_spectral_fft_period,1002 .open = simple_open,1003 .owner = THIS_MODULE,1004 .llseek = default_llseek,1005};1006 1007/*******************/1008/* Relay interface */1009/*******************/1010 1011static struct dentry *create_buf_file_handler(const char *filename,1012 struct dentry *parent,1013 umode_t mode,1014 struct rchan_buf *buf,1015 int *is_global)1016{1017 struct dentry *buf_file;1018 1019 buf_file = debugfs_create_file(filename, mode, parent, buf,1020 &relay_file_operations);1021 if (IS_ERR(buf_file))1022 return NULL;1023 1024 *is_global = 1;1025 return buf_file;1026}1027 1028static int remove_buf_file_handler(struct dentry *dentry)1029{1030 debugfs_remove(dentry);1031 1032 return 0;1033}1034 1035static const struct rchan_callbacks rfs_spec_scan_cb = {1036 .create_buf_file = create_buf_file_handler,1037 .remove_buf_file = remove_buf_file_handler,1038};1039 1040/*********************/1041/* Debug Init/Deinit */1042/*********************/1043 1044void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv)1045{1046 if (spec_priv->rfs_chan_spec_scan) {1047 relay_close(spec_priv->rfs_chan_spec_scan);1048 spec_priv->rfs_chan_spec_scan = NULL;1049 }1050}1051EXPORT_SYMBOL(ath9k_cmn_spectral_deinit_debug);1052 1053void ath9k_cmn_spectral_init_debug(struct ath_spec_scan_priv *spec_priv,1054 struct dentry *debugfs_phy)1055{1056 spec_priv->rfs_chan_spec_scan = relay_open("spectral_scan",1057 debugfs_phy,1058 1024, 256, &rfs_spec_scan_cb,1059 NULL);1060 if (!spec_priv->rfs_chan_spec_scan)1061 return;1062 1063 debugfs_create_file("spectral_scan_ctl",1064 0600,1065 debugfs_phy, spec_priv,1066 &fops_spec_scan_ctl);1067 debugfs_create_file("spectral_short_repeat",1068 0600,1069 debugfs_phy, spec_priv,1070 &fops_spectral_short_repeat);1071 debugfs_create_file("spectral_count",1072 0600,1073 debugfs_phy, spec_priv,1074 &fops_spectral_count);1075 debugfs_create_file("spectral_period",1076 0600,1077 debugfs_phy, spec_priv,1078 &fops_spectral_period);1079 debugfs_create_file("spectral_fft_period",1080 0600,1081 debugfs_phy, spec_priv,1082 &fops_spectral_fft_period);1083}1084EXPORT_SYMBOL(ath9k_cmn_spectral_init_debug);1085