diff options
author | David S. Miller <davem@davemloft.net> | 2013-11-05 02:34:57 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-11-05 02:34:57 -0500 |
commit | cfce0a2b61d0658d40bc2af2dca28a817804e17a (patch) | |
tree | 9dae9fbd6d072fc2ee0995ffe60516dcbf29fb77 /drivers/net/wireless/ath/ath9k | |
parent | ba275241030cfe87b87d6592345c7e7ebd9b6fba (diff) | |
parent | 87bc0728d462ae37841a550542829aa65a97e7c2 (diff) |
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next
John W. Linville says:
====================
Please accept the following pull request intended for the 3.13 tree...
I had intended to pass most of these to you as much as two weeks ago.
Unfortunately, I failed to account for the effects of bad Internet
connections and my own fatique/laziness while traveling. On the bright
side, at least these have been baking in linux-next for some time!
For the mac80211 bits, Johannes says:
"This time I have two fixes for P2P (which requires not using CCK rates)
and a workaround for APs with broken WMM information."
For the iwlwifi bits, Johannes says:
"I have a few fixes for warnings/issues: one from Alex, fixing scan
timings, one from Emmanuel fixing a WARN_ON in the DVM driver, one from
Stanislaw removing a trigger-happy WARN_ON in the MVM driver and a
change from myself to try to recover when the device isn't processing
commands quickly."
And:
"For this round, I have a lot of changes:
* power management improvements
* BT coexistence improvements/updates
* new device support
* VHT support
* IBSS support (though due to a small bug it requires new firmware)
* various other fixes/improvements."
For the Bluetooth bits, Gustavo says:
"More patches for 3.12, busy times for Bluetooth. More than a 100 commits since
the last pull. The bulk of work comes from Johan and Marcel, they are doing
fixes and improvements all over the Bluetooth subsystem, as the diffstat can
show."
For the ath10k and ath6kl bits, Kalle says:
"Bartosz added support to ath10k for our 10.x AP firmware branch, which
gives us AP specific features and fixes. We still support the main
firmware branch as well just like before, ath10k detects runtime what
firmware is used. Unfortunately the firmware interface in 10.x branch is
somewhat different so there was quite a lot of changes in ath10k for
this.
Michal and Sujith did some performance improvements in ath10k. Vladimir
fixed a compiler warning and Fengguang removed an extra semicolon."
For the NFC bits, Samuel says:
"It's a fairly big one, with the following highlights:
- NFC digital layer implementation: Most NFC chipsets implement the NFC
digital layer in firmware, but others have more basic functionalities
and expect the host to implement the digital layer. This layer sits
below the NFC core.
- Sony's port100 support: This is "soft" NFC USB dongle that expects the
digital layer to be implemented on the host. This is the first user of
our NFC digital stack implementation.
- Secure element API: We now provide a netlink API for enabling,
disabling and discovering NFC attached (embedded or UICC ones) secure
elements. With some userspace help, this allows us to support NFC
payments.
Only the pn544 driver currently supports that API.
- NCI SPI fixes and improvements: In order to support NCI devices over
SPI, we fixed and improved our NCI/SPI implementation. The currently
most deployed NFC NCI chipset, Broadcom's bcm2079x, supports that mode
and we're planning to use our NCI/SPI framework to implement a
driver for it.
- pn533 fragmentation support in target mode: This was the only missing
feature from our pn533 impementation. We now support fragmentation in
both Tx and Rx modes, in target mode."
On top of all that, brcmfmac and rt2x00 both get the usual flurry
of updates. A few other drivers get hit here or there as well.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/wireless/ath/ath9k')
23 files changed, 682 insertions, 1005 deletions
diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 7944c25c9a4..32f139e2e89 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -84,6 +84,26 @@ config ATH9K_DFS_CERTIFIED developed. At this point enabling this option won't do anything except increase code size. +config ATH9K_TX99 + bool "Atheros ath9k TX99 testing support" + depends on CFG80211_CERTIFICATION_ONUS + default n + ---help--- + Say N. This should only be enabled on systems undergoing + certification testing and evaluation in a controlled environment. + Enabling this will only enable TX99 support, all other modes of + operation will be disabled. + + TX99 support enables Specific Absorption Rate (SAR) testing. + SAR is the unit of measurement for the amount of radio frequency(RF) + absorbed by the body when using a wireless device. The RF exposure + limits used are expressed in the terms of SAR, which is a measure + of the electric and magnetic field strength and power density for + transmitters operating at frequencies from 300 kHz to 100 GHz. + Regulatory bodies around the world require that wireless device + be evaluated to meet the RF exposure limits set forth in the + governmental SAR regulations. + config ATH9K_LEGACY_RATE_CONTROL bool "Atheros ath9k rate control" depends on ATH9K diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 75ee9e7704c..6205ef5a932 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -14,9 +14,7 @@ ath9k-$(CONFIG_ATH9K_AHB) += ahb.o ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \ - dfs.o \ - dfs_pattern_detector.o \ - dfs_pri_detector.o + dfs.o ath9k-$(CONFIG_PM_SLEEP) += wow.o obj-$(CONFIG_ATH9K) += ath9k.o diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index 17970d49d85..f087117b2e6 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -680,6 +680,26 @@ static void ar9002_hw_spectral_scan_wait(struct ath_hw *ah) } } +static void ar9002_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + REG_SET_BIT(ah, 0x9864, 0x7f000); + REG_SET_BIT(ah, 0x9924, 0x7f00fe); + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + REG_WRITE(ah, AR_CR, AR_CR_RXD); + REG_WRITE(ah, AR_DLCL_IFS(qnum), 0); + REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); + REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20); + REG_WRITE(ah, AR_D_FPCTL, 0x10|qnum); + REG_WRITE(ah, AR_TIME_OUT, 0x00000400); + REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff); + REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ); +} + +static void ar9002_hw_tx99_stop(struct ath_hw *ah) +{ + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); +} + void ar9002_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); @@ -701,6 +721,8 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah) #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT ops->set_bt_ant_diversity = ar9002_hw_set_bt_ant_diversity; #endif + ops->tx99_start = ar9002_hw_tx99_start; + ops->tx99_stop = ar9002_hw_tx99_stop; ar9002_hw_set_nf_limits(ah); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index f3adafd3370..11f53589a3f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -1617,6 +1617,98 @@ static void ar9003_hw_spectral_scan_wait(struct ath_hw *ah) } } +static void ar9003_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + REG_SET_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR); + REG_SET_BIT(ah, 0x9864, 0x7f000); + REG_SET_BIT(ah, 0x9924, 0x7f00fe); + REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); + REG_WRITE(ah, AR_CR, AR_CR_RXD); + REG_WRITE(ah, AR_DLCL_IFS(qnum), 0); + REG_WRITE(ah, AR_D_GBL_IFS_SIFS, 20); /* 50 OK */ + REG_WRITE(ah, AR_D_GBL_IFS_EIFS, 20); + REG_WRITE(ah, AR_TIME_OUT, 0x00000400); + REG_WRITE(ah, AR_DRETRY_LIMIT(qnum), 0xffffffff); + REG_SET_BIT(ah, AR_QMISC(qnum), AR_Q_MISC_DCU_EARLY_TERM_REQ); +} + +static void ar9003_hw_tx99_stop(struct ath_hw *ah) +{ + REG_CLR_BIT(ah, AR_PHY_TEST, PHY_AGC_CLR); + REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_RX_DIS); +} + +static void ar9003_hw_tx99_set_txpower(struct ath_hw *ah, u8 txpower) +{ + static s16 p_pwr_array[ar9300RateSize] = { 0 }; + unsigned int i; + + if (txpower <= MAX_RATE_POWER) { + for (i = 0; i < ar9300RateSize; i++) + p_pwr_array[i] = txpower; + } else { + for (i = 0; i < ar9300RateSize; i++) + p_pwr_array[i] = MAX_RATE_POWER; + } + + REG_WRITE(ah, 0xa458, 0); + + REG_WRITE(ah, 0xa3c0, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0)); + REG_WRITE(ah, 0xa3c4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_54], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_48], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_36], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_6_24], 0)); + REG_WRITE(ah, 0xa3c8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0)); + REG_WRITE(ah, 0xa3cc, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11S], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_11L], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_5S], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_LEGACY_1L_5L], 0)); + REG_WRITE(ah, 0xa3d0, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_5], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_4], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_1_3_9_11_17_19], 8)| + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_0_8_16], 0)); + REG_WRITE(ah, 0xa3d4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_13], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_12], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_7], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_6], 0)); + REG_WRITE(ah, 0xa3e4, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_21], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_20], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_15], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_14], 0)); + REG_WRITE(ah, 0xa3e8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_23], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_22], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_23], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT20_22], 0)); + REG_WRITE(ah, 0xa3d8, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_5], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_4], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_1_3_9_11_17_19], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_0_8_16], 0)); + REG_WRITE(ah, 0xa3dc, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_13], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_12], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_7], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_6], 0)); + REG_WRITE(ah, 0xa3ec, + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_21], 24) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_20], 16) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_15], 8) | + ATH9K_POW_SM(p_pwr_array[ALL_TARGET_HT40_14], 0)); +} + void ar9003_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); @@ -1656,6 +1748,9 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT ops->set_bt_ant_diversity = ar9003_hw_set_bt_ant_diversity; #endif + ops->tx99_start = ar9003_hw_tx99_start; + ops->tx99_stop = ar9003_hw_tx99_stop; + ops->tx99_set_txpower = ar9003_hw_tx99_set_txpower; ar9003_hw_set_nf_limits(ah); ar9003_hw_set_radar_conf(ah); diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 14ff7e9dde4..e7a38d844a6 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -778,6 +778,11 @@ struct ath_softc { enum spectral_mode spectral_mode; struct ath_spec_scan spec_config; + struct ieee80211_vif *tx99_vif; + struct sk_buff *tx99_skb; + bool tx99_state; + s16 tx99_power; + #ifdef CONFIG_PM_SLEEP atomic_t wow_got_bmiss_intr; atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */ @@ -886,6 +891,7 @@ static inline u8 spectral_bitmap_weight(u8 *bins) */ enum ath_fft_sample_type { ATH_FFT_SAMPLE_HT20 = 1, + ATH_FFT_SAMPLE_HT20_40, }; struct fft_sample_tlv { @@ -912,6 +918,39 @@ struct fft_sample_ht20 { u8 data[SPECTRAL_HT20_NUM_BINS]; } __packed; +struct fft_sample_ht20_40 { + struct fft_sample_tlv tlv; + + u8 channel_type; + __be16 freq; + + s8 lower_rssi; + s8 upper_rssi; + + __be64 tsf; + + s8 lower_noise; + s8 upper_noise; + + __be16 lower_max_magnitude; + __be16 upper_max_magnitude; + + u8 lower_max_index; + u8 upper_max_index; + + u8 lower_bitmap_weight; + u8 upper_bitmap_weight; + + u8 max_exp; + + u8 data[SPECTRAL_HT20_40_NUM_BINS]; +} __packed; + +int ath9k_tx99_init(struct ath_softc *sc); +void ath9k_tx99_deinit(struct ath_softc *sc); +int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, + struct ath_tx_control *txctl); + void ath9k_tasklet(unsigned long data); int ath_cabq_update(struct ath_softc *); diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index d8db74b0ef6..278365b8a89 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -63,13 +63,13 @@ static s16 ath9k_hw_get_default_nf(struct ath_hw *ah, return ath9k_hw_get_nf_limits(ah, chan)->nominal; } -s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan) +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, + s16 nf) { s8 noise = ATH_DEFAULT_NOISE_FLOOR; - if (chan && chan->noisefloor) { - s8 delta = chan->noisefloor - - ATH9K_NF_CAL_NOISE_THRESH - + if (nf) { + s8 delta = nf - ATH9K_NF_CAL_NOISE_THRESH - ath9k_hw_get_default_nf(ah, chan); if (delta > 0) noise += delta; @@ -392,7 +392,7 @@ bool ath9k_hw_getnf(struct ath_hw *ah, struct ath9k_channel *chan) clear_bit(NFCAL_PENDING, &caldata->cal_flags); ath9k_hw_update_nfcal_hist_buffer(ah, caldata, nfarray); chan->noisefloor = h[0].privNF; - ah->noise = ath9k_hw_getchan_noise(ah, chan); + ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); return true; } EXPORT_SYMBOL(ath9k_hw_getnf); diff --git a/drivers/net/wireless/ath/ath9k/calib.h b/drivers/net/wireless/ath/ath9k/calib.h index 3d70b8c2bcd..b8ed95e9a33 100644 --- a/drivers/net/wireless/ath/ath9k/calib.h +++ b/drivers/net/wireless/ath/ath9k/calib.h @@ -116,7 +116,8 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah, void ath9k_hw_bstuck_nfcal(struct ath_hw *ah); void ath9k_hw_reset_calibration(struct ath_hw *ah, struct ath9k_cal_list *currCal); -s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan); +s16 ath9k_hw_getchan_noise(struct ath_hw *ah, struct ath9k_channel *chan, + s16 nf); #endif /* CALIB_H */ diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 1be2c787aac..83a2c59f680 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -1050,6 +1050,9 @@ static ssize_t write_file_spec_scan_ctl(struct file *file, char buf[32]; ssize_t len; + if (config_enabled(CONFIG_ATH9K_TX99)) + return -EOPNOTSUPP; + len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; @@ -1775,6 +1778,111 @@ void ath9k_deinit_debug(struct ath_softc *sc) } } +static ssize_t read_file_tx99(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[3]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->tx99_state); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_tx99(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + char buf[32]; + bool start; + ssize_t len; + int r; + + if (sc->nvifs > 1) + return -EOPNOTSUPP; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + if (strtobool(buf, &start)) + return -EINVAL; + + if (start == sc->tx99_state) { + if (!start) + return count; + ath_dbg(common, XMIT, "Resetting TX99\n"); + ath9k_tx99_deinit(sc); + } + + if (!start) { + ath9k_tx99_deinit(sc); + return count; + } + + r = ath9k_tx99_init(sc); + if (r) + return r; + + return count; +} + +static const struct file_operations fops_tx99 = { + .read = read_file_tx99, + .write = write_file_tx99, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t read_file_tx99_power(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d (%d dBm)\n", + sc->tx99_power, + sc->tx99_power / 2); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_tx99_power(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + int r; + u8 tx_power; + + r = kstrtou8_from_user(user_buf, count, 0, &tx_power); + if (r) + return r; + + if (tx_power > MAX_RATE_POWER) + return -EINVAL; + + sc->tx99_power = tx_power; + + ath9k_ps_wakeup(sc); + ath9k_hw_tx99_set_txpower(sc->sc_ah, sc->tx99_power); + ath9k_ps_restore(sc); + + return count; +} + +static const struct file_operations fops_tx99_power = { + .read = read_file_tx99_power, + .write = write_file_tx99_power, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath9k_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -1866,5 +1974,15 @@ int ath9k_init_debug(struct ath_hw *ah) debugfs_create_file("btcoex", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_btcoex); #endif + if (config_enabled(CONFIG_ATH9K_TX99) && + AR_SREV_9300_20_OR_LATER(ah)) { + debugfs_create_file("tx99", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_tx99); + debugfs_create_file("tx99_power", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_tx99_power); + } + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/dfs.h b/drivers/net/wireless/ath/ath9k/dfs.h index 3c839f06a06..c6fa3d5b5d7 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.h +++ b/drivers/net/wireless/ath/ath9k/dfs.h @@ -17,7 +17,7 @@ #ifndef ATH9K_DFS_H #define ATH9K_DFS_H -#include "dfs_pattern_detector.h" +#include "../dfs_pattern_detector.h" #if defined(CONFIG_ATH9K_DFS_CERTIFIED) /** diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c index 821599135d8..90b8342d1ed 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.c +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c @@ -20,16 +20,16 @@ #include "ath9k.h" #include "dfs_debug.h" +#include "../dfs_pattern_detector.h" - -struct ath_dfs_pool_stats global_dfs_pool_stats = { 0 }; +static struct ath_dfs_pool_stats dfs_pool_stats = { 0 }; #define ATH9K_DFS_STAT(s, p) \ len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \ sc->debug.stats.dfs_stats.p); #define ATH9K_DFS_POOL_STAT(s, p) \ len += scnprintf(buf + len, size - len, "%28s : %10u\n", s, \ - global_dfs_pool_stats.p); + dfs_pool_stats.p); static ssize_t read_file_dfs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -44,6 +44,9 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; + if (sc->dfs_detector) + dfs_pool_stats = sc->dfs_detector->get_stats(sc->dfs_detector); + len += scnprintf(buf + len, size - len, "DFS support for " "macVersion = 0x%x, macRev = 0x%x: %s\n", hw_ver->macVersion, hw_ver->macRev, diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.h b/drivers/net/wireless/ath/ath9k/dfs_debug.h index e36810a4b58..0a7ddf4c88c 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.h +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.h @@ -51,25 +51,11 @@ struct ath_dfs_stats { u32 radar_detected; }; -/** - * struct ath_dfs_pool_stats - DFS Statistics for global pools - */ -struct ath_dfs_pool_stats { - u32 pool_reference; - u32 pulse_allocated; - u32 pulse_alloc_error; - u32 pulse_used; - u32 pseq_allocated; - u32 pseq_alloc_error; - u32 pseq_used; -}; #if defined(CONFIG_ATH9K_DFS_DEBUGFS) #define DFS_STAT_INC(sc, c) (sc->debug.stats.dfs_stats.c++) void ath9k_dfs_init_debug(struct ath_softc *sc); -#define DFS_POOL_STAT_INC(c) (global_dfs_pool_stats.c++) -#define DFS_POOL_STAT_DEC(c) (global_dfs_pool_stats.c--) extern struct ath_dfs_pool_stats global_dfs_pool_stats; #else @@ -77,8 +63,6 @@ extern struct ath_dfs_pool_stats global_dfs_pool_stats; #define DFS_STAT_INC(sc, c) do { } while (0) static inline void ath9k_dfs_init_debug(struct ath_softc *sc) { } -#define DFS_POOL_STAT_INC(c) do { } while (0) -#define DFS_POOL_STAT_DEC(c) do { } while (0) #endif /* CONFIG_ATH9K_DFS_DEBUGFS */ #endif /* ATH9K_DFS_DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c deleted file mode 100644 index 491305c81fc..00000000000 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ /dev/null @@ -1,311 +0,0 @@ -/* - * Copyright (c) 2012 Neratec Solutions AG - * - * 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. - */ - -#include <linux/slab.h> -#include <linux/export.h> - -#include "dfs_pattern_detector.h" -#include "dfs_pri_detector.h" -#include "ath9k.h" - -/* - * tolerated deviation of radar time stamp in usecs on both sides - * TODO: this might need to be HW-dependent - */ -#define PRI_TOLERANCE 16 - -/** - * struct radar_types - contains array of patterns defined for one DFS domain - * @domain: DFS regulatory domain - * @num_radar_types: number of radar types to follow - * @radar_types: radar types array - */ -struct radar_types { - enum nl80211_dfs_regions region; - u32 num_radar_types; - const struct radar_detector_specs *radar_types; -}; - -/* percentage on ppb threshold to trigger detection */ -#define MIN_PPB_THRESH 50 -#define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100) -#define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF) -/* percentage of pulse width tolerance */ -#define WIDTH_TOLERANCE 5 -#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100) -#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100) - -#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \ -{ \ - ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \ - (PRF2PRI(PMAX) - PRI_TOLERANCE), \ - (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF, \ - PPB_THRESH(PPB), PRI_TOLERANCE, \ -} - -/* radar types as defined by ETSI EN-301-893 v1.5.1 */ -static const struct radar_detector_specs etsi_radar_ref_types_v15[] = { - ETSI_PATTERN(0, 0, 1, 700, 700, 1, 18), - ETSI_PATTERN(1, 0, 5, 200, 1000, 1, 10), - ETSI_PATTERN(2, 0, 15, 200, 1600, 1, 15), - ETSI_PATTERN(3, 0, 15, 2300, 4000, 1, 25), - ETSI_PATTERN(4, 20, 30, 2000, 4000, 1, 20), - ETSI_PATTERN(5, 0, 2, 300, 400, 3, 10), - ETSI_PATTERN(6, 0, 2, 400, 1200, 3, 15), -}; - -static const struct radar_types etsi_radar_types_v15 = { - .region = NL80211_DFS_ETSI, - .num_radar_types = ARRAY_SIZE(etsi_radar_ref_types_v15), - .radar_types = etsi_radar_ref_types_v15, -}; - -/* for now, we support ETSI radar types, FCC and JP are TODO */ -static const struct radar_types *dfs_domains[] = { - &etsi_radar_types_v15, -}; - -/** - * get_dfs_domain_radar_types() - get radar types for a given DFS domain - * @param domain DFS domain - * @return radar_types ptr on success, NULL if DFS domain is not supported - */ -static const struct radar_types * -get_dfs_domain_radar_types(enum nl80211_dfs_regions region) -{ - u32 i; - for (i = 0; i < ARRAY_SIZE(dfs_domains); i++) { - if (dfs_domains[i]->region == region) - return dfs_domains[i]; - } - return NULL; -} - -/** - * struct channel_detector - detector elements for a DFS channel - * @head: list_head - * @freq: frequency for this channel detector in MHz - * @detectors: array of dynamically created detector elements for this freq - * - * Channel detectors are required to provide multi-channel DFS detection, e.g. - * to support off-channel scanning. A pattern detector has a list of channels - * radar pulses have been reported for in the past. - */ -struct channel_detector { - struct list_head head; - u16 freq; - struct pri_detector **detectors; -}; - -/* channel_detector_reset() - reset detector lines for a given channel */ -static void channel_detector_reset(struct dfs_pattern_detector *dpd, - struct channel_detector *cd) -{ - u32 i; - if (cd == NULL) - return; - for (i = 0; i < dpd->num_radar_types; i++) - cd->detectors[i]->reset(cd->detectors[i], dpd->last_pulse_ts); -} - -/* channel_detector_exit() - destructor */ -static void channel_detector_exit(struct dfs_pattern_detector *dpd, - struct channel_detector *cd) -{ - u32 i; - if (cd == NULL) - return; - list_del(&cd->head); - for (i = 0; i < dpd->num_radar_types; i++) { - struct pri_detector *de = cd->detectors[i]; - if (de != NULL) - de->exit(de); - } - kfree(cd->detectors); - kfree(cd); -} - -static struct channel_detector * -channel_detector_create(struct dfs_pattern_detector *dpd, u16 freq) -{ - u32 sz, i; - struct channel_detector *cd; - struct ath_common *common = ath9k_hw_common(dpd->ah); - - cd = kmalloc(sizeof(*cd), GFP_ATOMIC); - if (cd == NULL) - goto fail; - - INIT_LIST_HEAD(&cd->head); - cd->freq = freq; - sz = sizeof(cd->detectors) * dpd->num_radar_types; - cd->detectors = kzalloc(sz, GFP_ATOMIC); - if (cd->detectors == NULL) - goto fail; - - for (i = 0; i < dpd->num_radar_types; i++) { - const struct radar_detector_specs *rs = &dpd->radar_spec[i]; - struct pri_detector *de = pri_detector_init(rs); - if (de == NULL) - goto fail; - cd->detectors[i] = de; - } - list_add(&cd->head, &dpd->channel_detectors); - return cd; - -fail: - ath_dbg(common, DFS, - "failed to allocate channel_detector for freq=%d\n", freq); - channel_detector_exit(dpd, cd); - return NULL; -} - -/** - * channel_detector_get() - get channel detector for given frequency - * @param dpd instance pointer - * @param freq frequency in MHz - * @return pointer to channel detector on success, NULL otherwise - * - * Return existing channel detector for the given frequency or return a - * newly create one. - */ -static struct channel_detector * -channel_detector_get(struct dfs_pattern_detector *dpd, u16 freq) -{ - struct channel_detector *cd; - list_for_each_entry(cd, &dpd->channel_detectors, head) { - if (cd->freq == freq) - return cd; - } - return channel_detector_create(dpd, freq); -} - -/* - * DFS Pattern Detector - */ - -/* dpd_reset(): reset all channel detectors */ -static void dpd_reset(struct dfs_pattern_detector *dpd) -{ - struct channel_detector *cd; - if (!list_empty(&dpd->channel_detectors)) - list_for_each_entry(cd, &dpd->channel_detectors, head) - channel_detector_reset(dpd, cd); - -} -static void dpd_exit(struct dfs_pattern_detector *dpd) -{ - struct channel_detector *cd, *cd0; - if (!list_empty(&dpd->channel_detectors)) - list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) - channel_detector_exit(dpd, cd); - kfree(dpd); -} - -static bool -dpd_add_pulse(struct dfs_pattern_detector *dpd, struct pulse_event *event) -{ - u32 i; - struct channel_detector *cd; - - /* - * pulses received for a non-supported or un-initialized - * domain are treated as detected radars for fail-safety - */ - if (dpd->region == NL80211_DFS_UNSET) - return true; - - cd = channel_detector_get(dpd, event->freq); - if (cd == NULL) - return false; - - dpd->last_pulse_ts = event->ts; - /* reset detector on time stamp wraparound, caused by TSF reset */ - if (event->ts < dpd->last_pulse_ts) - dpd_reset(dpd); - - /* do type individual pattern matching */ - for (i = 0; i < dpd->num_radar_types; i++) { - struct pri_detector *pd = cd->detectors[i]; - struct pri_sequence *ps = pd->add_pulse(pd, event); - if (ps != NULL) { - ath_dbg(ath9k_hw_common(dpd->ah), DFS, - "DFS: radar found on freq=%d: id=%d, pri=%d, " - "count=%d, count_false=%d\n", - event->freq, pd->rs->type_id, - ps->pri, ps->count, ps->count_falses); - channel_detector_reset(dpd, cd); - return true; - } - } - return false; -} - -static bool dpd_set_domain(struct dfs_pattern_detector *dpd, - enum nl80211_dfs_regions region) -{ - const struct radar_types *rt; - struct channel_detector *cd, *cd0; - - if (dpd->region == region) - return true; - - dpd->region = NL80211_DFS_UNSET; - - rt = get_dfs_domain_radar_types(region); - if (rt == NULL) - return false; - - /* delete all channel detectors for previous DFS domain */ - if (!list_empty(&dpd->channel_detectors)) - list_for_each_entry_safe(cd, cd0, &dpd->channel_detectors, head) - channel_detector_exit(dpd, cd); - dpd->radar_spec = rt->radar_types; - dpd->num_radar_types = rt->num_radar_types; - - dpd->region = region; - return true; -} - -static struct dfs_pattern_detector default_dpd = { - .exit = dpd_exit, - .set_dfs_domain = dpd_set_domain, - .add_pulse = dpd_add_pulse, - .region = NL80211_DFS_UNSET, -}; - -struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) -{ - struct dfs_pattern_detector *dpd; - struct ath_common *common = ath9k_hw_common(ah); - - dpd = kmalloc(sizeof(*dpd), GFP_KERNEL); - if (dpd == NULL) - return NULL; - - *dpd = default_dpd; - INIT_LIST_HEAD(&dpd->channel_detectors); - - dpd->ah = ah; - if (dpd->set_dfs_domain(dpd, region)) - return dpd; - - ath_dbg(common, DFS,"Could not set DFS domain to %d", region); - kfree(dpd); - return NULL; -} -EXPORT_SYMBOL(dfs_pattern_detector_init); diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h deleted file mode 100644 index 90a5abcc426..00000000000 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2012 Neratec Solutions AG - * - * 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. - */ - -#ifndef DFS_PATTERN_DETECTOR_H -#define DFS_PATTERN_DETECTOR_H - -#include <linux/types.h> -#include <linux/list.h> -#include <linux/nl80211.h> - -/** - * struct pulse_event - describing pulses reported by PHY - * @ts: pulse time stamp in us - * @freq: channel frequency in MHz - * @width: pulse duration in us - * @rssi: rssi of radar event - */ -struct pulse_event { - u64 ts; - u16 freq; - u8 width; - u8 rssi; -}; - -/** - * struct radar_detector_specs - detector specs for a radar pattern type - * @type_id: pattern type, as defined by regulatory - * @width_min: minimum radar pulse width in [us] - * @width_max: maximum radar pulse width in [us] - * @pri_min: minimum pulse repetition interval in [us] (including tolerance) - * @pri_max: minimum pri in [us] (including tolerance) - * @num_pri: maximum number of different pri for this type - * @ppb: pulses per bursts for this type - * @ppb_thresh: number of pulses required to trigger detection - * @max_pri_tolerance: pulse time stamp tolerance on both sides [us] - */ -struct radar_detector_specs { - u8 type_id; - u8 width_min; - u8 width_max; - u16 pri_min; - u16 pri_max; - u8 num_pri; - u8 ppb; - u8 ppb_thresh; - u8 max_pri_tolerance; -}; - -/** - * struct dfs_pattern_detector - DFS pattern detector - * @exit(): destructor - * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes - * @add_pulse(): add radar pulse to detector, returns true on detection - * @region: active DFS region, NL80211_DFS_UNSET until set - * @num_radar_types: number of different radar types - * @last_pulse_ts: time stamp of last valid pulse in usecs - * @radar_detector_specs: array of radar detection specs - * @channel_detectors: list connecting channel_detector elements - */ -struct dfs_pattern_detector { - void (*exit)(struct dfs_pattern_detector *dpd); - bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd, - enum nl80211_dfs_regions region); - bool (*add_pulse)(struct dfs_pattern_detector *dpd, - struct pulse_event *pe); - - enum nl80211_dfs_regions region; - u8 num_radar_types; - u64 last_pulse_ts; - /* needed for ath_dbg() */ - struct ath_hw *ah; - - const struct radar_detector_specs *radar_spec; - struct list_head channel_detectors; -}; - -/** - * dfs_pattern_detector_init() - constructor for pattern detector class - * @param region: DFS domain to be used, can be NL80211_DFS_UNSET at creation - * @return instance pointer on success, NULL otherwise - */ -#if defined(CONFIG_ATH9K_DFS_CERTIFIED) -extern struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region); -#else -static inline struct dfs_pattern_detector * -dfs_pattern_detector_init(struct ath_hw *ah, enum nl80211_dfs_regions region) -{ - return NULL; -} -#endif /* CONFIG_ATH9K_DFS_CERTIFIED */ - -#endif /* DFS_PATTERN_DETECTOR_H */ diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c deleted file mode 100644 index c718fc379a1..00000000000 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c +++ /dev/null @@ -1,425 +0,0 @@ -/* - * Copyright (c) 2012 Neratec Solutions AG - * - * 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. - */ - -#include <linux/slab.h> -#include <linux/spinlock.h> - -#include "ath9k.h" -#include "dfs_pattern_detector.h" -#include "dfs_pri_detector.h" -#include "dfs_debug.h" - -/** - * struct pulse_elem - elements in pulse queue - * @ts: time stamp in usecs - */ -struct pulse_elem { - struct list_head head; - u64 ts; -}; - -/** - * pde_get_multiple() - get number of multiples considering a given tolerance - * @return factor if abs(val - factor*fraction) <= tolerance, 0 otherwise - */ -static u32 pde_get_multiple(u32 val, u32 fraction, u32 tolerance) -{ - u32 remainder; - u32 factor; - u32 delta; - - if (fraction == 0) - return 0; - - delta = (val < fraction) ? (fraction - val) : (val - fraction); - - if (delta <= tolerance) - /* val and fraction are within tolerance */ - return 1; - - factor = val / fraction; - remainder = val % fraction; - if (remainder > tolerance) { - /* no exact match */ - if ((fraction - remainder) <= tolerance) - /* remainder is within tolerance */ - factor++; - else - factor = 0; - } - return factor; -} - -/** - * DOC: Singleton Pulse and Sequence Pools - * - * Instances of pri_sequence and pulse_elem are kept in singleton pools to - * reduce the number of dynamic allocations. They are shared between all - * instances and grow up to the peak number of simultaneously used objects. - * - * Memory is freed after all references to the pools are released. - */ -static u32 singleton_pool_references; -static LIST_HEAD(pulse_pool); -static LIST_HEAD(pseq_pool); -static DEFINE_SPINLOCK(pool_lock); - -static void pool_register_ref(void) -{ - spin_lock_bh(&pool_lock); - singleton_pool_references++; - DFS_POOL_STAT_INC(pool_reference); - spin_unlock_bh(&pool_lock); -} - -static void pool_deregister_ref(void) -{ - spin_lock_bh(&pool_lock); - singleton_pool_references--; - DFS_POOL_STAT_DEC(pool_reference); - if (singleton_pool_references == 0) { - /* free singleton pools with no references left */ - struct pri_sequence *ps, *ps0; - struct pulse_elem *p, *p0; - - list_for_each_entry_safe(p, p0, &pulse_pool, head) { - list_del(&p->head); - DFS_POOL_STAT_DEC(pulse_allocated); - kfree(p); - } - list_for_each_entry_safe(ps, ps0, &pseq_pool, head) { - list_del(&ps->head); - DFS_POOL_STAT_DEC(pseq_allocated); - kfree(ps); - } - } - spin_unlock_bh(&pool_lock); -} - -static void pool_put_pulse_elem(struct pulse_elem *pe) -{ - spin_lock_bh(&pool_lock); - list_add(&pe->head, &pulse_pool); - DFS_POOL_STAT_DEC(pulse_used); - spin_unlock_bh(&pool_lock); -} - -static void pool_put_pseq_elem(struct pri_sequence *pse) -{ - spin_lock_bh(&pool_lock); - list_add(&pse->head, &pseq_pool); - DFS_POOL_STAT_DEC(pseq_used); - spin_unlock_bh(&pool_lock); -} - -static struct pri_sequence *pool_get_pseq_elem(void) -{ - struct pri_sequence *pse = NULL; - spin_lock_bh(&pool_lock); - if (!list_empty(&pseq_pool)) { - pse = list_first_entry(&pseq_pool, struct pri_sequence, head); - list_del(&pse->head); - DFS_POOL_STAT_INC(pseq_used); - } - spin_unlock_bh(&pool_lock); - return pse; -} - -static struct pulse_elem *pool_get_pulse_elem(void) -{ - struct pulse_elem *pe = NULL; - spin_lock_bh(&pool_lock); - if (!list_empty(&pulse_pool)) { - pe = list_first_entry(&pulse_pool, struct pulse_elem, head); - list_del(&pe->head); - DFS_POOL_STAT_INC(pulse_used); - } - spin_unlock_bh(&pool_lock); - return pe; -} - -static struct pulse_elem *pulse_queue_get_tail(struct pri_detector *pde) -{ - struct list_head *l = &pde->pulses; - if (list_empty(l)) - return NULL; - return list_entry(l->prev, struct pulse_elem, head); -} - -static bool pulse_queue_dequeue(struct pri_detector *pde) -{ - struct pulse_elem *p = pulse_queue_get_tail(pde); - if (p != NULL) { - list_del_init(&p->head); - pde->count--; - /* give it back to pool */ - pool_put_pulse_elem(p); - } - return (pde->count > 0); -} - -/* remove pulses older than window */ -static void pulse_queue_check_window(struct pri_detector *pde) -{ - u64 min_valid_ts; - struct pulse_elem *p; - - /* there is no delta time with less than 2 pulses */ - if (pde->count < 2) - return; - - if (pde->last_ts <= pde->window_size) - return; - - min_valid_ts = pde->last_ts - pde->window_size; - while ((p = pulse_queue_get_tail(pde)) != NULL) { - if (p->ts >= min_valid_ts) - return; - pulse_queue_dequeue(pde); - } -} - -static bool pulse_queue_enqueue(struct pri_detector *pde, u64 ts) -{ - struct pulse_elem *p = pool_get_pulse_elem(); - if (p == NULL) { - p = kmalloc(sizeof(*p), GFP_ATOMIC); - if (p == NULL) { - DFS_POOL_STAT_INC(pulse_alloc_error); - return false; - } - DFS_POOL_STAT_INC(pulse_allocated); - DFS_POOL_STAT_INC(pulse_used); - } - INIT_LIST_HEAD(&p->head); - p->ts = ts; - list_add(&p->head, &pde->pulses); - pde->count++; - pde->last_ts = ts; - pulse_queue_check_window(pde); - if (pde->count >= pde->max_count) - pulse_queue_dequeue(pde); - return true; -} - -static bool pseq_handler_create_sequences(struct pri_detector *pde, - u64 ts, u32 min_count) -{ - struct pulse_elem *p; - list_for_each_entry(p, &pde->pulses, head) { - struct pri_sequence ps, *new_ps; - struct pulse_elem *p2; - u32 tmp_false_count; - u64 min_valid_ts; - u32 delta_ts = ts - p->ts; - - if (delta_ts < pde->rs->pri_min) - /* ignore too small pri */ - continue; - - if (delta_ts > pde->rs->pri_max) - /* stop on too large pri (sorted list) */ - break; - - /* build a new sequence with new potential pri */ - ps.count = 2; - ps.count_falses = 0; - ps.first_ts = p->ts; - ps.last_ts = ts; - ps.pri = ts - p->ts; - ps.dur = ps.pri * (pde->rs->ppb - 1) - + 2 * pde->rs->max_pri_tolerance; - - p2 = p; - tmp_false_count = 0; - min_valid_ts = ts - ps.dur; - /* check which past pulses are candidates for new sequence */ - list_for_each_entry_continue(p2, &pde->pulses, head) { - u32 factor; - if (p2->ts < min_valid_ts) - /* stop on crossing window border */ - break; - /* check if pulse match (multi)PRI */ - factor = pde_get_multiple(ps.last_ts - p2->ts, ps.pri, - pde->rs->max_pri_tolerance); - if (factor > 0) { - ps.count++; - ps.first_ts = p2->ts; - /* - * on match, add the intermediate falses - * and reset counter - */ - ps.count_falses += tmp_false_count; - tmp_false_count = 0; - } else { - /* this is a potential false one */ - tmp_false_count++; - } - } - if (ps.count < min_count) - /* did not reach minimum count, drop sequence */ - continue; - - /* this is a valid one, add it */ - ps.deadline_ts = ps.first_ts + ps.dur; - new_ps = pool_get_pseq_elem(); - if (new_ps == NULL) { - new_ps = kmalloc(sizeof(*new_ps), GFP_ATOMIC); - if (new_ps == NULL) { - DFS_POOL_STAT_INC(pseq_alloc_error); - return false; - } - DFS_POOL_STAT_INC(pseq_allocated); - DFS_POOL_STAT_INC(pseq_used); - } - memcpy(new_ps, &ps, sizeof(ps)); - INIT_LIST_HEAD(&new_ps->head); - list_add(&new_ps->head, &pde->sequences); - } - return true; -} - -/* check new ts and add to all matching existing sequences */ -static u32 -pseq_handler_add_to_existing_seqs(struct pri_detector *pde, u64 ts) -{ - u32 max_count = 0; - struct pri_sequence *ps, *ps2; - list_for_each_entry_safe(ps, ps2, &pde->sequences, head) { - u32 delta_ts; - u32 factor; - - /* first ensure that sequence is within window */ - if (ts > ps->deadline_ts) { - list_del_init(&ps->head); - pool_put_pseq_elem(ps); - continue; - } - - delta_ts = ts - ps->last_ts; - factor = pde_get_multiple(delta_ts, ps->pri, - pde->rs->max_pri_tolerance); - if (factor > 0) { - ps->last_ts = ts; - ps->count++; - - if (max_count < ps->count) - max_count = ps->count; - } else { - ps->count_falses++; - } - } - return max_count; -} - -static struct pri_sequence * -pseq_handler_check_detection(struct pri_detector *pde) -{ - struct pri_sequence *ps; - - if (list_empty(&pde->sequences)) - return NULL; - - list_for_each_entry(ps, &pde->sequences, head) { - /* - * we assume to have enough matching confidence if we - * 1) have enough pulses - * 2) have more matching than false pulses - */ - if ((ps->count >= pde->rs->ppb_thresh) && - (ps->count * pde->rs->num_pri >= ps->count_falses)) - return ps; - } - return NULL; -} - - -/* free pulse queue and sequences list and give objects back to pools */ -static void pri_detector_reset(struct pri_detector *pde, u64 ts) -{ - struct pri_sequence *ps, *ps0; - struct pulse_elem *p, *p0; - list_for_each_entry_safe(ps, ps0, &pde->sequences, head) { - list_del_init(&ps->head); - pool_put_pseq_elem(ps); - } - list_for_each_entry_safe(p, p0, &pde->pulses, head) { - list_del_init(&p->head); - pool_put_pulse_elem(p); - } - pde->count = 0; - pde->last_ts = ts; -} - -static void pri_detector_exit(struct pri_detector *de) -{ - pri_detector_reset(de, 0); - pool_deregister_ref(); - kfree(de); -} - -static struct pri_sequence *pri_detector_add_pulse(struct pri_detector *de, - struct pulse_event *event) -{ - u32 max_updated_seq; - struct pri_sequence *ps; - u64 ts = event->ts; - const struct radar_detector_specs *rs = de->rs; - - /* ignore pulses not within width range */ - if ((rs->width_min > event->width) || (rs->width_max < event->width)) - return NULL; - - if ((ts - de->last_ts) < rs->max_pri_tolerance) - /* if delta to last pulse is too short, don't use this pulse */ - return NULL; - de->last_ts = ts; - - max_updated_seq = pseq_handler_add_to_existing_seqs(de, ts); - - if (!pseq_handler_create_sequences(de, ts, max_updated_seq)) { - pri_detector_reset(de, ts); - return NULL; - } - - ps = pseq_handler_check_detection(de); - - if (ps == NULL) - pulse_queue_enqueue(de, ts); - - return ps; -} - -struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs) -{ - struct pri_detector *de; - - de = kzalloc(sizeof(*de), GFP_ATOMIC); - if (de == NULL) - return NULL; - de->exit = pri_detector_exit; - de->add_pulse = pri_detector_add_pulse; - de->reset = pri_detector_reset; - - INIT_LIST_HEAD(&de->sequences); - INIT_LIST_HEAD(&de->pulses); - de->window_size = rs->pri_max * rs->ppb * rs->num_pri; - de->max_count = rs->ppb * 2; - de->rs = rs; - - pool_register_ref(); - return de; -} diff --git a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h deleted file mode 100644 index 723962d1abc..00000000000 --- a/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2012 Neratec Solutions AG - * - * 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. - */ - -#ifndef DFS_PRI_DETECTOR_H -#define DFS_PRI_DETECTOR_H - -#include <linux/list.h> - -/** - * struct pri_sequence - sequence of pulses matching one PRI - * @head: list_head - * @pri: pulse repetition interval (PRI) in usecs - * @dur: duration of sequence in usecs - * @count: number of pulses in this sequence - * @count_falses: number of not matching pulses in this sequence - * @first_ts: time stamp of first pulse in usecs - * @last_ts: time stamp of last pulse in usecs - * @deadline_ts: deadline when this sequence becomes invalid (first_ts + dur) - */ -struct pri_sequence { - struct list_head head; - u32 pri; - u32 dur; - u32 count; - u32 count_falses; - u64 first_ts; - u64 last_ts; - u64 deadline_ts; -}; - -/** - * struct pri_detector - PRI detector element for a dedicated radar type - * @exit(): destructor - * @add_pulse(): add pulse event, returns pri_sequence if pattern was detected - * @reset(): clear states and reset to given time stamp - * @rs: detector specs for this detector element - * @last_ts: last pulse time stamp considered for this element in usecs - * @sequences: list_head holding potential pulse sequences - * @pulses: list connecting pulse_elem objects - * @count: number of pulses in queue - * @max_count: maximum number of pulses to be queued - * @window_size: window size back from newest pulse time stamp in usecs - */ -struct pri_detector { - void (*exit) (struct pri_detector *de); - struct pri_sequence * - (*add_pulse)(struct pri_detector *de, struct pulse_event *e); - void (*reset) (struct pri_detector *de, u64 ts); - -/* private: internal use only */ - const struct radar_detector_specs *rs; - u64 last_ts; - struct list_head sequences; - struct list_head pulses; - u32 count; - u32 max_count; - u32 window_size; -}; - -struct pri_detector *pri_detector_init(const struct radar_detector_specs *rs); - -#endif /* DFS_PRI_DETECTOR_H */ diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 83f4927aeac..4f9378ddf07 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -78,6 +78,22 @@ static inline void ath9k_hw_antdiv_comb_conf_set(struct ath_hw *ah, ath9k_hw_ops(ah)->antdiv_comb_conf_set(ah, antconf); } +static inline void ath9k_hw_tx99_start(struct ath_hw *ah, u32 qnum) +{ + ath9k_hw_ops(ah)->tx99_start(ah, qnum); +} + +static inline void ath9k_hw_tx99_stop(struct ath_hw *ah) +{ + ath9k_hw_ops(ah)->tx99_stop(ah); +} + +static inline void ath9k_hw_tx99_set_txpower(struct ath_hw *ah, u8 power) +{ + if (ath9k_hw_ops(ah)->tx99_set_txpower) + ath9k_hw_ops(ah)->tx99_set_txpower(ah, power); +} + #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT static inline void ath9k_hw_set_bt_ant_diversity(struct ath_hw *ah, bool enable) diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index dcdbab48709..54b04155e43 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -1885,7 +1885,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, } else if (caldata) { clear_bit(PAPRD_PACKET_SENT, &caldata->cal_flags); } - ah->noise = ath9k_hw_getchan_noise(ah, chan); + ah->noise = ath9k_hw_getchan_noise(ah, chan, chan->noisefloor); if (fastcc) { r = ath9k_hw_do_fastcc(ah, chan); diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 81fcbc75612..9ea24f1cba7 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -703,6 +703,10 @@ struct ath_hw_ops { void (*spectral_scan_trigger)(struct ath_hw *ah); void (*spectral_scan_wait)(struct ath_hw *ah); + void (*tx99_start)(struct ath_hw *ah, u32 qnum); + void (*tx99_stop)(struct ath_hw *ah); + void (*tx99_set_txpower)(struct ath_hw *ah, u8 power); + #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT void (*set_bt_ant_diversity)(struct ath_hw *hw, bool enable); #endif diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 7df728f3633..e89db64532f 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -680,7 +680,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, sc->sc_ah = ah; pCap = &ah->caps; - sc->dfs_detector = dfs_pattern_detector_init(ah, NL80211_DFS_UNSET); + common = ath9k_hw_common(ah); + sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET); + sc->tx99_power = MAX_RATE_POWER + 1; if (!pdata) { ah->ah_flags |= AH_USE_EEPROM; @@ -694,7 +696,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ah->external_reset = pdata->external_reset; } - common = ath9k_hw_common(ah); common->ops = &ah->reg_ops; common->bus_ops = bus_ops; common->ah = ah; @@ -785,6 +786,7 @@ err_queues: ath9k_hw_deinit(ah); err_hw: ath9k_eeprom_release(sc); + dev_kfree_skb_any(sc->tx99_skb); return ret; } @@ -842,7 +844,6 @@ static const struct ieee80211_iface_limit if_limits[] = { BIT(NL80211_IFTYPE_P2P_GO) }, }; - static const struct ieee80211_iface_limit if_dfs_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_AP) }, }; @@ -903,17 +904,18 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) hw->wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR; - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_WDS) | - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT); - - hw->wiphy->iface_combinations = if_comb; - hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + if (!config_enabled(CONFIG_ATH9K_TX99)) { + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_WDS) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + hw->wiphy->iface_combinations = if_comb; + hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb); + } hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 84a60644f93..aed7e29dc50 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -28,6 +28,13 @@ void ath_tx_complete_poll_work(struct work_struct *work) int i; bool needreset = false; + + if (sc->tx99_state) { + ath_dbg(ath9k_hw_common(sc->sc_ah), RESET, + "skip tx hung detection on tx99\n"); + return; + } + for (i = 0; i < IEEE80211_NUM_ACS; i++) { txq = sc->tx.txq_map[i]; @@ -70,7 +77,7 @@ void ath_hw_check(struct work_struct *work) ath9k_ps_wakeup(sc); is_alive = ath9k_hw_check_alive(sc->sc_ah); - if (is_alive && !AR_SREV_9300(sc->sc_ah)) + if ((is_alive && !AR_SREV_9300(sc->sc_ah)) || sc->tx99_state) goto out; else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { ath_dbg(common, RESET, @@ -141,6 +148,9 @@ void ath_hw_pll_work(struct work_struct *work) if (!test_bit(SC_OP_BEACONS, &sc->sc_flags)) return; + if (sc->tx99_state) + return; + ath9k_ps_wakeup(sc); pll_sqsum = ar9003_get_pll_sqsum_dvc(sc->sc_ah); ath9k_ps_restore(sc); @@ -518,7 +528,8 @@ void ath_update_survey_nf(struct ath_softc *sc, int channel) if (chan->noisefloor) { survey->filled |= SURVEY_INFO_NOISE_DBM; - survey->noise = ath9k_hw_getchan_noise(ah, chan); + survey->noise = ath9k_hw_getchan_noise(ah, chan, + chan->noisefloor); } } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 20a2fbc1e34..74f452c7b16 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1046,6 +1046,14 @@ static int ath9k_add_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); + if (config_enabled(CONFIG_ATH9K_TX99)) { + if (sc->nvifs >= 1) { + mutex_unlock(&sc->mutex); + return -EOPNOTSUPP; + } + sc->tx99_vif = vif; + } + ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type); sc->nvifs++; @@ -1074,9 +1082,15 @@ static int ath9k_change_interface(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); - ath_dbg(common, CONFIG, "Change Interface\n"); mutex_lock(&sc->mutex); + if (config_enabled(CONFIG_ATH9K_TX99)) { + mutex_unlock(&sc->mutex); + return -EOPNOTSUPP; + } + + ath_dbg(common, CONFIG, "Change Interface\n"); + if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); @@ -1106,6 +1120,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); sc->nvifs--; + sc->tx99_vif = NULL; if (ath9k_uses_beacons(vif->type)) ath9k_beacon_remove_slot(sc, vif); @@ -1127,6 +1142,9 @@ static void ath9k_enable_ps(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + sc->ps_enabled = true; if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { if ((ah->imask & ATH9K_INT_TIM_TIMER) == 0) { @@ -1143,6 +1161,9 @@ static void ath9k_disable_ps(struct ath_softc *sc) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + sc->ps_enabled = false; ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { @@ -1166,6 +1187,9 @@ void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw) struct ath_common *common = ath9k_hw_common(ah); u32 rxfilter; + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + if (!ath9k_hw_ops(ah)->spectral_scan_trigger) { ath_err(common, "spectrum analyzer not implemented on this hardware\n"); return; @@ -1745,6 +1769,9 @@ static int ath9k_get_survey(struct ieee80211_hw *hw, int idx, unsigned long flags; int pos; + if (config_enabled(CONFIG_ATH9K_TX99)) + return -EOPNOTSUPP; + spin_lock_irqsave(&common->cc_lock, flags); if (idx == 0) ath_update_survey_stats(sc); @@ -1777,6 +1804,9 @@ static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; + if (config_enabled(CONFIG_ATH9K_TX99)) + return; + mutex_lock(&sc->mutex); ah->coverage_class = coverage_class; @@ -2343,6 +2373,134 @@ static void ath9k_channel_switch_beacon(struct ieee80211_hw *hw, sc->csa_vif = vif; } +static void ath9k_tx99_stop(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + + ath_drain_all_txq(sc); + ath_startrecv(sc); + + ath9k_hw_set_interrupts(ah); + ath9k_hw_enable_interrupts(ah); + + ieee80211_wake_queues(sc->hw); + + kfree_skb(sc->tx99_skb); + sc->tx99_skb = NULL; + sc->tx99_state = false; + + ath9k_hw_tx99_stop(sc->sc_ah); + ath_dbg(common, XMIT, "TX99 stopped\n"); +} + +static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc) +{ + static u8 PN9Data[] = {0xff, 0x87, 0xb8, 0x59, 0xb7, 0xa1, 0xcc, 0x24, + 0x57, 0x5e, 0x4b, 0x9c, 0x0e, 0xe9, 0xea, 0x50, + 0x2a, 0xbe, 0xb4, 0x1b, 0xb6, 0xb0, 0x5d, 0xf1, + 0xe6, 0x9a, 0xe3, 0x45, 0xfd, 0x2c, 0x53, 0x18, + 0x0c, 0xca, 0xc9, 0xfb, 0x49, 0x37, 0xe5, 0xa8, + 0x51, 0x3b, 0x2f, 0x61, 0xaa, 0x72, 0x18, 0x84, + 0x02, 0x23, 0x23, 0xab, 0x63, 0x89, 0x51, 0xb3, + 0xe7, 0x8b, 0x72, 0x90, 0x4c, 0xe8, 0xfb, 0xc0}; + u32 len = 1200; + struct ieee80211_hw *hw = sc->hw; + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *tx_info; + struct sk_buff *skb; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return NULL; + + skb_put(skb, len); + + memset(skb->data, 0, len); + + hdr = (struct ieee80211_hdr *)skb->data; + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA); + hdr->duration_id = 0; + + memcpy(hdr->addr1, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr2, hw->wiphy->perm_addr, ETH_ALEN); + memcpy(hdr->addr3, hw->wiphy->perm_addr, ETH_ALEN); + + hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); + + tx_info = IEEE80211_SKB_CB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->band = hw->conf.chandef.chan->band; + tx_info->flags = IEEE80211_TX_CTL_NO_ACK; + tx_info->control.vif = sc->tx99_vif; + + memcpy(skb->data + sizeof(*hdr), PN9Data, sizeof(PN9Data)); + + return skb; +} + +void ath9k_tx99_deinit(struct ath_softc *sc) +{ + ath_reset(sc); + + ath9k_ps_wakeup(sc); + ath9k_tx99_stop(sc); + ath9k_ps_restore(sc); +} + +int ath9k_tx99_init(struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_tx_control txctl; + int r; + + if (sc->sc_flags & SC_OP_INVALID) { + ath_err(common, + "driver is in invalid state unable to use TX99"); + return -EINVAL; + } + + sc->tx99_skb = ath9k_build_tx99_skb(sc); + if (!sc->tx99_skb) + return -ENOMEM; + + memset(&txctl, 0, sizeof(txctl)); + txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO]; + + ath_reset(sc); + + ath9k_ps_wakeup(sc); + + ath9k_hw_disable_interrupts(ah); + atomic_set(&ah->intr_ref_cnt, -1); + ath_drain_all_txq(sc); + ath_stoprecv(sc); + + sc->tx99_state = true; + + ieee80211_stop_queues(hw); + + if (sc->tx99_power == MAX_RATE_POWER + 1) + sc->tx99_power = MAX_RATE_POWER; + + ath9k_hw_tx99_set_txpower(ah, sc->tx99_power); + r = ath9k_tx99_send(sc, sc->tx99_skb, &txctl); + if (r) { + ath_dbg(common, XMIT, "Failed to xmit TX99 skb\n"); + return r; + } + + ath_dbg(common, XMIT, "TX99 xmit started using %d ( %ddBm)\n", + sc->tx99_power, + sc->tx99_power / 2); + + /* We leave the harware awake as it will be chugging on */ + + return 0; +} + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 8b788efb41f..95ddca5495d 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -375,6 +375,9 @@ u32 ath_calcrxfilter(struct ath_softc *sc) { u32 rfilt; + if (config_enabled(CONFIG_ATH9K_TX99)) + return 0; + rfilt = ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST | ATH9K_RX_FILTER_MCAST; @@ -972,14 +975,15 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, { #ifdef CONFIG_ATH9K_DEBUGFS struct ath_hw *ah = sc->sc_ah; - u8 bins[SPECTRAL_HT20_NUM_BINS]; - u8 *vdata = (u8 *)hdr; - struct fft_sample_ht20 fft_sample; + u8 num_bins, *bins, *vdata = (u8 *)hdr; + struct fft_sample_ht20 fft_sample_20; + struct fft_sample_ht20_40 fft_sample_40; + struct fft_sample_tlv *tlv; struct ath_radar_info *radar_info; - struct ath_ht20_mag_info *mag_info; int len = rs->rs_datalen; int dc_pos; - u16 length, max_magnitude; + u16 fft_len, length, freq = ah->curchan->chan->center_freq; + enum nl80211_channel_type chan_type; /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT @@ -997,45 +1001,44 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) return 0; - /* Variation in the data length is possible and will be fixed later. - * Note that we only support HT20 for now. - * - * TODO: add HT20_40 support as well. - */ - if ((len > SPECTRAL_HT20_TOTAL_DATA_LEN + 2) || - (len < SPECTRAL_HT20_TOTAL_DATA_LEN - 1)) - return 1; - - fft_sample.tlv.type = ATH_FFT_SAMPLE_HT20; - length = sizeof(fft_sample) - sizeof(fft_sample.tlv); - fft_sample.tlv.length = __cpu_to_be16(length); + chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef); + if ((chan_type == NL80211_CHAN_HT40MINUS) || + (chan_type == NL80211_CHAN_HT40PLUS)) { + fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; + num_bins = SPECTRAL_HT20_40_NUM_BINS; + bins = (u8 *)fft_sample_40.data; + } else { + fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; + num_bins = SPECTRAL_HT20_NUM_BINS; + bins = (u8 *)fft_sample_20.data; + } - fft_sample.freq = __cpu_to_be16(ah->curchan->chan->center_freq); - fft_sample.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); - fft_sample.noise = ah->noise; + /* Variation in the data length is possible and will be fixed later */ + if ((len > fft_len + 2) || (len < fft_len - 1)) + return 1; - switch (len - SPECTRAL_HT20_TOTAL_DATA_LEN) { + switch (len - fft_len) { case 0: /* length correct, nothing to do. */ - memcpy(bins, vdata, SPECTRAL_HT20_NUM_BINS); + memcpy(bins, vdata, num_bins); break; case -1: /* first byte missing, duplicate it. */ - memcpy(&bins[1], vdata, SPECTRAL_HT20_NUM_BINS - 1); + memcpy(&bins[1], vdata, num_bins - 1); bins[0] = vdata[0]; break; case 2: /* MAC added 2 extra bytes at bin 30 and 32, remove them. */ memcpy(bins, vdata, 30); bins[30] = vdata[31]; - memcpy(&bins[31], &vdata[33], SPECTRAL_HT20_NUM_BINS - 31); + memcpy(&bins[31], &vdata[33], num_bins - 31); break; case 1: /* MAC added 2 extra bytes AND first byte is missing. */ bins[0] = vdata[0]; - memcpy(&bins[0], vdata, 30); + memcpy(&bins[1], vdata, 30); bins[31] = vdata[31]; - memcpy(&bins[32], &vdata[33], SPECTRAL_HT20_NUM_BINS - 32); + memcpy(&bins[32], &vdata[33], num_bins - 32); break; default: return 1; @@ -1044,23 +1047,93 @@ static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, /* DC value (value in the middle) is the blind spot of the spectral * sample and invalid, interpolate it. */ - dc_pos = SPECTRAL_HT20_NUM_BINS / 2; + dc_pos = num_bins / 2; bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2; - /* mag data is at the end of the frame, in front of radar_info */ - mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; + if ((chan_type == NL80211_CHAN_HT40MINUS) || + (chan_type == NL80211_CHAN_HT40PLUS)) { + s8 lower_rssi, upper_rssi; + s16 ext_nf; + u8 lower_max_index, upper_max_index; + u8 lower_bitmap_w, upper_bitmap_w; + u16 lower_mag, upper_mag; + struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_ht20_40_mag_info *mag_info; + + if (caldata) + ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan, + caldata->nfCalHist[3].privNF); + else + ext_nf = ATH_DEFAULT_NOISE_FLOOR; + + length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv); + fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40; + fft_sample_40.tlv.length = __cpu_to_be16(length); + fft_sample_40.freq = __cpu_to_be16(freq); + fft_sample_40.channel_type = chan_type; + + if (chan_type == NL80211_CHAN_HT40PLUS) { + lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); + upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0); - /* copy raw bins without scaling them */ - memcpy(fft_sample.data, bins, SPECTRAL_HT20_NUM_BINS); - fft_sample.max_exp = mag_info->max_exp & 0xf; + fft_sample_40.lower_noise = ah->noise; + fft_sample_40.upper_noise = ext_nf; + } else { + lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0); + upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); - max_magnitude = spectral_max_magnitude(mag_info->all_bins); - fft_sample.max_magnitude = __cpu_to_be16(max_magnitude); - fft_sample.max_index = spectral_max_index(mag_info->all_bins); - fft_sample.bitmap_weight = spectral_bitmap_weight(mag_info->all_bins); - fft_sample.tsf = __cpu_to_be64(tsf); + fft_sample_40.lower_noise = ext_nf; + fft_sample_40.upper_noise = ah->noise; + } + fft_sample_40.lower_rssi = lower_rssi; + fft_sample_40.upper_rssi = upper_rssi; + + mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1; + lower_mag = spectral_max_magnitude(mag_info->lower_bins); + upper_mag = spectral_max_magnitude(mag_info->upper_bins); + fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); + fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); + lower_max_index = spectral_max_index(mag_info->lower_bins); + upper_max_index = spectral_max_index(mag_info->upper_bins); + fft_sample_40.lower_max_index = lower_max_index; + fft_sample_40.upper_max_index = upper_max_index; + lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins); + upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins); + fft_sample_40.lower_bitmap_weight = lower_bitmap_w; + fft_sample_40.upper_bitmap_weight = upper_bitmap_w; + fft_sample_40.max_exp = mag_info->max_exp & 0xf; + + fft_sample_40.tsf = __cpu_to_be64(tsf); + + tlv = (struct fft_sample_tlv *)&fft_sample_40; + } else { + u8 max_index, bitmap_w; + u16 magnitude; + struct ath_ht20_mag_info *mag_info; + + length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv); + fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20; + fft_sample_20.tlv.length = __cpu_to_be16(length); + fft_sample_20.freq = __cpu_to_be16(freq); + + fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); + fft_sample_20.noise = ah->noise; + + mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; + magnitude = spectral_max_magnitude(mag_info->all_bins); + fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); + max_index = spectral_max_index(mag_info->all_bins); + fft_sample_20.max_index = max_index; + bitmap_w = spectral_bitmap_weight(mag_info->all_bins); + fft_sample_20.bitmap_weight = bitmap_w; + fft_sample_20.max_exp = mag_info->max_exp & 0xf; + + fft_sample_20.tsf = __cpu_to_be64(tsf); + + tlv = (struct fft_sample_tlv *)&fft_sample_20; + } - ath_debug_send_fft_sample(sc, &fft_sample.tlv); + ath_debug_send_fft_sample(sc, tlv); return 1; #else return 0; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 47696d29743..09cdbcd0973 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1241,12 +1241,13 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, if (bf->bf_next) info.link = bf->bf_next->bf_daddr; else - info.link = 0; + info.link = (sc->tx99_state) ? bf->bf_daddr : 0; if (!bf_first) { bf_first = bf; - info.flags = ATH9K_TXDESC_INTREQ; + if (!sc->tx99_state) + info.flags = ATH9K_TXDESC_INTREQ; if ((tx_info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) || txq == sc->tx.uapsdq) info.flags |= ATH9K_TXDESC_CLRDMASK; @@ -1941,7 +1942,7 @@ static void ath_tx_txqaddbuf(struct ath_softc *sc, struct ath_txq *txq, txq->axq_qnum, ito64(bf->bf_daddr), bf->bf_desc); } - if (!edma) { + if (!edma || sc->tx99_state) { TX_STAT_INC(txq->axq_qnum, txstart); ath9k_hw_txstart(ah, txq->axq_qnum); } @@ -2020,6 +2021,9 @@ static void setup_frame_info(struct ieee80211_hw *hw, fi->keyix = ATH9K_TXKEYIX_INVALID; fi->keytype = keytype; fi->framelen = framelen; + + if (!rate) + return; fi->rtscts_rate = rate->hw_value; if (short_preamble) fi->rtscts_rate |= rate->hw_value_short; @@ -2371,6 +2375,8 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, dma_unmap_single(sc->dev, bf->bf_buf_addr, skb->len, DMA_TO_DEVICE); bf->bf_buf_addr = 0; + if (sc->tx99_state) + goto skip_tx_complete; if (bf->bf_state.bfs_paprd) { if (time_after(jiffies, @@ -2383,6 +2389,7 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf, ath_debug_stat_tx(sc, bf, ts, txq, tx_flags); ath_tx_complete(sc, skb, tx_flags, txq); } +skip_tx_complete: /* At this point, skb (bf->bf_mpdu) is consumed...make sure we don't * accidentally reference it later. */ @@ -2741,3 +2748,46 @@ void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) ath_txq_unlock(sc, txq); } } + +int ath9k_tx99_send(struct ath_softc *sc, struct sk_buff *skb, + struct ath_tx_control *txctl) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ath_frame_info *fi = get_frame_info(skb); + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_buf *bf; + int padpos, padsize; + + padpos = ieee80211_hdrlen(hdr->frame_control); + padsize = padpos & 3; + + if (padsize && skb->len > padpos) { + if (skb_headroom(skb) < padsize) { + ath_dbg(common, XMIT, + "tx99 padding failed\n"); + return -EINVAL; + } + + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, padpos); + } + + fi->keyix = ATH9K_TXKEYIX_INVALID; + fi->framelen = skb->len + FCS_LEN; + fi->keytype = ATH9K_KEY_TYPE_CLEAR; + + bf = ath_tx_setup_buffer(sc, txctl->txq, NULL, skb); + if (!bf) { + ath_dbg(common, XMIT, "tx99 buffer setup failed\n"); + return -EINVAL; + } + + ath_set_rates(sc->tx99_vif, NULL, bf); + + ath9k_hw_set_desc_link(sc->sc_ah, bf->bf_desc, bf->bf_daddr); + ath9k_hw_tx99_start(sc->sc_ah, txctl->txq->axq_qnum); + + ath_tx_send_normal(sc, txctl->txq, NULL, skb); + + return 0; +} |