Skip to content

Commit

Permalink
ath9k: add spectral scan feature
Browse files Browse the repository at this point in the history
Adds the spectral scan feature for ath9k. AR92xx and AR93xx chips
are supported for now. The spectral scan is triggered by configuring
a mode through a debugfs control file. Samples can be gathered via
another relay debugfs file.

Essentially, to try it out:

echo chanscan > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan_ctl
iw dev wlan0 scan
cat /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan0 > samples
echo disable > /sys/kernel/debug/ieee80211/phy0/ath9k/spectral_scan_ctl

This feature is still experimental.

The special "chanscan" mode is used to perform spectral scan while
mac80211 is scanning for channels. To allow this,
sw_scan_start/complete() ops have been added.

The patch contains code snippets and information from Zefir Kurtisi and
information provided by Adrian Chadd and Felix Fietkau.

Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
Signed-off-by: Mathias Kretschmer <mathias.kretschmer@fokus.fraunhofer.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Simon Wunderlich authored and John W. Linville committed Jan 9, 2013
1 parent 1a26cda commit e93d083
Show file tree
Hide file tree
Showing 10 changed files with 653 additions and 1 deletion.
64 changes: 64 additions & 0 deletions drivers/net/wireless/ath/ath9k/ar9002_phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,67 @@ static void ar9002_hw_antdiv_comb_conf_set(struct ath_hw *ah,
REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
}

void ar9002_hw_spectral_scan_config(struct ath_hw *ah,
struct ath_spec_scan *param)
{
u8 count;

if (!param->enabled) {
REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_ENABLE);
return;
}
REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_FFT_ENA);
REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE);

if (param->short_repeat)
REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);
else
REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);

/* on AR92xx, the highest bit of count will make the the chip send
* spectral samples endlessly. Check if this really was intended,
* and fix otherwise.
*/
count = param->count;
if (param->endless)
count = 0;
else if (count & 0x80)
count = 0x7f;

REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_COUNT, count);
REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_PERIOD, param->period);
REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_FFT_PERIOD, param->fft_period);

return;
}

static void ar9002_hw_spectral_scan_trigger(struct ath_hw *ah)
{
REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE);
/* Activate spectral scan */
REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_ACTIVE);
}

static void ar9002_hw_spectral_scan_wait(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);

/* Poll for spectral scan complete */
if (!ath9k_hw_wait(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_ACTIVE,
0, AH_WAIT_TIMEOUT)) {
ath_err(common, "spectral scan wait failed\n");
return;
}
}

void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
{
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
Expand All @@ -569,6 +630,9 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah)

ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get;
ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set;
ops->spectral_scan_config = ar9002_hw_spectral_scan_config;
ops->spectral_scan_trigger = ar9002_hw_spectral_scan_trigger;
ops->spectral_scan_wait = ar9002_hw_spectral_scan_wait;

ar9002_hw_set_nf_limits(ah);
}
64 changes: 64 additions & 0 deletions drivers/net/wireless/ath/ath9k/ar9003_phy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1453,6 +1453,67 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
return 0;
}

static void ar9003_hw_spectral_scan_config(struct ath_hw *ah,
struct ath_spec_scan *param)
{
u8 count;

if (!param->enabled) {
REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_ENABLE);
return;
}

REG_SET_BIT(ah, AR_PHY_RADAR_0, AR_PHY_RADAR_0_FFT_ENA);
REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN, AR_PHY_SPECTRAL_SCAN_ENABLE);

/* on AR93xx and newer, count = 0 will make the the chip send
* spectral samples endlessly. Check if this really was intended,
* and fix otherwise.
*/
count = param->count;
if (param->endless)
count = 0;
else if (param->count == 0)
count = 1;

if (param->short_repeat)
REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);
else
REG_CLR_BIT(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_SHORT_REPEAT);

REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_COUNT, count);
REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_PERIOD, param->period);
REG_RMW_FIELD(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_FFT_PERIOD, param->fft_period);

return;
}

static void ar9003_hw_spectral_scan_trigger(struct ath_hw *ah)
{
/* Activate spectral scan */
REG_SET_BIT(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_ACTIVE);
}

static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);

/* Poll for spectral scan complete */
if (!ath9k_hw_wait(ah, AR_PHY_SPECTRAL_SCAN,
AR_PHY_SPECTRAL_SCAN_ACTIVE,
0, AH_WAIT_TIMEOUT)) {
ath_err(common, "spectral scan wait failed\n");
return;
}
}

