/* * Copyright (c) 2013 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* Don't try to read something outside the read buffer * in case of a missing byte (so bins[0] will be outside * the read buffer)
*/ if (bytes_read < SPECTRAL_HT20_SAMPLE_LEN && max_index < 1) return -1;
/* Don't try to read something outside the read buffer * in case of a missing byte (so bins[0] will be outside * the read buffer)
*/ if (bytes_read < SPECTRAL_HT20_40_SAMPLE_LEN &&
((upper_max_index < 1) || (lower_max_index < 1))) return -1;
if ((fft_sample_20.data[max_index] & 0xf8) !=
((magnitude >> max_exp) & 0xf8)) {
ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n");
ret = -1;
}
/* DC value (value in the middle) is the blind spot of the spectral * sample and invalid, interpolate it.
*/
fft_sample_20.data[dc_pos] = (fft_sample_20.data[dc_pos + 1] +
fft_sample_20.data[dc_pos - 1]) / 2;
/* Check if the maximum magnitude is indeed maximum, * also if the maximum value was at dc_pos, calculate * a new one (since value at dc_pos is invalid).
*/ if (max_index == dc_pos) {
tmp_mag = 0; for (i = 0; i < dc_pos; i++) { if (fft_sample_20.data[i] > tmp_mag) {
tmp_mag = fft_sample_20.data[i];
fft_sample_20.max_index = i;
}
}
ath_dbg(common, SPECTRAL_SCAN, "Calculated new lower max 0x%X at %i\n",
tmp_mag, fft_sample_20.max_index);
} else for (i = 0; i < SPECTRAL_HT20_NUM_BINS; i++) { if (fft_sample_20.data[i] == (magnitude >> max_exp))
ath_dbg(common, SPECTRAL_SCAN, "Got max: 0x%X at index %i\n",
fft_sample_20.data[i], i);
if (fft_sample_20.data[i] > (magnitude >> max_exp)) {
ath_dbg(common, SPECTRAL_SCAN, "Got bin %i greater than max: 0x%X\n",
i, fft_sample_20.data[i]);
ret = -1;
}
}
/* Check if we got the expected magnitude values at * the expected bins
*/ if (((fft_sample_40.data[upper_max_index + dc_pos] & 0xf8)
!= ((upper_mag >> max_exp) & 0xf8)) ||
((fft_sample_40.data[lower_max_index] & 0xf8)
!= ((lower_mag >> max_exp) & 0xf8))) {
ath_dbg(common, SPECTRAL_SCAN, "Magnitude mismatch !\n");
ret = -1;
}
/* DC value (value in the middle) is the blind spot of the spectral * sample and invalid, interpolate it.
*/
fft_sample_40.data[dc_pos] = (fft_sample_40.data[dc_pos + 1] +
fft_sample_40.data[dc_pos - 1]) / 2;
/* Check if the maximum magnitudes are indeed maximum, * also if the maximum value was at dc_pos, calculate * a new one (since value at dc_pos is invalid).
*/ if (lower_max_index == dc_pos) {
tmp_mag = 0; for (i = 0; i < dc_pos; i++) { if (fft_sample_40.data[i] > tmp_mag) {
tmp_mag = fft_sample_40.data[i];
fft_sample_40.lower_max_index = i;
}
}
ath_dbg(common, SPECTRAL_SCAN, "Calculated new lower max 0x%X at %i\n",
tmp_mag, fft_sample_40.lower_max_index);
} else for (i = 0; i < dc_pos; i++) { if (fft_sample_40.data[i] == (lower_mag >> max_exp))
ath_dbg(common, SPECTRAL_SCAN, "Got lower mag: 0x%X at index %i\n",
fft_sample_40.data[i], i);
if (fft_sample_40.data[i] > (lower_mag >> max_exp)) {
ath_dbg(common, SPECTRAL_SCAN, "Got lower bin %i higher than max: 0x%X\n",
i, fft_sample_40.data[i]);
ret = -1;
}
}
if (upper_max_index == dc_pos) {
tmp_mag = 0; for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { if (fft_sample_40.data[i] > tmp_mag) {
tmp_mag = fft_sample_40.data[i];
fft_sample_40.upper_max_index = i;
}
}
upper_mag = tmp_mag << max_exp;
fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
ath_dbg(common, SPECTRAL_SCAN, "Calculated new upper max 0x%X at %i\n",
tmp_mag, fft_sample_40.upper_max_index);
} else for (i = dc_pos; i < SPECTRAL_HT20_40_NUM_BINS; i++) { if (fft_sample_40.data[i] == (upper_mag >> max_exp))
ath_dbg(common, SPECTRAL_SCAN, "Got upper mag: 0x%X at index %i\n",
fft_sample_40.data[i], i);
if (fft_sample_40.data[i] > (upper_mag >> max_exp)) {
ath_dbg(common, SPECTRAL_SCAN, "Got upper bin %i higher than max: 0x%X\n",
i, fft_sample_40.data[i]);
ret = -1;
}
}
if (ret < 0) return ret;
tlv = (struct fft_sample_tlv *)&fft_sample_40;
ath_debug_send_fft_sample(spec_priv, tlv);
return 0;
}
staticinlinevoid
ath_cmn_copy_fft_frame(u8 *in, u8 *out, int sample_len, int sample_bytes)
{ switch (sample_bytes - sample_len) { case -1: /* First byte missing */
memcpy(&out[1], in,
sample_len - 1); break; case 0: /* Length correct, nothing to do. */
memcpy(out, in, sample_len); break; case 1: /* MAC added 2 extra bytes AND first byte * is missing.
*/
memcpy(&out[1], in, 30);
out[31] = in[31];
memcpy(&out[32], &in[33],
sample_len - 32); break; case 2: /* MAC added 2 extra bytes at bin 30 and 32, * remove them.
*/
memcpy(out, in, 30);
out[30] = in[31];
memcpy(&out[31], &in[33],
sample_len - 31); break; default: break;
}
}
staticint
ath_cmn_is_fft_buf_full(struct ath_spec_scan_priv *spec_priv)
{ int i = 0; int ret = 0; struct rchan_buf *buf; struct rchan *rc = spec_priv->rfs_chan_spec_scan;
for_each_possible_cpu(i) { if ((buf = *per_cpu_ptr(rc->buf, i))) {
ret += relay_buf_full(buf);
}
}
if (ret) return 1; else return 0;
}
/* returns 1 if this was a spectral frame, even if not handled. */ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_hdr *hdr, struct ath_rx_status *rs, u64 tsf)
{
u8 sample_buf[SPECTRAL_SAMPLE_MAX_LEN] = {0}; struct ath_hw *ah = spec_priv->ah; struct ath_common *common = ath9k_hw_common(spec_priv->ah); struct ath_softc *sc = common->priv;
u8 num_bins, *vdata = (u8 *)hdr; struct ath_radar_info *radar_info; int len = rs->rs_datalen; int i; int got_slen = 0;
u8 *sample_start; int sample_bytes = 0; int ret = 0;
u16 fft_len, sample_len, freq = ah->curchan->chan->center_freq; enum nl80211_channel_type chan_type;
ath_cmn_fft_idx_validator *fft_idx_validator;
ath_cmn_fft_sample_handler *fft_handler;
/* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT * yet, but this is supposed to be possible as well.
*/ if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) return 0;
/* check if spectral scan bit is set. This does not have to be checked * if received through a SPECTRAL phy error, but shouldn't hurt.
*/
radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) return 0;
if (!spec_priv->rfs_chan_spec_scan) return 1;
/* Output buffers are full, no need to process anything * since there is no space to put the result anyway
*/
ret = ath_cmn_is_fft_buf_full(spec_priv); if (ret == 1) {
ath_dbg(common, SPECTRAL_SCAN, "FFT report ignored, no space " "left on output buffers\n"); return 1;
}
ath_dbg(common, SPECTRAL_SCAN, "Got radar dump bw_info: 0x%X," "len: %i fft_len: %i\n",
radar_info->pulse_bw_info,
len,
fft_len);
sample_start = vdata; for (i = 0; i < len - 2; i++) {
sample_bytes++;
/* Only a single sample received, no need to look * for the sample's end, do the correction based * on the packet's length instead. Note that hw * will always put the radar_info structure on * the end.
*/ if (len <= fft_len + 2) {
sample_bytes = len - sizeof(struct ath_radar_info);
got_slen = 1;
}
/* Search for the end of the FFT frame between * sample_len - 1 and sample_len + 2. exp_max is 3 * bits long and it's the only value on the last * byte of the frame so since it'll be smaller than * the next byte (the first bin of the next sample) * 90% of the time, we can use it as a separator.
*/ if (vdata[i] <= 0x7 && sample_bytes >= sample_len - 1) {
/* Got a frame length within boundaries, there are * four scenarios here: * * a) sample_len -> We got the correct length * b) sample_len + 2 -> 2 bytes added around bin[31] * c) sample_len - 1 -> The first byte is missing * d) sample_len + 1 -> b + c at the same time * * When MAC adds 2 extra bytes, bin[31] and bin[32] * have the same value, so we can use that for further * verification in cases b and d.
*/
/* Did we go too far ? If so we couldn't determine * this sample's boundaries, discard any further * data
*/ if ((sample_bytes > sample_len + 2) ||
((sample_bytes > sample_len) &&
(sample_start[31] != sample_start[32]))) break;
/* See if we got a valid frame by checking the * consistency of mag_info fields. This is to * prevent from "fixing" a correct frame. * Failure is non-fatal, later frames may * be valid.
*/ if (!fft_idx_validator(&vdata[i], i)) {
ath_dbg(common, SPECTRAL_SCAN, "Found valid fft frame at %i\n", i);
got_slen = 1;
}
/* Try to distinguish cases a and c */ elseif ((sample_bytes == sample_len - 1) &&
(vdata[i + 1] <= 0x7)) continue;
got_slen = 1;
}
if (got_slen) {
ath_dbg(common, SPECTRAL_SCAN, "FFT frame len: %i\n",
sample_bytes);
/* Only try to fix a frame if it's the only one * on the report, else just skip it.
*/ if (sample_bytes != sample_len && len <= fft_len + 2) {
ath_cmn_copy_fft_frame(sample_start,
sample_buf, sample_len,
sample_bytes);
ret = fft_handler(rs, spec_priv, sample_buf,
tsf, freq, chan_type);
if (ret == 0)
RX_STAT_INC(sc, rx_spectral_sample_good); else
RX_STAT_INC(sc, rx_spectral_sample_err);
/* Mix the received bins to the /dev/random * pool
*/
add_device_randomness(sample_buf, num_bins);
memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN);
}
/* Process a normal frame */ if (sample_bytes == sample_len) {
ret = fft_handler(rs, spec_priv, sample_start,
tsf, freq, chan_type);
if (ret == 0)
RX_STAT_INC(sc, rx_spectral_sample_good); else
RX_STAT_INC(sc, rx_spectral_sample_err);
/* Mix the received bins to the /dev/random * pool
*/
add_device_randomness(sample_start, num_bins);
}
/* Short report processed, break out of the * loop.
*/ if (len <= fft_len + 2) return 1;
sample_start = &vdata[i + 1];
/* -1 to grab sample_len -1, -2 since * they 'll get increased by one. In case * of failure try to recover by going byte * by byte instead.
*/ if (ret == 0) {
i += num_bins - 2;
sample_bytes = num_bins - 2;
}
got_slen = 0;
}
}
i -= num_bins - 2; if (len - i != sizeof(struct ath_radar_info))
ath_dbg(common, SPECTRAL_SCAN, "FFT report truncated" "(bytes left: %i)\n",
len - i); return 1;
}
EXPORT_SYMBOL(ath_cmn_process_fft);
/* TODO: usually this should not be necessary, but for some reason * (or in some mode?) the trigger must be called after the * configuration, otherwise the register will have its values reset * (on my ar9220 to value 0x01002310)
*/
ath9k_cmn_spectral_scan_config(common, spec_priv, spec_priv->spectral_mode);
ath9k_hw_ops(ah)->spectral_scan_trigger(ah);
ath_ps_ops(common)->restore(common);
}
EXPORT_SYMBOL(ath9k_cmn_spectral_scan_trigger);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.