void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
{
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
Expand Down Expand Up @@ -1486,6 +1547,9 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
ops->antdiv_comb_conf_get = ar9003_hw_antdiv_comb_conf_get;
ops->antdiv_comb_conf_set = ar9003_hw_antdiv_comb_conf_set;
ops->antctrl_shared_chain_lnadiv = ar9003_hw_antctrl_shared_chain_lnadiv;
ops->spectral_scan_config = ar9003_hw_spectral_scan_config;
ops->spectral_scan_trigger = ar9003_hw_spectral_scan_trigger;
ops->spectral_scan_wait = ar9003_hw_spectral_scan_wait;

ar9003_hw_set_nf_limits(ah);
ar9003_hw_set_radar_conf(ah);
Expand Down
152 changes: 152 additions & 0 deletions drivers/net/wireless/ath/ath9k/ath9k.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,23 @@ struct ath9k_vif_iter_data {
int nadhocs; /* number of adhoc vifs */
};

/* enum spectral_mode:
*
* @SPECTRAL_DISABLED: spectral mode is disabled
* @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with
* something else.
* @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples
* is performed manually.
* @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels
* during a channel scan.
*/
enum spectral_mode {
SPECTRAL_DISABLED = 0,
SPECTRAL_BACKGROUND,
SPECTRAL_MANUAL,
SPECTRAL_CHANSCAN,
};

struct ath_softc {
struct ieee80211_hw *hw;
struct device *dev;
Expand Down Expand Up @@ -738,6 +755,10 @@ struct ath_softc {
u8 ant_tx, ant_rx;
struct dfs_pattern_detector *dfs_detector;
u32 wow_enabled;
/* relay(fs) channel for spectral scan */
struct rchan *rfs_chan_spec_scan;
enum spectral_mode spectral_mode;
int scanning;

#ifdef CONFIG_PM_SLEEP
atomic_t wow_got_bmiss_intr;
Expand All @@ -746,6 +767,133 @@ struct ath_softc {
#endif
};

#define SPECTRAL_SCAN_BITMASK 0x10
/* Radar info packet format, used for DFS and spectral formats. */
struct ath_radar_info {
u8 pulse_length_pri;
u8 pulse_length_ext;
u8 pulse_bw_info;
} __packed;

/* The HT20 spectral data has 4 bytes of additional information at it's end.
*
* [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]}
* [7:0]: all bins max_magnitude[9:2]
* [7:0]: all bins {max_index[5:0], max_magnitude[11:10]}
* [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
*/
struct ath_ht20_mag_info {
u8 all_bins[3];
u8 max_exp;
} __packed;

#define SPECTRAL_HT20_NUM_BINS 56

/* WARNING: don't actually use this struct! MAC may vary the amount of
* data by -1/+2. This struct is for reference only.
*/
struct ath_ht20_fft_packet {
u8 data[SPECTRAL_HT20_NUM_BINS];
struct ath_ht20_mag_info mag_info;
struct ath_radar_info radar_info;
} __packed;

#define SPECTRAL_HT20_TOTAL_DATA_LEN (sizeof(struct ath_ht20_fft_packet))

/* Dynamic 20/40 mode:
*
* [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]}
* [7:0]: lower bins max_magnitude[9:2]
* [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]}
* [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]}
* [7:0]: upper bins max_magnitude[9:2]
* [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]}
* [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
*/
struct ath_ht20_40_mag_info {
u8 lower_bins[3];
u8 upper_bins[3];
u8 max_exp;
} __packed;

#define SPECTRAL_HT20_40_NUM_BINS 128

/* WARNING: don't actually use this struct! MAC may vary the amount of
* data. This struct is for reference only.
*/
struct ath_ht20_40_fft_packet {
u8 data[SPECTRAL_HT20_40_NUM_BINS];
struct ath_ht20_40_mag_info mag_info;
struct ath_radar_info radar_info;
} __packed;


#define SPECTRAL_HT20_40_TOTAL_DATA_LEN (sizeof(struct ath_ht20_40_fft_packet))

/* grabs the max magnitude from the all/upper/lower bins */
static inline u16 spectral_max_magnitude(u8 *bins)
{
return (bins[0] & 0xc0) >> 6 |
(bins[1] & 0xff) << 2 |
(bins[2] & 0x03) << 10;
}

/* return the max magnitude from the all/upper/lower bins */
static inline u8 spectral_max_index(u8 *bins)
{
s8 m = (bins[2] & 0xfc) >> 2;

/* TODO: this still doesn't always report the right values ... */
if (m > 32)
m |= 0xe0;
else
m &= ~0xe0;

return m + 29;
}

/* return the bitmap weight from the all/upper/lower bins */
static inline u8 spectral_bitmap_weight(u8 *bins)
{
return bins[0] & 0x3f;
}

/* FFT sample format given to userspace via debugfs.
*
* Please keep the type/length at the front position and change
* other fields after adding another sample type
*
* TODO: this might need rework when switching to nl80211-based
* interface.
*/
enum ath_fft_sample_type {
ATH_FFT_SAMPLE_HT20 = 0,
};

struct fft_sample_tlv {
u8 type; /* see ath_fft_sample */
u16 length;
/* type dependent data follows */
} __packed;

struct fft_sample_ht20 {
struct fft_sample_tlv tlv;

u8 __alignment;

u16 freq;
s8 rssi;
s8 noise;

u16 max_magnitude;
u8 max_index;
u8 bitmap_weight;

u64 tsf;

u16 data[SPECTRAL_HT20_NUM_BINS];
} __packed;

void ath9k_tasklet(unsigned long data);
int ath_cabq_update(struct ath_softc *);

Expand All @@ -768,6 +916,10 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
void ath9k_reload_chainmask_settings(struct ath_softc *sc);

bool ath9k_uses_beacons(int type);
void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
enum spectral_mode spectral_mode);


#ifdef CONFIG_ATH9K_PCI
int ath_pci_init(void);
Expand Down
Loading

0 comments on commit e93d083

Please sign in to comment.