diff options
Diffstat (limited to 'drivers/net/wireless/ath')
95 files changed, 5884 insertions, 1198 deletions
diff --git a/drivers/net/wireless/ath/ath5k/ani.c b/drivers/net/wireless/ath/ath5k/ani.c index 35e93704c4ef..5c008757662b 100644 --- a/drivers/net/wireless/ath/ath5k/ani.c +++ b/drivers/net/wireless/ath/ath5k/ani.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "ath5k.h" #include "reg.h" #include "debug.h" @@ -728,33 +730,25 @@ void ath5k_ani_print_counters(struct ath5k_hw *ah) { /* clears too */ - printk(KERN_NOTICE "ACK fail\t%d\n", - ath5k_hw_reg_read(ah, AR5K_ACK_FAIL)); - printk(KERN_NOTICE "RTS fail\t%d\n", - ath5k_hw_reg_read(ah, AR5K_RTS_FAIL)); - printk(KERN_NOTICE "RTS success\t%d\n", - ath5k_hw_reg_read(ah, AR5K_RTS_OK)); - printk(KERN_NOTICE "FCS error\t%d\n", - ath5k_hw_reg_read(ah, AR5K_FCS_FAIL)); + pr_notice("ACK fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_ACK_FAIL)); + pr_notice("RTS fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_FAIL)); + pr_notice("RTS success\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_OK)); + pr_notice("FCS error\t%d\n", ath5k_hw_reg_read(ah, AR5K_FCS_FAIL)); /* no clear */ - printk(KERN_NOTICE "tx\t%d\n", - ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX)); - printk(KERN_NOTICE "rx\t%d\n", - ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX)); - printk(KERN_NOTICE "busy\t%d\n", - ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR)); - printk(KERN_NOTICE "cycles\t%d\n", - ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE)); - - printk(KERN_NOTICE "AR5K_PHYERR_CNT1\t%d\n", - ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1)); - printk(KERN_NOTICE "AR5K_PHYERR_CNT2\t%d\n", - ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2)); - printk(KERN_NOTICE "AR5K_OFDM_FIL_CNT\t%d\n", - ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT)); - printk(KERN_NOTICE "AR5K_CCK_FIL_CNT\t%d\n", - ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT)); + pr_notice("tx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX)); + pr_notice("rx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX)); + pr_notice("busy\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR)); + pr_notice("cycles\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE)); + + pr_notice("AR5K_PHYERR_CNT1\t%d\n", + ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1)); + pr_notice("AR5K_PHYERR_CNT2\t%d\n", + ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2)); + pr_notice("AR5K_OFDM_FIL_CNT\t%d\n", + ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT)); + pr_notice("AR5K_CCK_FIL_CNT\t%d\n", + ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT)); } #endif diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 8d434b8f5855..64a453a6dfe4 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -76,26 +76,29 @@ GENERIC DRIVER DEFINITIONS \****************************/ -#define ATH5K_PRINTF(fmt, ...) \ - printk(KERN_WARNING "%s: " fmt, __func__, ##__VA_ARGS__) +#define ATH5K_PRINTF(fmt, ...) \ + pr_warn("%s: " fmt, __func__, ##__VA_ARGS__) -#define ATH5K_PRINTK(_sc, _level, _fmt, ...) \ - printk(_level "ath5k %s: " _fmt, \ - ((_sc) && (_sc)->hw) ? wiphy_name((_sc)->hw->wiphy) : "", \ - ##__VA_ARGS__) +void __printf(3, 4) +_ath5k_printk(const struct ath5k_hw *ah, const char *level, + const char *fmt, ...); -#define ATH5K_PRINTK_LIMIT(_sc, _level, _fmt, ...) do { \ - if (net_ratelimit()) \ - ATH5K_PRINTK(_sc, _level, _fmt, ##__VA_ARGS__); \ - } while (0) +#define ATH5K_PRINTK(_sc, _level, _fmt, ...) \ + _ath5k_printk(_sc, _level, _fmt, ##__VA_ARGS__) -#define ATH5K_INFO(_sc, _fmt, ...) \ +#define ATH5K_PRINTK_LIMIT(_sc, _level, _fmt, ...) \ +do { \ + if (net_ratelimit()) \ + ATH5K_PRINTK(_sc, _level, _fmt, ##__VA_ARGS__); \ +} while (0) + +#define ATH5K_INFO(_sc, _fmt, ...) \ ATH5K_PRINTK(_sc, KERN_INFO, _fmt, ##__VA_ARGS__) -#define ATH5K_WARN(_sc, _fmt, ...) \ +#define ATH5K_WARN(_sc, _fmt, ...) \ ATH5K_PRINTK_LIMIT(_sc, KERN_WARNING, _fmt, ##__VA_ARGS__) -#define ATH5K_ERR(_sc, _fmt, ...) \ +#define ATH5K_ERR(_sc, _fmt, ...) \ ATH5K_PRINTK_LIMIT(_sc, KERN_ERR, _fmt, ##__VA_ARGS__) /* @@ -1524,7 +1527,7 @@ void ath5k_eeprom_detach(struct ath5k_hw *ah); /* Protocol Control Unit Functions */ /* Helpers */ -int ath5k_hw_get_frame_duration(struct ath5k_hw *ah, +int ath5k_hw_get_frame_duration(struct ath5k_hw *ah, enum ieee80211_band band, int len, struct ieee80211_rate *rate, bool shortpre); unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah); unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah); diff --git a/drivers/net/wireless/ath/ath5k/attach.c b/drivers/net/wireless/ath/ath5k/attach.c index d7114c75fe9b..7106547a14dd 100644 --- a/drivers/net/wireless/ath/ath5k/attach.c +++ b/drivers/net/wireless/ath/ath5k/attach.c @@ -20,6 +20,8 @@ * Attach/Detach Functions and helpers * \*************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/pci.h> #include <linux/slab.h> #include "ath5k.h" diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 0e643b016b32..fbaa30930076 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -40,6 +40,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/delay.h> #include <linux/dma-mapping.h> @@ -460,7 +462,7 @@ void ath5k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) } if (iter_data->need_set_hw_addr && iter_data->hw_macaddr) - if (compare_ether_addr(iter_data->hw_macaddr, mac) == 0) + if (ether_addr_equal(iter_data->hw_macaddr, mac)) iter_data->need_set_hw_addr = false; if (!iter_data->any_assoc) { @@ -1168,7 +1170,7 @@ ath5k_check_ibss_tsf(struct ath5k_hw *ah, struct sk_buff *skb, if (ieee80211_is_beacon(mgmt->frame_control) && le16_to_cpu(mgmt->u.beacon.capab_info) & WLAN_CAPABILITY_IBSS && - memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) == 0) { + ether_addr_equal(mgmt->bssid, common->curbssid)) { /* * Received an IBSS beacon with the same BSSID. Hardware *must* * have updated the local TSF. We have to work around various @@ -1232,7 +1234,7 @@ ath5k_update_beacon_rssi(struct ath5k_hw *ah, struct sk_buff *skb, int rssi) /* only beacons from our BSSID */ if (!ieee80211_is_beacon(mgmt->frame_control) || - memcmp(mgmt->bssid, common->curbssid, ETH_ALEN) != 0) + !ether_addr_equal(mgmt->bssid, common->curbssid)) return; ewma_add(&ah->ah_beacon_rssi_avg, rssi); @@ -2413,6 +2415,22 @@ ath5k_tx_complete_poll_work(struct work_struct *work) * Initialization routines * \*************************/ +static const struct ieee80211_iface_limit if_limits[] = { + { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) }, + { .max = 4, .types = +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_AP) }, +}; + +static const struct ieee80211_iface_combination if_comb = { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = 2048, + .num_different_channels = 1, +}; + int __devinit ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) { @@ -2434,6 +2452,9 @@ ath5k_init_ah(struct ath5k_hw *ah, const struct ath_bus_ops *bus_ops) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); + hw->wiphy->iface_combinations = &if_comb; + hw->wiphy->n_iface_combinations = 1; + /* SW support for IBSS_RSN is provided by mac80211 */ hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; @@ -3038,3 +3059,23 @@ ath5k_set_beacon_filter(struct ieee80211_hw *hw, bool enable) ath5k_hw_set_rx_filter(ah, rfilt); ah->filter_flags = rfilt; } + +void _ath5k_printk(const struct ath5k_hw *ah, const char *level, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + if (ah && ah->hw) + printk("%s" pr_fmt("%s: %pV"), + level, wiphy_name(ah->hw->wiphy), &vaf); + else + printk("%s" pr_fmt("%pV"), level, &vaf); + + va_end(args); +} diff --git a/drivers/net/wireless/ath/ath5k/debug.c b/drivers/net/wireless/ath/ath5k/debug.c index e5e8f45d86ac..9d00dab666a8 100644 --- a/drivers/net/wireless/ath/ath5k/debug.c +++ b/drivers/net/wireless/ath/ath5k/debug.c @@ -57,6 +57,9 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGES. */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/export.h> #include <linux/moduleparam.h> @@ -247,10 +250,10 @@ static ssize_t write_file_beacon(struct file *file, if (strncmp(buf, "disable", 7) == 0) { AR5K_REG_DISABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); - printk(KERN_INFO "debugfs disable beacons\n"); + pr_info("debugfs disable beacons\n"); } else if (strncmp(buf, "enable", 6) == 0) { AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_ENABLE); - printk(KERN_INFO "debugfs enable beacons\n"); + pr_info("debugfs enable beacons\n"); } return count; } @@ -450,19 +453,19 @@ static ssize_t write_file_antenna(struct file *file, if (strncmp(buf, "diversity", 9) == 0) { ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_DEFAULT); - printk(KERN_INFO "ath5k debug: enable diversity\n"); + pr_info("debug: enable diversity\n"); } else if (strncmp(buf, "fixed-a", 7) == 0) { ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_A); - printk(KERN_INFO "ath5k debugfs: fixed antenna A\n"); + pr_info("debug: fixed antenna A\n"); } else if (strncmp(buf, "fixed-b", 7) == 0) { ath5k_hw_set_antenna_mode(ah, AR5K_ANTMODE_FIXED_B); - printk(KERN_INFO "ath5k debug: fixed antenna B\n"); + pr_info("debug: fixed antenna B\n"); } else if (strncmp(buf, "clear", 5) == 0) { for (i = 0; i < ARRAY_SIZE(ah->stats.antenna_rx); i++) { ah->stats.antenna_rx[i] = 0; ah->stats.antenna_tx[i] = 0; } - printk(KERN_INFO "ath5k debug: cleared antenna stats\n"); + pr_info("debug: cleared antenna stats\n"); } return count; } @@ -632,7 +635,7 @@ static ssize_t write_file_frameerrors(struct file *file, st->txerr_fifo = 0; st->txerr_filt = 0; st->tx_all_count = 0; - printk(KERN_INFO "ath5k debug: cleared frameerrors stats\n"); + pr_info("debug: cleared frameerrors stats\n"); } return count; } diff --git a/drivers/net/wireless/ath/ath5k/desc.c b/drivers/net/wireless/ath/ath5k/desc.c index f8bfa3ac2af0..bd8d4392d68b 100644 --- a/drivers/net/wireless/ath/ath5k/desc.c +++ b/drivers/net/wireless/ath/ath5k/desc.c @@ -21,6 +21,8 @@ Hardware Descriptor Functions \******************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "ath5k.h" #include "reg.h" #include "debug.h" @@ -441,10 +443,8 @@ ath5k_hw_proc_2word_tx_status(struct ath5k_hw *ah, struct ath5k_desc *desc, struct ath5k_tx_status *ts) { - struct ath5k_hw_2w_tx_ctl *tx_ctl; struct ath5k_hw_tx_status *tx_status; - tx_ctl = &desc->ud.ds_tx5210.tx_ctl; tx_status = &desc->ud.ds_tx5210.tx_stat; /* No frame has been send or error */ @@ -495,11 +495,9 @@ ath5k_hw_proc_4word_tx_status(struct ath5k_hw *ah, struct ath5k_desc *desc, struct ath5k_tx_status *ts) { - struct ath5k_hw_4w_tx_ctl *tx_ctl; struct ath5k_hw_tx_status *tx_status; u32 txstat0, txstat1; - tx_ctl = &desc->ud.ds_tx5212.tx_ctl; tx_status = &desc->ud.ds_tx5212.tx_stat; txstat1 = ACCESS_ONCE(tx_status->tx_status_1); diff --git a/drivers/net/wireless/ath/ath5k/dma.c b/drivers/net/wireless/ath/ath5k/dma.c index 5cc9aa814697..ce86f158423b 100644 --- a/drivers/net/wireless/ath/ath5k/dma.c +++ b/drivers/net/wireless/ath/ath5k/dma.c @@ -29,6 +29,8 @@ * status registers (ISR). */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "ath5k.h" #include "reg.h" #include "debug.h" diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c index cd708c15b774..4026c906cc7b 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -21,6 +21,8 @@ * EEPROM access functions and helpers * \*************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/slab.h> #include "ath5k.h" diff --git a/drivers/net/wireless/ath/ath5k/initvals.c b/drivers/net/wireless/ath/ath5k/initvals.c index a1ea78e05b47..ee1c2fa8b591 100644 --- a/drivers/net/wireless/ath/ath5k/initvals.c +++ b/drivers/net/wireless/ath/ath5k/initvals.c @@ -19,6 +19,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "ath5k.h" #include "reg.h" #include "debug.h" @@ -1574,8 +1576,7 @@ ath5k_hw_write_initvals(struct ath5k_hw *ah, u8 mode, bool skip_pcu) /* AR5K_MODE_11B */ if (mode > 2) { - ATH5K_ERR(ah, - "unsupported channel mode: %d\n", mode); + ATH5K_ERR(ah, "unsupported channel mode: %d\n", mode); return -EINVAL; } diff --git a/drivers/net/wireless/ath/ath5k/led.c b/drivers/net/wireless/ath/ath5k/led.c index c1151c723711..b9f708a45f4e 100644 --- a/drivers/net/wireless/ath/ath5k/led.c +++ b/drivers/net/wireless/ath/ath5k/led.c @@ -39,6 +39,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/pci.h> #include "ath5k.h" diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 5c5329955414..22b80af0f47c 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -41,6 +41,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <net/mac80211.h> #include <asm/unaligned.h> diff --git a/drivers/net/wireless/ath/ath5k/pci.c b/drivers/net/wireless/ath/ath5k/pci.c index 849fa060ebc4..dff48fbc63bf 100644 --- a/drivers/net/wireless/ath/ath5k/pci.c +++ b/drivers/net/wireless/ath/ath5k/pci.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/nl80211.h> #include <linux/pci.h> #include <linux/pci-aspm.h> @@ -45,6 +47,7 @@ static DEFINE_PCI_DEVICE_TABLE(ath5k_pci_id_table) = { { PCI_VDEVICE(ATHEROS, 0x001b) }, /* 5413 Eagle */ { PCI_VDEVICE(ATHEROS, 0x001c) }, /* PCI-E cards */ { PCI_VDEVICE(ATHEROS, 0x001d) }, /* 2417 Nala */ + { PCI_VDEVICE(ATHEROS, 0xff1b) }, /* AR5BXB63 */ { 0 } }; MODULE_DEVICE_TABLE(pci, ath5k_pci_id_table); @@ -337,28 +340,4 @@ static struct pci_driver ath5k_pci_driver = { .driver.pm = ATH5K_PM_OPS, }; -/* - * Module init/exit functions - */ -static int __init -init_ath5k_pci(void) -{ - int ret; - - ret = pci_register_driver(&ath5k_pci_driver); - if (ret) { - printk(KERN_ERR "ath5k_pci: can't register pci driver\n"); - return ret; - } - - return 0; -} - -static void __exit -exit_ath5k_pci(void) -{ - pci_unregister_driver(&ath5k_pci_driver); -} - -module_init(init_ath5k_pci); -module_exit(exit_ath5k_pci); +module_pci_driver(ath5k_pci_driver); diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index cebfd6fd31d3..1f16b4227d8f 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -110,7 +110,7 @@ static const unsigned int ack_rates_high[] = * bwmodes. */ int -ath5k_hw_get_frame_duration(struct ath5k_hw *ah, +ath5k_hw_get_frame_duration(struct ath5k_hw *ah, enum ieee80211_band band, int len, struct ieee80211_rate *rate, bool shortpre) { int sifs, preamble, plcp_bits, sym_time; @@ -120,7 +120,7 @@ ath5k_hw_get_frame_duration(struct ath5k_hw *ah, /* Fallback */ if (!ah->ah_bwmode) { __le16 raw_dur = ieee80211_generic_frame_duration(ah->hw, - NULL, len, rate); + NULL, band, len, rate); /* subtract difference between long and short preamble */ dur = le16_to_cpu(raw_dur); @@ -302,14 +302,15 @@ ath5k_hw_write_rate_duration(struct ath5k_hw *ah) * actual rate for this rate. See mac80211 tx.c * ieee80211_duration() for a brief description of * what rate we should choose to TX ACKs. */ - tx_time = ath5k_hw_get_frame_duration(ah, 10, rate, false); + tx_time = ath5k_hw_get_frame_duration(ah, band, 10, + rate, false); ath5k_hw_reg_write(ah, tx_time, reg); if (!(rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)) continue; - tx_time = ath5k_hw_get_frame_duration(ah, 10, rate, true); + tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, true); ath5k_hw_reg_write(ah, tx_time, reg + (AR5K_SET_SHORT_PREAMBLE << 2)); } diff --git a/drivers/net/wireless/ath/ath5k/phy.c b/drivers/net/wireless/ath/ath5k/phy.c index 3a2845489a1b..8b71a2d947e0 100644 --- a/drivers/net/wireless/ath/ath5k/phy.c +++ b/drivers/net/wireless/ath/ath5k/phy.c @@ -22,6 +22,8 @@ * PHY related functions * \***********************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/delay.h> #include <linux/slab.h> #include <asm/unaligned.h> diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c index 30b50f934172..65fe929529a8 100644 --- a/drivers/net/wireless/ath/ath5k/qcu.c +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -20,6 +20,8 @@ Queue Control Unit, DCF Control Unit Functions \********************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "ath5k.h" #include "reg.h" #include "debug.h" @@ -563,6 +565,7 @@ ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time) { struct ieee80211_channel *channel = ah->ah_current_channel; + enum ieee80211_band band; struct ieee80211_rate *rate; u32 ack_tx_time, eifs, eifs_clock, sifs, sifs_clock; u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time); @@ -598,11 +601,12 @@ int ath5k_hw_set_ifs_intervals(struct ath5k_hw *ah, unsigned int slot_time) * Also we have different lowest rate for 802.11a */ if (channel->band == IEEE80211_BAND_5GHZ) - rate = &ah->sbands[IEEE80211_BAND_5GHZ].bitrates[0]; + band = IEEE80211_BAND_5GHZ; else - rate = &ah->sbands[IEEE80211_BAND_2GHZ].bitrates[0]; + band = IEEE80211_BAND_2GHZ; - ack_tx_time = ath5k_hw_get_frame_duration(ah, 10, rate, false); + rate = &ah->sbands[band].bitrates[0]; + ack_tx_time = ath5k_hw_get_frame_duration(ah, band, 10, rate, false); /* ack_tx_time includes an SIFS already */ eifs = ack_tx_time + sifs + 2 * slot_time; diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 200f165c0c6d..0c2dd4771c36 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -23,6 +23,8 @@ Reset function and helpers \****************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <asm/unaligned.h> #include <linux/pci.h> /* To determine if a card is pci-e */ diff --git a/drivers/net/wireless/ath/ath5k/sysfs.c b/drivers/net/wireless/ath/ath5k/sysfs.c index 9364da7bd131..04cf0ca72610 100644 --- a/drivers/net/wireless/ath/ath5k/sysfs.c +++ b/drivers/net/wireless/ath/ath5k/sysfs.c @@ -1,3 +1,5 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/device.h> #include <linux/pci.h> diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index 85746c3eb027..8cae8886f17d 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -25,7 +25,8 @@ obj-$(CONFIG_ATH6KL) += ath6kl_core.o ath6kl_core-y += debug.o ath6kl_core-y += hif.o -ath6kl_core-y += htc.o +ath6kl_core-y += htc_mbox.o +ath6kl_core-y += htc_pipe.o ath6kl_core-y += bmi.o ath6kl_core-y += cfg80211.o ath6kl_core-y += init.o diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 00d38952b5fb..b869a358ce43 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -15,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/moduleparam.h> #include <linux/inetdevice.h> #include <linux/export.h> @@ -49,6 +51,8 @@ .max_power = 30, \ } +#define DEFAULT_BG_SCAN_PERIOD 60 + static struct ieee80211_rate ath6kl_rates[] = { RATETAB_ENT(10, 0x1, 0), RATETAB_ENT(20, 0x2, 0), @@ -69,7 +73,8 @@ static struct ieee80211_rate ath6kl_rates[] = { #define ath6kl_g_rates (ath6kl_rates + 0) #define ath6kl_g_rates_size 12 -#define ath6kl_g_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ +#define ath6kl_g_htcap IEEE80211_HT_CAP_SGI_20 +#define ath6kl_a_htcap (IEEE80211_HT_CAP_SUP_WIDTH_20_40 | \ IEEE80211_HT_CAP_SGI_20 | \ IEEE80211_HT_CAP_SGI_40) @@ -126,7 +131,7 @@ static struct ieee80211_supported_band ath6kl_band_5ghz = { .channels = ath6kl_5ghz_a_channels, .n_bitrates = ath6kl_a_rates_size, .bitrates = ath6kl_a_rates, - .ht_cap.cap = ath6kl_g_htcap, + .ht_cap.cap = ath6kl_a_htcap, .ht_cap.ht_supported = true, }; @@ -607,6 +612,17 @@ static int ath6kl_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, vif->req_bssid, vif->ch_hint, ar->connect_ctrl_flags, nw_subtype); + /* disable background scan if period is 0 */ + if (sme->bg_scan_period == 0) + sme->bg_scan_period = 0xffff; + + /* configure default value if not specified */ + if (sme->bg_scan_period == -1) + sme->bg_scan_period = DEFAULT_BG_SCAN_PERIOD; + + ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0, + sme->bg_scan_period, 0, 0, 0, 3, 0, 0, 0); + up(&ar->sem); if (status == -EINVAL) { @@ -677,8 +693,8 @@ ath6kl_add_bss_if_needed(struct ath6kl_vif *vif, ie, 2 + vif->ssid_len + beacon_ie_len, 0, GFP_KERNEL); if (bss) - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "added bss %pM to " - "cfg80211\n", bssid); + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "added bss %pM to cfg80211\n", bssid); kfree(ie); } else ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "cfg80211 already has a bss\n"); @@ -866,6 +882,32 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, vif->sme_state = SME_DISCONNECTED; } +static int ath6kl_set_probed_ssids(struct ath6kl *ar, + struct ath6kl_vif *vif, + struct cfg80211_ssid *ssids, int n_ssids) +{ + u8 i; + + if (n_ssids > MAX_PROBED_SSID_INDEX) + return -EINVAL; + + for (i = 0; i < n_ssids; i++) { + ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i, + ssids[i].ssid_len ? + SPECIFIC_SSID_FLAG : ANY_SSID_FLAG, + ssids[i].ssid_len, + ssids[i].ssid); + } + + /* Make sure no old entries are left behind */ + for (i = n_ssids; i < MAX_PROBED_SSID_INDEX; i++) { + ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i, + DISABLE_SSID_FLAG, 0, NULL); + } + + return 0; +} + static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, struct cfg80211_scan_request *request) { @@ -883,36 +925,25 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, if (!ar->usr_bss_filter) { clear_bit(CLEAR_BSSFILTER_ON_BEACON, &vif->flags); - ret = ath6kl_wmi_bssfilter_cmd( - ar->wmi, vif->fw_vif_idx, - (test_bit(CONNECTED, &vif->flags) ? - ALL_BUT_BSS_FILTER : ALL_BSS_FILTER), 0); + ret = ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, + ALL_BSS_FILTER, 0); if (ret) { ath6kl_err("couldn't set bss filtering\n"); return ret; } } - if (request->n_ssids && request->ssids[0].ssid_len) { - u8 i; - - if (request->n_ssids > (MAX_PROBED_SSID_INDEX - 1)) - request->n_ssids = MAX_PROBED_SSID_INDEX - 1; - - for (i = 0; i < request->n_ssids; i++) - ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, - i + 1, SPECIFIC_SSID_FLAG, - request->ssids[i].ssid_len, - request->ssids[i].ssid); - } + ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, + request->n_ssids); + if (ret < 0) + return ret; /* this also clears IE in fw if it's not set */ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_PROBE_REQ, request->ie, request->ie_len); if (ret) { - ath6kl_err("failed to set Probe Request appie for " - "scan"); + ath6kl_err("failed to set Probe Request appie for scan"); return ret; } @@ -929,8 +960,7 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, channels = kzalloc(n_channels * sizeof(u16), GFP_KERNEL); if (channels == NULL) { - ath6kl_warn("failed to set scan channels, " - "scan all channels"); + ath6kl_warn("failed to set scan channels, scan all channels"); n_channels = 0; } @@ -941,6 +971,8 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, if (test_bit(CONNECTED, &vif->flags)) force_fg_scan = 1; + vif->scan_req = request; + if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, ar->fw_capabilities)) { /* @@ -963,10 +995,10 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, ATH6KL_FG_SCAN_INTERVAL, n_channels, channels); } - if (ret) + if (ret) { ath6kl_err("wmi_startscan_cmd failed\n"); - else - vif->scan_req = request; + vif->scan_req = NULL; + } kfree(channels); @@ -1000,6 +1032,20 @@ out: vif->scan_req = NULL; } +void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, + enum wmi_phy_mode mode) +{ + enum nl80211_channel_type type; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "channel switch notify nw_type %d freq %d mode %d\n", + vif->nw_type, freq, mode); + + type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT; + + cfg80211_ch_switch_notify(vif->ndev, freq, type); +} + static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_index, bool pairwise, const u8 *mac_addr, @@ -1093,9 +1139,8 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, ar->ap_mode_bkey.key_len = key->key_len; memcpy(ar->ap_mode_bkey.key, key->key, key->key_len); if (!test_bit(CONNECTED, &vif->flags)) { - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay initial group " - "key configuration until AP mode has been " - "started\n"); + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "Delay initial group key configuration until AP mode has been started\n"); /* * The key will be set in ath6kl_connect_ap_mode() once * the connected event is received from the target. @@ -1111,8 +1156,8 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, * the AP mode has properly started * (ath6kl_install_statioc_wep_keys). */ - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delay WEP key configuration " - "until AP mode has been started\n"); + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "Delay WEP key configuration until AP mode has been started\n"); vif->wep_key_list[key_index].key_len = key->key_len; memcpy(vif->wep_key_list[key_index].key, key->key, key->key_len); @@ -1436,9 +1481,38 @@ static int ath6kl_cfg80211_change_iface(struct wiphy *wiphy, struct vif_params *params) { struct ath6kl_vif *vif = netdev_priv(ndev); + int i; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: type %u\n", __func__, type); + /* + * Don't bring up p2p on an interface which is not initialized + * for p2p operation where fw does not have capability to switch + * dynamically between non-p2p and p2p type interface. + */ + if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, + vif->ar->fw_capabilities) && + (type == NL80211_IFTYPE_P2P_CLIENT || + type == NL80211_IFTYPE_P2P_GO)) { + if (vif->ar->vif_max == 1) { + if (vif->fw_vif_idx != 0) + return -EINVAL; + else + goto set_iface_type; + } + + for (i = vif->ar->max_norm_iface; i < vif->ar->vif_max; i++) { + if (i == vif->fw_vif_idx) + break; + } + + if (i == vif->ar->vif_max) { + ath6kl_err("Invalid interface to bring up P2P\n"); + return -EINVAL; + } + } + +set_iface_type: switch (type) { case NL80211_IFTYPE_STATION: vif->next_mode = INFRA_NETWORK; @@ -1915,8 +1989,7 @@ static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif) sizeof(discvr_pattern), discvr_offset, discvr_pattern, discvr_mask); if (ret) { - ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR " - "pattern\n"); + ath6kl_err("failed to add WOW mDNS/SSDP/LLMNR pattern\n"); return ret; } } @@ -1924,17 +1997,70 @@ static int ath6kl_wow_sta(struct ath6kl *ar, struct ath6kl_vif *vif) return 0; } +static int is_hsleep_mode_procsed(struct ath6kl_vif *vif) +{ + return test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); +} + +static bool is_ctrl_ep_empty(struct ath6kl *ar) +{ + return !ar->tx_pending[ar->ctrl_ep]; +} + +static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif) +{ + int ret, left; + + clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); + + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_ASLEEP); + if (ret) + return ret; + + left = wait_event_interruptible_timeout(ar->event_wq, + is_hsleep_mode_procsed(vif), + WMI_TIMEOUT); + if (left == 0) { + ath6kl_warn("timeout, didn't get host sleep cmd processed event\n"); + ret = -ETIMEDOUT; + } else if (left < 0) { + ath6kl_warn("error while waiting for host sleep cmd processed event %d\n", + left); + ret = left; + } + + if (ar->tx_pending[ar->ctrl_ep]) { + left = wait_event_interruptible_timeout(ar->event_wq, + is_ctrl_ep_empty(ar), + WMI_TIMEOUT); + if (left == 0) { + ath6kl_warn("clear wmi ctrl data timeout\n"); + ret = -ETIMEDOUT; + } else if (left < 0) { + ath6kl_warn("clear wmi ctrl data failed: %d\n", left); + ret = left; + } + } + + return ret; +} + static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) { struct in_device *in_dev; struct in_ifaddr *ifa; struct ath6kl_vif *vif; - int ret, left; + int ret; u32 filter = 0; u16 i, bmiss_time; u8 index = 0; __be32 ips[MAX_IP_ADDRS]; + /* The FW currently can't support multi-vif WoW properly. */ + if (ar->num_vif > 1) + return -EIO; + vif = ath6kl_vif_first(ar); if (!vif) return -EIO; @@ -1948,6 +2074,13 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST)) return -EINVAL; + if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags)) { + ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, + vif->fw_vif_idx, false); + if (ret) + return ret; + } + /* Clear existing WOW patterns */ for (i = 0; i < WOW_MAX_FILTERS_PER_LIST; i++) ath6kl_wmi_del_wow_pattern_cmd(ar->wmi, vif->fw_vif_idx, @@ -2030,39 +2163,11 @@ skip_arp: if (ret) return ret; - clear_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags); - - ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_ASLEEP); + ret = ath6kl_cfg80211_host_sleep(ar, vif); if (ret) return ret; - left = wait_event_interruptible_timeout(ar->event_wq, - test_bit(HOST_SLEEP_MODE_CMD_PROCESSED, &vif->flags), - WMI_TIMEOUT); - if (left == 0) { - ath6kl_warn("timeout, didn't get host sleep cmd " - "processed event\n"); - ret = -ETIMEDOUT; - } else if (left < 0) { - ath6kl_warn("error while waiting for host sleep cmd " - "processed event %d\n", left); - ret = left; - } - - if (ar->tx_pending[ar->ctrl_ep]) { - left = wait_event_interruptible_timeout(ar->event_wq, - ar->tx_pending[ar->ctrl_ep] == 0, WMI_TIMEOUT); - if (left == 0) { - ath6kl_warn("clear wmi ctrl data timeout\n"); - ret = -ETIMEDOUT; - } else if (left < 0) { - ath6kl_warn("clear wmi ctrl data failed: %d\n", left); - ret = left; - } - } - - return ret; + return 0; } static int ath6kl_wow_resume(struct ath6kl *ar) @@ -2079,8 +2184,8 @@ static int ath6kl_wow_resume(struct ath6kl *ar) ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_HOST_MODE_AWAKE); if (ret) { - ath6kl_warn("Failed to configure host sleep mode for " - "wow resume: %d\n", ret); + ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n", + ret); ar->state = ATH6KL_STATE_WOW; return ret; } @@ -2104,15 +2209,96 @@ static int ath6kl_wow_resume(struct ath6kl *ar) ar->state = ATH6KL_STATE_ON; + if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags)) { + ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, + vif->fw_vif_idx, true); + if (ret) + return ret; + } + netif_wake_queue(vif->ndev); return 0; } +static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar) +{ + struct ath6kl_vif *vif; + int ret; + + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + + if (!test_bit(WMI_READY, &ar->flag)) { + ath6kl_err("deepsleep failed as wmi is not ready\n"); + return -EIO; + } + + ath6kl_cfg80211_stop_all(ar); + + /* Save the current power mode before enabling power save */ + ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; + + ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER); + if (ret) + return ret; + + /* Disable WOW mode */ + ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_WOW_MODE_DISABLE, + 0, 0); + if (ret) + return ret; + + /* Flush all non control pkts in TX path */ + ath6kl_tx_data_cleanup(ar); + + ret = ath6kl_cfg80211_host_sleep(ar, vif); + if (ret) + return ret; + + return 0; +} + +static int ath6kl_cfg80211_deepsleep_resume(struct ath6kl *ar) +{ + struct ath6kl_vif *vif; + int ret; + + vif = ath6kl_vif_first(ar); + + if (!vif) + return -EIO; + + if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) { + ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, + ar->wmi->saved_pwr_mode); + if (ret) + return ret; + } + + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_AWAKE); + if (ret) + return ret; + + ar->state = ATH6KL_STATE_ON; + + /* Reset scan parameter to default values */ + ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, + 0, 0, 0, 0, 0, 0, 3, 0, 0, 0); + if (ret) + return ret; + + return 0; +} + int ath6kl_cfg80211_suspend(struct ath6kl *ar, enum ath6kl_cfg_suspend_mode mode, struct cfg80211_wowlan *wow) { + struct ath6kl_vif *vif; enum ath6kl_state prev_state; int ret; @@ -2137,15 +2323,12 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, case ATH6KL_CFG_SUSPEND_DEEPSLEEP: - ath6kl_cfg80211_stop_all(ar); - - /* save the current power mode before enabling power save */ - ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep suspend\n"); - ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER); + ret = ath6kl_cfg80211_deepsleep_suspend(ar); if (ret) { - ath6kl_warn("wmi powermode command failed during suspend: %d\n", - ret); + ath6kl_err("deepsleep suspend failed: %d\n", ret); + return ret; } ar->state = ATH6KL_STATE_DEEPSLEEP; @@ -2185,6 +2368,9 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, break; } + list_for_each_entry(vif, &ar->vif_list, list) + ath6kl_cfg80211_scan_complete_event(vif, true); + return 0; } EXPORT_SYMBOL(ath6kl_cfg80211_suspend); @@ -2206,17 +2392,13 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) break; case ATH6KL_STATE_DEEPSLEEP: - if (ar->wmi->pwr_mode != ar->wmi->saved_pwr_mode) { - ret = ath6kl_wmi_powermode_cmd(ar->wmi, 0, - ar->wmi->saved_pwr_mode); - if (ret) { - ath6kl_warn("wmi powermode command failed during resume: %d\n", - ret); - } - } - - ar->state = ATH6KL_STATE_ON; + ath6kl_dbg(ATH6KL_DBG_SUSPEND, "deep sleep resume\n"); + ret = ath6kl_cfg80211_deepsleep_resume(ar); + if (ret) { + ath6kl_warn("deep sleep resume failed: %d\n", ret); + return ret; + } break; case ATH6KL_STATE_CUTPOWER: @@ -2290,31 +2472,43 @@ void ath6kl_check_wow_status(struct ath6kl *ar) } #endif -static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) +static int ath6kl_set_htcap(struct ath6kl_vif *vif, enum ieee80211_band band, + bool ht_enable) { - struct ath6kl_vif *vif; + struct ath6kl_htcap *htcap = &vif->htcap; - /* - * 'dev' could be NULL if a channel change is required for the hardware - * device itself, instead of a particular VIF. - * - * FIXME: To be handled properly when monitor mode is supported. - */ - if (!dev) - return -EBUSY; + if (htcap->ht_enable == ht_enable) + return 0; - vif = netdev_priv(dev); + if (ht_enable) { + /* Set default ht capabilities */ + htcap->ht_enable = true; + htcap->cap_info = (band == IEEE80211_BAND_2GHZ) ? + ath6kl_g_htcap : ath6kl_a_htcap; + htcap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K; + } else /* Disable ht */ + memset(htcap, 0, sizeof(*htcap)); - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; + return ath6kl_wmi_set_htcap_cmd(vif->ar->wmi, vif->fw_vif_idx, + band, htcap); +} - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n", - __func__, chan->center_freq, chan->hw_value); - vif->next_chan = chan->center_freq; +static int ath6kl_restore_htcap(struct ath6kl_vif *vif) +{ + struct wiphy *wiphy = vif->ar->wiphy; + int band, ret = 0; - return 0; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!wiphy->bands[band]) + continue; + + ret = ath6kl_set_htcap(vif, band, + wiphy->bands[band]->ht_cap.ht_supported); + if (ret) + return ret; + } + + return ret; } static bool ath6kl_is_p2p_ie(const u8 *pos) @@ -2391,6 +2585,87 @@ static int ath6kl_set_ies(struct ath6kl_vif *vif, return 0; } +static int ath6kl_set_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + struct ath6kl_vif *vif; + + /* + * 'dev' could be NULL if a channel change is required for the hardware + * device itself, instead of a particular VIF. + * + * FIXME: To be handled properly when monitor mode is supported. + */ + if (!dev) + return -EBUSY; + + vif = netdev_priv(dev); + + if (!ath6kl_cfg80211_ready(vif)) + return -EIO; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: center_freq=%u hw_value=%u\n", + __func__, chan->center_freq, chan->hw_value); + vif->next_chan = chan->center_freq; + vif->next_ch_type = channel_type; + vif->next_ch_band = chan->band; + + return 0; +} + +static int ath6kl_get_rsn_capab(struct cfg80211_beacon_data *beacon, + u8 *rsn_capab) +{ + const u8 *rsn_ie; + size_t rsn_ie_len; + u16 cnt; + + if (!beacon->tail) + return -EINVAL; + + rsn_ie = cfg80211_find_ie(WLAN_EID_RSN, beacon->tail, beacon->tail_len); + if (!rsn_ie) + return -EINVAL; + + rsn_ie_len = *(rsn_ie + 1); + /* skip element id and length */ + rsn_ie += 2; + + /* skip version */ + if (rsn_ie_len < 2) + return -EINVAL; + rsn_ie += 2; + rsn_ie_len -= 2; + + /* skip group cipher suite */ + if (rsn_ie_len < 4) + return 0; + rsn_ie += 4; + rsn_ie_len -= 4; + + /* skip pairwise cipher suite */ + if (rsn_ie_len < 2) + return 0; + cnt = get_unaligned_le16(rsn_ie); + rsn_ie += (2 + cnt * 4); + rsn_ie_len -= (2 + cnt * 4); + + /* skip akm suite */ + if (rsn_ie_len < 2) + return 0; + cnt = get_unaligned_le16(rsn_ie); + rsn_ie += (2 + cnt * 4); + rsn_ie_len -= (2 + cnt * 4); + + if (rsn_ie_len < 2) + return 0; + + memcpy(rsn_capab, rsn_ie, 2); + + return 0; +} + static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_ap_settings *info) { @@ -2403,6 +2678,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, struct wmi_connect_cmd p; int res; int i, ret; + u16 rsn_capab = 0; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__); @@ -2532,6 +2808,35 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, p.nw_subtype = SUBTYPE_NONE; } + if (info->inactivity_timeout) { + res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx, + info->inactivity_timeout); + if (res < 0) + return res; + } + + if (ath6kl_set_htcap(vif, vif->next_ch_band, + vif->next_ch_type != NL80211_CHAN_NO_HT)) + return -EIO; + + /* + * Get the PTKSA replay counter in the RSN IE. Supplicant + * will use the RSN IE in M3 message and firmware has to + * advertise the same in beacon/probe response. Send + * the complete RSN IE capability field to firmware + */ + if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) && + test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, + ar->fw_capabilities)) { + res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx, + WLAN_EID_RSN, WMI_RSN_IE_CAPB, + (const u8 *) &rsn_capab, + sizeof(rsn_capab)); + if (res < 0) + return res; + } + + memcpy(&vif->profile, &p, sizeof(p)); res = ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &p); if (res < 0) return res; @@ -2566,7 +2871,8 @@ static int ath6kl_stop_ap(struct wiphy *wiphy, struct net_device *dev) ath6kl_wmi_disconnect_cmd(ar->wmi, vif->fw_vif_idx); clear_bit(CONNECTED, &vif->flags); - return 0; + /* Restore ht setting in firmware */ + return ath6kl_restore_htcap(vif); } static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -2747,6 +3053,21 @@ static bool ath6kl_mgmt_powersave_ap(struct ath6kl_vif *vif, return false; } +/* Check if SSID length is greater than DIRECT- */ +static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len) +{ + const struct ieee80211_mgmt *mgmt; + mgmt = (const struct ieee80211_mgmt *) buf; + + /* variable[1] contains the SSID tag length */ + if (buf + len >= &mgmt->u.probe_resp.variable[1] && + (mgmt->u.probe_resp.variable[1] > P2P_WILDCARD_SSID_LEN)) { + return true; + } + + return false; +} + static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, @@ -2761,11 +3082,11 @@ static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, bool more_data, queued; mgmt = (const struct ieee80211_mgmt *) buf; - if (buf + len >= mgmt->u.probe_resp.variable && - vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && - ieee80211_is_probe_resp(mgmt->frame_control)) { + if (vif->nw_type == AP_NETWORK && test_bit(CONNECTED, &vif->flags) && + ieee80211_is_probe_resp(mgmt->frame_control) && + ath6kl_is_p2p_go_ssid(buf, len)) { /* - * Send Probe Response frame in AP mode using a separate WMI + * Send Probe Response frame in GO mode using a separate WMI * command to allow the target to fill in the generic IEs. */ *cookie = 0; /* TX status not supported */ @@ -2825,7 +3146,6 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, struct ath6kl_vif *vif = netdev_priv(dev); u16 interval; int ret; - u8 i; if (ar->state != ATH6KL_STATE_ON) return -EIO; @@ -2833,27 +3153,23 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, if (vif->sme_state != SME_DISCONNECTED) return -EBUSY; - for (i = 0; i < ar->wiphy->max_sched_scan_ssids; i++) { - ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, - i, DISABLE_SSID_FLAG, - 0, NULL); - } + /* The FW currently can't support multi-vif WoW properly. */ + if (ar->num_vif > 1) + return -EIO; + + ath6kl_cfg80211_scan_complete_event(vif, true); + + ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, + request->n_ssids); + if (ret < 0) + return ret; /* fw uses seconds, also make sure that it's >0 */ interval = max_t(u16, 1, request->interval / 1000); ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, interval, interval, - 10, 0, 0, 0, 3, 0, 0, 0); - - if (request->n_ssids && request->ssids[0].ssid_len) { - for (i = 0; i < request->n_ssids; i++) { - ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, - i, SPECIFIC_SSID_FLAG, - request->ssids[i].ssid_len, - request->ssids[i].ssid); - } - } + vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0); ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, ATH6KL_WOW_MODE_ENABLE, @@ -3013,8 +3329,7 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar) ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; if (ath6kl_wmi_powermode_cmd(ar->wmi, 0, REC_POWER) != 0) - ath6kl_warn("ath6kl_deep_sleep_enable: " - "wmi_powermode_cmd failed\n"); + ath6kl_warn("ath6kl_deep_sleep_enable: wmi_powermode_cmd failed\n"); return; } @@ -3094,6 +3409,8 @@ struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, vif->next_mode = nw_type; vif->listen_intvl_t = ATH6KL_DEFAULT_LISTEN_INTVAL; vif->bmiss_time_t = ATH6KL_DEFAULT_BMISS_TIME; + vif->bg_scan_period = 0; + vif->htcap.ht_enable = true; memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); if (fw_vif_idx != 0) @@ -3134,6 +3451,7 @@ err: int ath6kl_cfg80211_init(struct ath6kl *ar) { struct wiphy *wiphy = ar->wiphy; + bool band_2gig = false, band_5gig = false, ht = false; int ret; wiphy->mgmt_stypes = ath6kl_mgmt_stypes; @@ -3154,8 +3472,46 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) /* max num of ssids that can be probed during scanning */ wiphy->max_scan_ssids = MAX_PROBED_SSID_INDEX; wiphy->max_scan_ie_len = 1000; /* FIX: what is correct limit? */ - wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; - wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; + switch (ar->hw.cap) { + case WMI_11AN_CAP: + ht = true; + case WMI_11A_CAP: + band_5gig = true; + break; + case WMI_11GN_CAP: + ht = true; + case WMI_11G_CAP: + band_2gig = true; + break; + case WMI_11AGN_CAP: + ht = true; + case WMI_11AG_CAP: + band_2gig = true; + band_5gig = true; + break; + default: + ath6kl_err("invalid phy capability!\n"); + return -EINVAL; + } + + /* + * Even if the fw has HT support, advertise HT cap only when + * the firmware has support to override RSN capability, otherwise + * 4-way handshake would fail. + */ + if (!(ht && + test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, + ar->fw_capabilities))) { + ath6kl_band_2ghz.ht_cap.cap = 0; + ath6kl_band_2ghz.ht_cap.ht_supported = false; + ath6kl_band_5ghz.ht_cap.cap = 0; + ath6kl_band_5ghz.ht_cap.ht_supported = false; + } + if (band_2gig) + wiphy->bands[IEEE80211_BAND_2GHZ] = &ath6kl_band_2ghz; + if (band_5gig) + wiphy->bands[IEEE80211_BAND_5GHZ] = &ath6kl_band_5ghz; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->cipher_suites = cipher_suites; @@ -3171,7 +3527,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) wiphy->wowlan.pattern_min_len = 1; wiphy->wowlan.pattern_max_len = WOW_PATTERN_SIZE; - wiphy->max_sched_scan_ssids = 10; + wiphy->max_sched_scan_ssids = MAX_PROBED_SSID_INDEX; ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | WIPHY_FLAG_HAVE_AP_SME | @@ -3181,11 +3537,14 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities)) ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, + ar->fw_capabilities)) + ar->wiphy->features = NL80211_FEATURE_INACTIVITY_TIMER; + ar->wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P | - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U; + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; ret = wiphy_register(wiphy); if (ret < 0) { diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index c5def436417f..5ea8cbb79f43 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h @@ -28,6 +28,8 @@ enum ath6kl_cfg_suspend_mode { struct net_device *ath6kl_interface_add(struct ath6kl *ar, char *name, enum nl80211_iftype type, u8 fw_vif_idx, u8 nw_type); +void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, + enum wmi_phy_mode mode); void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted); void ath6kl_cfg80211_connect_event(struct ath6kl_vif *vif, u16 channel, diff --git a/drivers/net/wireless/ath/ath6kl/common.h b/drivers/net/wireless/ath/ath6kl/common.h index a60e78c0472f..98a886154d9c 100644 --- a/drivers/net/wireless/ath/ath6kl/common.h +++ b/drivers/net/wireless/ath/ath6kl/common.h @@ -22,7 +22,8 @@ #define ATH6KL_MAX_IE 256 -extern int ath6kl_printk(const char *level, const char *fmt, ...); +extern __printf(2, 3) +int ath6kl_printk(const char *level, const char *fmt, ...); /* * Reflects the version of binary interface exposed by ATH6KL target @@ -77,6 +78,7 @@ enum crypto_type { struct htc_endpoint_credit_dist; struct ath6kl; +struct ath6kl_htcap; enum htc_credit_dist_reason; struct ath6kl_htc_credit_info; diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 45e641f3a41b..fdb3b1decc76 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -20,9 +20,11 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/export.h> +#include <linux/vmalloc.h> #include "debug.h" #include "hif-ops.h" +#include "htc-ops.h" #include "cfg80211.h" unsigned int debug_mask; @@ -39,12 +41,36 @@ module_param(uart_debug, uint, 0644); module_param(ath6kl_p2p, uint, 0644); module_param(testmode, uint, 0644); -int ath6kl_core_init(struct ath6kl *ar) +void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) +{ + ath6kl_htc_tx_complete(ar, skb); +} +EXPORT_SYMBOL(ath6kl_core_tx_complete); + +void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe) +{ + ath6kl_htc_rx_complete(ar, skb, pipe); +} +EXPORT_SYMBOL(ath6kl_core_rx_complete); + +int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) { struct ath6kl_bmi_target_info targ_info; struct net_device *ndev; int ret = 0, i; + switch (htc_type) { + case ATH6KL_HTC_TYPE_MBOX: + ath6kl_htc_mbox_attach(ar); + break; + case ATH6KL_HTC_TYPE_PIPE: + ath6kl_htc_pipe_attach(ar); + break; + default: + WARN_ON(1); + return -ENOMEM; + } + ar->ath6kl_wq = create_singlethread_workqueue("ath6kl"); if (!ar->ath6kl_wq) return -ENOMEM; @@ -280,7 +306,7 @@ void ath6kl_core_cleanup(struct ath6kl *ar) kfree(ar->fw_board); kfree(ar->fw_otp); - kfree(ar->fw); + vfree(ar->fw); kfree(ar->fw_patch); kfree(ar->fw_testscript); diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index f1dd8906be45..4d9c6f142698 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -91,6 +91,15 @@ enum ath6kl_fw_capability { */ ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, + /* + * Firmware has support to cleanup inactive stations + * in AP mode. + */ + ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, + + /* Firmware has support to override rsn cap of rsn ie */ + ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; @@ -117,9 +126,9 @@ struct ath6kl_fw_ie { #define AR6003_HW_2_0_FIRMWARE_FILE "athwlan.bin.z77" #define AR6003_HW_2_0_TCMD_FIRMWARE_FILE "athtcmd_ram.bin" #define AR6003_HW_2_0_PATCH_FILE "data.patch.bin" -#define AR6003_HW_2_0_BOARD_DATA_FILE "ath6k/AR6003/hw2.0/bdata.bin" +#define AR6003_HW_2_0_BOARD_DATA_FILE AR6003_HW_2_0_FW_DIR "/bdata.bin" #define AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE \ - "ath6k/AR6003/hw2.0/bdata.SD31.bin" + AR6003_HW_2_0_FW_DIR "/bdata.SD31.bin" /* AR6003 3.0 definitions */ #define AR6003_HW_2_1_1_VERSION 0x30000582 @@ -130,25 +139,33 @@ struct ath6kl_fw_ie { #define AR6003_HW_2_1_1_UTF_FIRMWARE_FILE "utf.bin" #define AR6003_HW_2_1_1_TESTSCRIPT_FILE "nullTestFlow.bin" #define AR6003_HW_2_1_1_PATCH_FILE "data.patch.bin" -#define AR6003_HW_2_1_1_BOARD_DATA_FILE "ath6k/AR6003/hw2.1.1/bdata.bin" +#define AR6003_HW_2_1_1_BOARD_DATA_FILE AR6003_HW_2_1_1_FW_DIR "/bdata.bin" #define AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE \ - "ath6k/AR6003/hw2.1.1/bdata.SD31.bin" + AR6003_HW_2_1_1_FW_DIR "/bdata.SD31.bin" /* AR6004 1.0 definitions */ #define AR6004_HW_1_0_VERSION 0x30000623 #define AR6004_HW_1_0_FW_DIR "ath6k/AR6004/hw1.0" #define AR6004_HW_1_0_FIRMWARE_FILE "fw.ram.bin" -#define AR6004_HW_1_0_BOARD_DATA_FILE "ath6k/AR6004/hw1.0/bdata.bin" +#define AR6004_HW_1_0_BOARD_DATA_FILE AR6004_HW_1_0_FW_DIR "/bdata.bin" #define AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE \ - "ath6k/AR6004/hw1.0/bdata.DB132.bin" + AR6004_HW_1_0_FW_DIR "/bdata.DB132.bin" /* AR6004 1.1 definitions */ #define AR6004_HW_1_1_VERSION 0x30000001 #define AR6004_HW_1_1_FW_DIR "ath6k/AR6004/hw1.1" #define AR6004_HW_1_1_FIRMWARE_FILE "fw.ram.bin" -#define AR6004_HW_1_1_BOARD_DATA_FILE "ath6k/AR6004/hw1.1/bdata.bin" +#define AR6004_HW_1_1_BOARD_DATA_FILE AR6004_HW_1_1_FW_DIR "/bdata.bin" #define AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE \ - "ath6k/AR6004/hw1.1/bdata.DB132.bin" + AR6004_HW_1_1_FW_DIR "/bdata.DB132.bin" + +/* AR6004 1.2 definitions */ +#define AR6004_HW_1_2_VERSION 0x300007e8 +#define AR6004_HW_1_2_FW_DIR "ath6k/AR6004/hw1.2" +#define AR6004_HW_1_2_FIRMWARE_FILE "fw.ram.bin" +#define AR6004_HW_1_2_BOARD_DATA_FILE AR6004_HW_1_2_FW_DIR "/bdata.bin" +#define AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE \ + AR6004_HW_1_2_FW_DIR "/bdata.bin" /* Per STA data, used in AP mode */ #define STA_PS_AWAKE BIT(0) @@ -205,6 +222,8 @@ struct ath6kl_fw_ie { #define ATH6KL_CONF_ENABLE_TX_BURST BIT(3) #define ATH6KL_CONF_UART_DEBUG BIT(4) +#define P2P_WILDCARD_SSID_LEN 7 /* DIRECT- */ + enum wlan_low_pwr_state { WLAN_POWER_STATE_ON, WLAN_POWER_STATE_CUT_PWR, @@ -454,6 +473,11 @@ enum ath6kl_hif_type { ATH6KL_HIF_TYPE_USB, }; +enum ath6kl_htc_type { + ATH6KL_HTC_TYPE_MBOX, + ATH6KL_HTC_TYPE_PIPE, +}; + /* Max number of filters that hw supports */ #define ATH6K_MAX_MC_FILTERS_PER_LIST 7 struct ath6kl_mc_filter { @@ -461,6 +485,12 @@ struct ath6kl_mc_filter { char hw_addr[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; }; +struct ath6kl_htcap { + bool ht_enable; + u8 ampdu_factor; + unsigned short cap_info; +}; + /* * Driver's maximum limit, note that some firmwares support only one vif * and the runtime (current) limit must be checked from ar->vif_max. @@ -480,6 +510,8 @@ enum ath6kl_vif_state { WLAN_ENABLED, STATS_UPDATE_PEND, HOST_SLEEP_MODE_CMD_PROCESSED, + NETDEV_MCAST_ALL_ON, + NETDEV_MCAST_ALL_OFF, }; struct ath6kl_vif { @@ -509,6 +541,7 @@ struct ath6kl_vif { struct ath6kl_wep_key wep_key_list[WMI_MAX_KEY_INDEX + 1]; struct ath6kl_key keys[WMI_MAX_KEY_INDEX + 1]; struct aggr_info *aggr_cntxt; + struct ath6kl_htcap htcap; struct timer_list disconnect_timer; struct timer_list sched_scan_timer; @@ -521,12 +554,16 @@ struct ath6kl_vif { u32 send_action_id; bool probe_req_report; u16 next_chan; + enum nl80211_channel_type next_ch_type; + enum ieee80211_band next_ch_band; u16 assoc_bss_beacon_int; u16 listen_intvl_t; u16 bmiss_time_t; + u16 bg_scan_period; u8 assoc_bss_dtim_period; struct net_device_stats net_stats; struct target_stats target_stats; + struct wmi_connect_cmd profile; struct list_head mc_filter; }; @@ -568,6 +605,7 @@ struct ath6kl { struct ath6kl_bmi bmi; const struct ath6kl_hif_ops *hif_ops; + const struct ath6kl_htc_ops *htc_ops; struct wmi *wmi; int tx_pending[ENDPOINT_MAX]; int total_tx_data_pend; @@ -614,6 +652,7 @@ struct ath6kl { u8 sta_list_index; struct ath6kl_req_key ap_mode_bkey; struct sk_buff_head mcastpsq; + u32 want_ch_switch; /* * FIXME: protects access to mcastpsq but is actually useless as @@ -646,6 +685,7 @@ struct ath6kl { u32 refclk_hz; u32 uarttx_pin; u32 testscript_addr; + enum wmi_phy_cap cap; struct ath6kl_hw_fw { const char *dir; @@ -746,7 +786,8 @@ void init_netdev(struct net_device *dev); void ath6kl_cookie_init(struct ath6kl *ar); void ath6kl_cookie_cleanup(struct ath6kl *ar); void ath6kl_rx(struct htc_target *target, struct htc_packet *packet); -void ath6kl_tx_complete(void *context, struct list_head *packet_queue); +void ath6kl_tx_complete(struct htc_target *context, + struct list_head *packet_queue); enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, struct htc_packet *packet); void ath6kl_stop_txrx(struct ath6kl *ar); @@ -778,7 +819,8 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn); struct ath6kl_sta *ath6kl_find_sta(struct ath6kl_vif *vif, u8 *node_addr); struct ath6kl_sta *ath6kl_find_sta_by_aid(struct ath6kl *ar, u8 aid); -void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver); +void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver, + enum wmi_phy_cap cap); int ath6kl_control_tx(void *devt, struct sk_buff *skb, enum htc_endpoint_id eid); void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, @@ -821,8 +863,11 @@ int ath6kl_init_hw_params(struct ath6kl *ar); void ath6kl_check_wow_status(struct ath6kl *ar); +void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb); +void ath6kl_core_rx_complete(struct ath6kl *ar, struct sk_buff *skb, u8 pipe); + struct ath6kl *ath6kl_core_create(struct device *dev); -int ath6kl_core_init(struct ath6kl *ar); +int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type); void ath6kl_core_cleanup(struct ath6kl *ar); void ath6kl_core_destroy(struct ath6kl *ar); diff --git a/drivers/net/wireless/ath/ath6kl/debug.c b/drivers/net/wireless/ath/ath6kl/debug.c index d01403a263ff..15cfe30e54fd 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.c +++ b/drivers/net/wireless/ath/ath6kl/debug.c @@ -401,8 +401,10 @@ static ssize_t ath6kl_fwlog_block_read(struct file *file, ret = wait_for_completion_interruptible( &ar->debug.fwlog_completion); - if (ret == -ERESTARTSYS) + if (ret == -ERESTARTSYS) { + vfree(buf); return ret; + } spin_lock(&ar->debug.fwlog_queue.lock); } @@ -616,6 +618,12 @@ static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, "Num disconnects", tgt_stats->cs_discon_cnt); len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", "Beacon avg rssi", tgt_stats->cs_ave_beacon_rssi); + len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", + "ARP pkt received", tgt_stats->arp_received); + len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", + "ARP pkt matched", tgt_stats->arp_matched); + len += scnprintf(buf + len, buf_len - len, "%20s %10d\n", + "ARP pkt replied", tgt_stats->arp_replied); if (len > buf_len) len = buf_len; @@ -1564,10 +1572,15 @@ static ssize_t ath6kl_bgscan_int_write(struct file *file, size_t count, loff_t *ppos) { struct ath6kl *ar = file->private_data; + struct ath6kl_vif *vif; u16 bgscan_int; char buf[32]; ssize_t len; + vif = ath6kl_vif_first(ar); + if (!vif) + return -EIO; + len = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, len)) return -EFAULT; @@ -1579,6 +1592,8 @@ static ssize_t ath6kl_bgscan_int_write(struct file *file, if (bgscan_int == 0) bgscan_int = 0xffff; + vif->bg_scan_period = bgscan_int; + ath6kl_wmi_scanparams_cmd(ar->wmi, 0, 0, 0, bgscan_int, 0, 0, 0, 3, 0, 0, 0); @@ -1803,6 +1818,7 @@ int ath6kl_debug_init_fs(struct ath6kl *ar) void ath6kl_debug_cleanup(struct ath6kl *ar) { skb_queue_purge(&ar->debug.fwlog_queue); + complete(&ar->debug.fwlog_completion); kfree(ar->debug.roam_tbl); } diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 1803a0baae82..49639d8266c2 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -43,6 +43,7 @@ enum ATH6K_DEBUG_MASK { ATH6KL_DBG_WMI_DUMP = BIT(19), ATH6KL_DBG_SUSPEND = BIT(20), ATH6KL_DBG_USB = BIT(21), + ATH6KL_DBG_USB_BULK = BIT(22), ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ }; diff --git a/drivers/net/wireless/ath/ath6kl/hif-ops.h b/drivers/net/wireless/ath/ath6kl/hif-ops.h index fd84086638e3..8c9e72d5250d 100644 --- a/drivers/net/wireless/ath/ath6kl/hif-ops.h +++ b/drivers/net/wireless/ath/ath6kl/hif-ops.h @@ -150,4 +150,38 @@ static inline void ath6kl_hif_stop(struct ath6kl *ar) ar->hif_ops->stop(ar); } +static inline int ath6kl_hif_pipe_send(struct ath6kl *ar, + u8 pipe, struct sk_buff *hdr_buf, + struct sk_buff *buf) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe send\n"); + + return ar->hif_ops->pipe_send(ar, pipe, hdr_buf, buf); +} + +static inline void ath6kl_hif_pipe_get_default(struct ath6kl *ar, + u8 *ul_pipe, u8 *dl_pipe) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n"); + + ar->hif_ops->pipe_get_default(ar, ul_pipe, dl_pipe); +} + +static inline int ath6kl_hif_pipe_map_service(struct ath6kl *ar, + u16 service_id, u8 *ul_pipe, + u8 *dl_pipe) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get default\n"); + + return ar->hif_ops->pipe_map_service(ar, service_id, ul_pipe, dl_pipe); +} + +static inline u16 ath6kl_hif_pipe_get_free_queue_number(struct ath6kl *ar, + u8 pipe) +{ + ath6kl_dbg(ATH6KL_DBG_HIF, "hif pipe get free queue number\n"); + + return ar->hif_ops->pipe_get_free_queue_number(ar, pipe); +} + #endif diff --git a/drivers/net/wireless/ath/ath6kl/hif.h b/drivers/net/wireless/ath/ath6kl/hif.h index 20ed6b73517b..61f6b21fb0ae 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.h +++ b/drivers/net/wireless/ath/ath6kl/hif.h @@ -256,6 +256,12 @@ struct ath6kl_hif_ops { int (*power_on)(struct ath6kl *ar); int (*power_off)(struct ath6kl *ar); void (*stop)(struct ath6kl *ar); + int (*pipe_send)(struct ath6kl *ar, u8 pipe, struct sk_buff *hdr_buf, + struct sk_buff *buf); + void (*pipe_get_default)(struct ath6kl *ar, u8 *pipe_ul, u8 *pipe_dl); + int (*pipe_map_service)(struct ath6kl *ar, u16 service_id, u8 *pipe_ul, + u8 *pipe_dl); + u16 (*pipe_get_free_queue_number)(struct ath6kl *ar, u8 pipe); }; int ath6kl_hif_setup(struct ath6kl_device *dev); diff --git a/drivers/net/wireless/ath/ath6kl/htc-ops.h b/drivers/net/wireless/ath/ath6kl/htc-ops.h new file mode 100644 index 000000000000..2d4eed55cfd1 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/htc-ops.h @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2004-2011 Atheros Communications 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. + */ + +#ifndef HTC_OPS_H +#define HTC_OPS_H + +#include "htc.h" +#include "debug.h" + +static inline void *ath6kl_htc_create(struct ath6kl *ar) +{ + return ar->htc_ops->create(ar); +} + +static inline int ath6kl_htc_wait_target(struct htc_target *target) +{ + return target->dev->ar->htc_ops->wait_target(target); +} + +static inline int ath6kl_htc_start(struct htc_target *target) +{ + return target->dev->ar->htc_ops->start(target); +} + +static inline int ath6kl_htc_conn_service(struct htc_target *target, + struct htc_service_connect_req *req, + struct htc_service_connect_resp *resp) +{ + return target->dev->ar->htc_ops->conn_service(target, req, resp); +} + +static inline int ath6kl_htc_tx(struct htc_target *target, + struct htc_packet *packet) +{ + return target->dev->ar->htc_ops->tx(target, packet); +} + +static inline void ath6kl_htc_stop(struct htc_target *target) +{ + return target->dev->ar->htc_ops->stop(target); +} + +static inline void ath6kl_htc_cleanup(struct htc_target *target) +{ + return target->dev->ar->htc_ops->cleanup(target); +} + +static inline void ath6kl_htc_flush_txep(struct htc_target *target, + enum htc_endpoint_id endpoint, + u16 tag) +{ + return target->dev->ar->htc_ops->flush_txep(target, endpoint, tag); +} + +static inline void ath6kl_htc_flush_rx_buf(struct htc_target *target) +{ + return target->dev->ar->htc_ops->flush_rx_buf(target); +} + +static inline void ath6kl_htc_activity_changed(struct htc_target *target, + enum htc_endpoint_id endpoint, + bool active) +{ + return target->dev->ar->htc_ops->activity_changed(target, endpoint, + active); +} + +static inline int ath6kl_htc_get_rxbuf_num(struct htc_target *target, + enum htc_endpoint_id endpoint) +{ + return target->dev->ar->htc_ops->get_rxbuf_num(target, endpoint); +} + +static inline int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, + struct list_head *pktq) +{ + return target->dev->ar->htc_ops->add_rxbuf_multiple(target, pktq); +} + +static inline int ath6kl_htc_credit_setup(struct htc_target *target, + struct ath6kl_htc_credit_info *info) +{ + return target->dev->ar->htc_ops->credit_setup(target, info); +} + +static inline void ath6kl_htc_tx_complete(struct ath6kl *ar, + struct sk_buff *skb) +{ + ar->htc_ops->tx_complete(ar, skb); +} + + +static inline void ath6kl_htc_rx_complete(struct ath6kl *ar, + struct sk_buff *skb, u8 pipe) +{ + ar->htc_ops->rx_complete(ar, skb, pipe); +} + + +#endif diff --git a/drivers/net/wireless/ath/ath6kl/htc.h b/drivers/net/wireless/ath/ath6kl/htc.h index 5027ccc36b62..a2c8ff809793 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.h +++ b/drivers/net/wireless/ath/ath6kl/htc.h @@ -25,6 +25,7 @@ /* send direction */ #define HTC_FLAGS_NEED_CREDIT_UPDATE (1 << 0) #define HTC_FLAGS_SEND_BUNDLE (1 << 1) +#define HTC_FLAGS_TX_FIXUP_NETBUF (1 << 2) /* receive direction */ #define HTC_FLG_RX_UNUSED (1 << 0) @@ -56,6 +57,10 @@ #define HTC_CONN_FLGS_THRESH_LVL_THREE_QUAT 0x2 #define HTC_CONN_FLGS_REDUCE_CRED_DRIB 0x4 #define HTC_CONN_FLGS_THRESH_MASK 0x3 +/* disable credit flow control on a specific service */ +#define HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL (1 << 3) +#define HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT 8 +#define HTC_CONN_FLGS_SET_RECV_ALLOC_MASK 0xFF00 /* connect response status codes */ #define HTC_SERVICE_SUCCESS 0 @@ -75,6 +80,7 @@ #define HTC_RECORD_LOOKAHEAD_BUNDLE 3 #define HTC_SETUP_COMP_FLG_RX_BNDL_EN (1 << 0) +#define HTC_SETUP_COMP_FLG_DISABLE_TX_CREDIT_FLOW (1 << 1) #define MAKE_SERVICE_ID(group, index) \ (int)(((int)group << 8) | (int)(index)) @@ -109,6 +115,8 @@ /* HTC operational parameters */ #define HTC_TARGET_RESPONSE_TIMEOUT 2000 /* in ms */ +#define HTC_TARGET_RESPONSE_POLL_WAIT 10 +#define HTC_TARGET_RESPONSE_POLL_COUNT 200 #define HTC_TARGET_DEBUG_INTR_MASK 0x01 #define HTC_TARGET_CREDIT_INTR_MASK 0xF0 @@ -128,6 +136,7 @@ #define HTC_RECV_WAIT_BUFFERS (1 << 0) #define HTC_OP_STATE_STOPPING (1 << 0) +#define HTC_OP_STATE_SETUP_COMPLETE (1 << 1) /* * The frame header length and message formats defined herein were selected @@ -311,6 +320,14 @@ struct htc_packet { void (*completion) (struct htc_target *, struct htc_packet *); struct htc_target *context; + + /* + * optimization for network-oriented data, the HTC packet + * can pass the network buffer corresponding to the HTC packet + * lower layers may optimized the transfer knowing this is + * a network buffer + */ + struct sk_buff *skb; }; enum htc_send_full_action { @@ -319,12 +336,14 @@ enum htc_send_full_action { }; struct htc_ep_callbacks { + void (*tx_complete) (struct htc_target *, struct htc_packet *); void (*rx) (struct htc_target *, struct htc_packet *); void (*rx_refill) (struct htc_target *, enum htc_endpoint_id endpoint); enum htc_send_full_action (*tx_full) (struct htc_target *, struct htc_packet *); struct htc_packet *(*rx_allocthresh) (struct htc_target *, enum htc_endpoint_id, int); + void (*tx_comp_multi) (struct htc_target *, struct list_head *); int rx_alloc_thresh; int rx_refill_thresh; }; @@ -502,6 +521,13 @@ struct htc_endpoint { u32 conn_flags; struct htc_endpoint_stats ep_st; u16 tx_drop_packet_threshold; + + struct { + u8 pipeid_ul; + u8 pipeid_dl; + struct list_head tx_lookup_queue; + bool tx_credit_flow_enabled; + } pipe; }; struct htc_control_buffer { @@ -509,6 +535,42 @@ struct htc_control_buffer { u8 *buf; }; +struct htc_pipe_txcredit_alloc { + u16 service_id; + u8 credit_alloc; +}; + +enum htc_send_queue_result { + HTC_SEND_QUEUE_OK = 0, /* packet was queued */ + HTC_SEND_QUEUE_DROP = 1, /* this packet should be dropped */ +}; + +struct ath6kl_htc_ops { + void* (*create)(struct ath6kl *ar); + int (*wait_target)(struct htc_target *target); + int (*start)(struct htc_target *target); + int (*conn_service)(struct htc_target *target, + struct htc_service_connect_req *req, + struct htc_service_connect_resp *resp); + int (*tx)(struct htc_target *target, struct htc_packet *packet); + void (*stop)(struct htc_target *target); + void (*cleanup)(struct htc_target *target); + void (*flush_txep)(struct htc_target *target, + enum htc_endpoint_id endpoint, u16 tag); + void (*flush_rx_buf)(struct htc_target *target); + void (*activity_changed)(struct htc_target *target, + enum htc_endpoint_id endpoint, + bool active); + int (*get_rxbuf_num)(struct htc_target *target, + enum htc_endpoint_id endpoint); + int (*add_rxbuf_multiple)(struct htc_target *target, + struct list_head *pktq); + int (*credit_setup)(struct htc_target *target, + struct ath6kl_htc_credit_info *cred_info); + int (*tx_complete)(struct ath6kl *ar, struct sk_buff *skb); + int (*rx_complete)(struct ath6kl *ar, struct sk_buff *skb, u8 pipe); +}; + struct ath6kl_device; /* our HTC target state */ @@ -557,36 +619,19 @@ struct htc_target { /* counts the number of Tx without bundling continously per AC */ u32 ac_tx_count[WMM_NUM_AC]; + + struct { + struct htc_packet *htc_packet_pool; + u8 ctrl_response_buf[HTC_MAX_CTRL_MSG_LEN]; + int ctrl_response_len; + bool ctrl_response_valid; + struct htc_pipe_txcredit_alloc txcredit_alloc[ENDPOINT_MAX]; + } pipe; }; -void *ath6kl_htc_create(struct ath6kl *ar); -void ath6kl_htc_set_credit_dist(struct htc_target *target, - struct ath6kl_htc_credit_info *cred_info, - u16 svc_pri_order[], int len); -int ath6kl_htc_wait_target(struct htc_target *target); -int ath6kl_htc_start(struct htc_target *target); -int ath6kl_htc_conn_service(struct htc_target *target, - struct htc_service_connect_req *req, - struct htc_service_connect_resp *resp); -int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet); -void ath6kl_htc_stop(struct htc_target *target); -void ath6kl_htc_cleanup(struct htc_target *target); -void ath6kl_htc_flush_txep(struct htc_target *target, - enum htc_endpoint_id endpoint, u16 tag); -void ath6kl_htc_flush_rx_buf(struct htc_target *target); -void ath6kl_htc_indicate_activity_change(struct htc_target *target, - enum htc_endpoint_id endpoint, - bool active); -int ath6kl_htc_get_rxbuf_num(struct htc_target *target, - enum htc_endpoint_id endpoint); -int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, - struct list_head *pktq); int ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, u32 msg_look_ahead, int *n_pkts); -int ath6kl_credit_setup(void *htc_handle, - struct ath6kl_htc_credit_info *cred_info); - static inline void set_htc_pkt_info(struct htc_packet *packet, void *context, u8 *buf, unsigned int len, enum htc_endpoint_id eid, u16 tag) @@ -626,4 +671,7 @@ static inline int get_queue_depth(struct list_head *queue) return depth; } +void ath6kl_htc_pipe_attach(struct ath6kl *ar); +void ath6kl_htc_mbox_attach(struct ath6kl *ar); + #endif diff --git a/drivers/net/wireless/ath/ath6kl/htc.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index 4849d99cce77..2798624d3a9d 100644 --- a/drivers/net/wireless/ath/ath6kl/htc.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c @@ -23,6 +23,14 @@ #define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) +static void ath6kl_htc_mbox_cleanup(struct htc_target *target); +static void ath6kl_htc_mbox_stop(struct htc_target *target); +static int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target, + struct list_head *pkt_queue); +static void ath6kl_htc_set_credit_dist(struct htc_target *target, + struct ath6kl_htc_credit_info *cred_info, + u16 svc_pri_order[], int len); + /* threshold to re-enable Tx bundling for an AC*/ #define TX_RESUME_BUNDLE_THRESHOLD 1500 @@ -75,10 +83,7 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, * never goes inactive EVER. */ cur_ep_dist->dist_flags |= HTC_EP_ACTIVE; - } else if (cur_ep_dist->svc_id == WMI_DATA_BK_SVC) - /* this is the lowest priority data endpoint */ - /* FIXME: this looks fishy, check */ - cred_info->lowestpri_ep_dist = cur_ep_dist->list; + } /* * Streams have to be created (explicit | implicit) for all @@ -92,6 +97,13 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, */ } + /* + * For ath6kl_credit_seek function, + * it use list_for_each_entry_reverse to walk around the whole ep list. + * Therefore assign this lowestpri_ep_dist after walk around the ep_list + */ + cred_info->lowestpri_ep_dist = cur_ep_dist->list; + WARN_ON(cred_info->cur_free_credits <= 0); list_for_each_entry(cur_ep_dist, ep_list, list) { @@ -130,8 +142,8 @@ static void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, } /* initialize and setup credit distribution */ -int ath6kl_credit_setup(void *htc_handle, - struct ath6kl_htc_credit_info *cred_info) +static int ath6kl_htc_mbox_credit_setup(struct htc_target *htc_target, + struct ath6kl_htc_credit_info *cred_info) { u16 servicepriority[5]; @@ -144,7 +156,7 @@ int ath6kl_credit_setup(void *htc_handle, servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */ /* set priority list */ - ath6kl_htc_set_credit_dist(htc_handle, cred_info, servicepriority, 5); + ath6kl_htc_set_credit_dist(htc_target, cred_info, servicepriority, 5); return 0; } @@ -432,7 +444,7 @@ static void htc_tx_complete(struct htc_endpoint *endpoint, "htc tx complete ep %d pkts %d\n", endpoint->eid, get_queue_depth(txq)); - ath6kl_tx_complete(endpoint->target->dev->ar, txq); + ath6kl_tx_complete(endpoint->target, txq); } static void htc_tx_comp_handler(struct htc_target *target, @@ -750,7 +762,7 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint, u32 txb_mask; u8 ac = WMM_NUM_AC; - if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) || + if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) && (WMI_CONTROL_SVC != endpoint->svc_id)) ac = target->dev->ar->ep2ac_map[endpoint->eid]; @@ -785,16 +797,17 @@ static void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint, * itself */ txb_mask = ((1 << ac) - 1); - /* - * when the scatter request resources drop below a - * certain threshold, disable Tx bundling for all - * AC's with priority lower than the current requesting - * AC. Otherwise re-enable Tx bundling for them - */ - if (scat_req->scat_q_depth < ATH6KL_SCATTER_REQS) - target->tx_bndl_mask &= ~txb_mask; - else - target->tx_bndl_mask |= txb_mask; + + /* + * when the scatter request resources drop below a + * certain threshold, disable Tx bundling for all + * AC's with priority lower than the current requesting + * AC. Otherwise re-enable Tx bundling for them + */ + if (scat_req->scat_q_depth < ATH6KL_SCATTER_REQS) + target->tx_bndl_mask &= ~txb_mask; + else + target->tx_bndl_mask |= txb_mask; } ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx pkts to scatter: %d\n", @@ -841,6 +854,7 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, int bundle_sent; int n_pkts_bundle; u8 ac = WMM_NUM_AC; + int status; spin_lock_bh(&target->tx_lock); @@ -858,7 +872,7 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, */ INIT_LIST_HEAD(&txq); - if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) || + if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) && (WMI_CONTROL_SVC != endpoint->svc_id)) ac = target->dev->ar->ep2ac_map[endpoint->eid]; @@ -902,7 +916,12 @@ static void ath6kl_htc_tx_from_queue(struct htc_target *target, ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags, 0, packet->info.tx.seqno); - ath6kl_htc_tx_issue(target, packet); + status = ath6kl_htc_tx_issue(target, packet); + + if (status) { + packet->status = status; + packet->completion(packet->context, packet); + } } spin_lock_bh(&target->tx_lock); @@ -1065,7 +1084,7 @@ static int htc_setup_tx_complete(struct htc_target *target) return status; } -void ath6kl_htc_set_credit_dist(struct htc_target *target, +static void ath6kl_htc_set_credit_dist(struct htc_target *target, struct ath6kl_htc_credit_info *credit_info, u16 srvc_pri_order[], int list_len) { @@ -1093,7 +1112,8 @@ void ath6kl_htc_set_credit_dist(struct htc_target *target, } } -int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet) +static int ath6kl_htc_mbox_tx(struct htc_target *target, + struct htc_packet *packet) { struct htc_endpoint *endpoint; struct list_head queue; @@ -1121,7 +1141,7 @@ int ath6kl_htc_tx(struct htc_target *target, struct htc_packet *packet) } /* flush endpoint TX queue */ -void ath6kl_htc_flush_txep(struct htc_target *target, +static void ath6kl_htc_mbox_flush_txep(struct htc_target *target, enum htc_endpoint_id eid, u16 tag) { struct htc_packet *packet, *tmp_pkt; @@ -1173,12 +1193,13 @@ static void ath6kl_htc_flush_txep_all(struct htc_target *target) if (endpoint->svc_id == 0) /* not in use.. */ continue; - ath6kl_htc_flush_txep(target, i, HTC_TX_PACKET_TAG_ALL); + ath6kl_htc_mbox_flush_txep(target, i, HTC_TX_PACKET_TAG_ALL); } } -void ath6kl_htc_indicate_activity_change(struct htc_target *target, - enum htc_endpoint_id eid, bool active) +static void ath6kl_htc_mbox_activity_changed(struct htc_target *target, + enum htc_endpoint_id eid, + bool active) { struct htc_endpoint *endpoint = &target->endpoint[eid]; bool dist = false; @@ -1246,7 +1267,7 @@ static int htc_add_rxbuf(struct htc_target *target, struct htc_packet *packet) INIT_LIST_HEAD(&queue); list_add_tail(&packet->list, &queue); - return ath6kl_htc_add_rxbuf_multiple(target, &queue); + return ath6kl_htc_mbox_add_rxbuf_multiple(target, &queue); } static void htc_reclaim_rxbuf(struct htc_target *target, @@ -1353,7 +1374,9 @@ static int ath6kl_htc_rx_setup(struct htc_target *target, sizeof(*htc_hdr)); if (!htc_valid_rx_frame_len(target, ep->eid, full_len)) { - ath6kl_warn("Rx buffer requested with invalid length\n"); + ath6kl_warn("Rx buffer requested with invalid length htc_hdr:eid %d, flags 0x%x, len %d\n", + htc_hdr->eid, htc_hdr->flags, + le16_to_cpu(htc_hdr->payld_len)); return -EINVAL; } @@ -2288,7 +2311,7 @@ fail_ctrl_rx: return NULL; } -int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, +static int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target, struct list_head *pkt_queue) { struct htc_endpoint *endpoint; @@ -2350,7 +2373,7 @@ int ath6kl_htc_add_rxbuf_multiple(struct htc_target *target, return status; } -void ath6kl_htc_flush_rx_buf(struct htc_target *target) +static void ath6kl_htc_mbox_flush_rx_buf(struct htc_target *target) { struct htc_endpoint *endpoint; struct htc_packet *packet, *tmp_pkt; @@ -2392,7 +2415,7 @@ void ath6kl_htc_flush_rx_buf(struct htc_target *target) } } -int ath6kl_htc_conn_service(struct htc_target *target, +static int ath6kl_htc_mbox_conn_service(struct htc_target *target, struct htc_service_connect_req *conn_req, struct htc_service_connect_resp *conn_resp) { @@ -2564,7 +2587,7 @@ static void reset_ep_state(struct htc_target *target) INIT_LIST_HEAD(&target->cred_dist_list); } -int ath6kl_htc_get_rxbuf_num(struct htc_target *target, +static int ath6kl_htc_mbox_get_rxbuf_num(struct htc_target *target, enum htc_endpoint_id endpoint) { int num; @@ -2624,7 +2647,7 @@ static void htc_setup_msg_bndl(struct htc_target *target) } } -int ath6kl_htc_wait_target(struct htc_target *target) +static int ath6kl_htc_mbox_wait_target(struct htc_target *target) { struct htc_packet *packet = NULL; struct htc_ready_ext_msg *rdy_msg; @@ -2693,12 +2716,12 @@ int ath6kl_htc_wait_target(struct htc_target *target) connect.svc_id = HTC_CTRL_RSVD_SVC; /* connect fake service */ - status = ath6kl_htc_conn_service((void *)target, &connect, &resp); + status = ath6kl_htc_mbox_conn_service((void *)target, &connect, &resp); if (status) /* * FIXME: this call doesn't make sense, the caller should - * call ath6kl_htc_cleanup() when it wants remove htc + * call ath6kl_htc_mbox_cleanup() when it wants remove htc */ ath6kl_hif_cleanup_scatter(target->dev->ar); @@ -2715,7 +2738,7 @@ fail_wait_target: * Start HTC, enable interrupts and let the target know * host has finished setup. */ -int ath6kl_htc_start(struct htc_target *target) +static int ath6kl_htc_mbox_start(struct htc_target *target) { struct htc_packet *packet; int status; @@ -2752,7 +2775,7 @@ int ath6kl_htc_start(struct htc_target *target) status = ath6kl_hif_unmask_intrs(target->dev); if (status) - ath6kl_htc_stop(target); + ath6kl_htc_mbox_stop(target); return status; } @@ -2796,7 +2819,7 @@ static int ath6kl_htc_reset(struct htc_target *target) } /* htc_stop: stop interrupt reception, and flush all queued buffers */ -void ath6kl_htc_stop(struct htc_target *target) +static void ath6kl_htc_mbox_stop(struct htc_target *target) { spin_lock_bh(&target->htc_lock); target->htc_flags |= HTC_OP_STATE_STOPPING; @@ -2811,12 +2834,12 @@ void ath6kl_htc_stop(struct htc_target *target) ath6kl_htc_flush_txep_all(target); - ath6kl_htc_flush_rx_buf(target); + ath6kl_htc_mbox_flush_rx_buf(target); ath6kl_htc_reset(target); } -void *ath6kl_htc_create(struct ath6kl *ar) +static void *ath6kl_htc_mbox_create(struct ath6kl *ar) { struct htc_target *target = NULL; int status = 0; @@ -2857,13 +2880,13 @@ void *ath6kl_htc_create(struct ath6kl *ar) return target; err_htc_cleanup: - ath6kl_htc_cleanup(target); + ath6kl_htc_mbox_cleanup(target); return NULL; } /* cleanup the HTC instance */ -void ath6kl_htc_cleanup(struct htc_target *target) +static void ath6kl_htc_mbox_cleanup(struct htc_target *target) { struct htc_packet *packet, *tmp_packet; @@ -2888,3 +2911,24 @@ void ath6kl_htc_cleanup(struct htc_target *target) kfree(target->dev); kfree(target); } + +static const struct ath6kl_htc_ops ath6kl_htc_mbox_ops = { + .create = ath6kl_htc_mbox_create, + .wait_target = ath6kl_htc_mbox_wait_target, + .start = ath6kl_htc_mbox_start, + .conn_service = ath6kl_htc_mbox_conn_service, + .tx = ath6kl_htc_mbox_tx, + .stop = ath6kl_htc_mbox_stop, + .cleanup = ath6kl_htc_mbox_cleanup, + .flush_txep = ath6kl_htc_mbox_flush_txep, + .flush_rx_buf = ath6kl_htc_mbox_flush_rx_buf, + .activity_changed = ath6kl_htc_mbox_activity_changed, + .get_rxbuf_num = ath6kl_htc_mbox_get_rxbuf_num, + .add_rxbuf_multiple = ath6kl_htc_mbox_add_rxbuf_multiple, + .credit_setup = ath6kl_htc_mbox_credit_setup, +}; + +void ath6kl_htc_mbox_attach(struct ath6kl *ar) +{ + ar->htc_ops = &ath6kl_htc_mbox_ops; +} diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c new file mode 100644 index 000000000000..f9626c723693 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c @@ -0,0 +1,1708 @@ +/* + * Copyright (c) 2007-2011 Atheros Communications 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. + */ + +#include "core.h" +#include "debug.h" +#include "hif-ops.h" + +#define HTC_PACKET_CONTAINER_ALLOCATION 32 +#define HTC_CONTROL_BUFFER_SIZE (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH) + +static int ath6kl_htc_pipe_tx(struct htc_target *handle, + struct htc_packet *packet); +static void ath6kl_htc_pipe_cleanup(struct htc_target *handle); + +/* htc pipe tx path */ +static inline void restore_tx_packet(struct htc_packet *packet) +{ + if (packet->info.tx.flags & HTC_FLAGS_TX_FIXUP_NETBUF) { + skb_pull(packet->skb, sizeof(struct htc_frame_hdr)); + packet->info.tx.flags &= ~HTC_FLAGS_TX_FIXUP_NETBUF; + } +} + +static void do_send_completion(struct htc_endpoint *ep, + struct list_head *queue_to_indicate) +{ + struct htc_packet *packet; + + if (list_empty(queue_to_indicate)) { + /* nothing to indicate */ + return; + } + + if (ep->ep_cb.tx_comp_multi != NULL) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: calling ep %d, send complete multiple callback (%d pkts)\n", + __func__, ep->eid, + get_queue_depth(queue_to_indicate)); + /* + * a multiple send complete handler is being used, + * pass the queue to the handler + */ + ep->ep_cb.tx_comp_multi(ep->target, queue_to_indicate); + /* + * all packets are now owned by the callback, + * reset queue to be safe + */ + INIT_LIST_HEAD(queue_to_indicate); + } else { + /* using legacy EpTxComplete */ + do { + packet = list_first_entry(queue_to_indicate, + struct htc_packet, list); + + list_del(&packet->list); + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: calling ep %d send complete callback on packet 0x%p\n", + __func__, ep->eid, packet); + ep->ep_cb.tx_complete(ep->target, packet); + } while (!list_empty(queue_to_indicate)); + } +} + +static void send_packet_completion(struct htc_target *target, + struct htc_packet *packet) +{ + struct htc_endpoint *ep = &target->endpoint[packet->endpoint]; + struct list_head container; + + restore_tx_packet(packet); + INIT_LIST_HEAD(&container); + list_add_tail(&packet->list, &container); + + /* do completion */ + do_send_completion(ep, &container); +} + +static void get_htc_packet_credit_based(struct htc_target *target, + struct htc_endpoint *ep, + struct list_head *queue) +{ + int credits_required; + int remainder; + u8 send_flags; + struct htc_packet *packet; + unsigned int transfer_len; + + /* NOTE : the TX lock is held when this function is called */ + + /* loop until we can grab as many packets out of the queue as we can */ + while (true) { + send_flags = 0; + if (list_empty(&ep->txq)) + break; + + /* get packet at head, but don't remove it */ + packet = list_first_entry(&ep->txq, struct htc_packet, list); + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: got head packet:0x%p , queue depth: %d\n", + __func__, packet, get_queue_depth(&ep->txq)); + + transfer_len = packet->act_len + HTC_HDR_LENGTH; + + if (transfer_len <= target->tgt_cred_sz) { + credits_required = 1; + } else { + /* figure out how many credits this message requires */ + credits_required = transfer_len / target->tgt_cred_sz; + remainder = transfer_len % target->tgt_cred_sz; + + if (remainder) + credits_required++; + } + + ath6kl_dbg(ATH6KL_DBG_HTC, "%s: creds required:%d got:%d\n", + __func__, credits_required, ep->cred_dist.credits); + + if (ep->eid == ENDPOINT_0) { + /* + * endpoint 0 is special, it always has a credit and + * does not require credit based flow control + */ + credits_required = 0; + + } else { + + if (ep->cred_dist.credits < credits_required) + break; + + ep->cred_dist.credits -= credits_required; + ep->ep_st.cred_cosumd += credits_required; + + /* check if we need credits back from the target */ + if (ep->cred_dist.credits < + ep->cred_dist.cred_per_msg) { + /* tell the target we need credits ASAP! */ + send_flags |= HTC_FLAGS_NEED_CREDIT_UPDATE; + ep->ep_st.cred_low_indicate += 1; + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: host needs credits\n", + __func__); + } + } + + /* now we can fully dequeue */ + packet = list_first_entry(&ep->txq, struct htc_packet, list); + + list_del(&packet->list); + /* save the number of credits this packet consumed */ + packet->info.tx.cred_used = credits_required; + /* save send flags */ + packet->info.tx.flags = send_flags; + packet->info.tx.seqno = ep->seqno; + ep->seqno++; + /* queue this packet into the caller's queue */ + list_add_tail(&packet->list, queue); + } + +} + +static void get_htc_packet(struct htc_target *target, + struct htc_endpoint *ep, + struct list_head *queue, int resources) +{ + struct htc_packet *packet; + + /* NOTE : the TX lock is held when this function is called */ + + /* loop until we can grab as many packets out of the queue as we can */ + while (resources) { + if (list_empty(&ep->txq)) + break; + + packet = list_first_entry(&ep->txq, struct htc_packet, list); + list_del(&packet->list); + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: got packet:0x%p , new queue depth: %d\n", + __func__, packet, get_queue_depth(&ep->txq)); + packet->info.tx.seqno = ep->seqno; + packet->info.tx.flags = 0; + packet->info.tx.cred_used = 0; + ep->seqno++; + + /* queue this packet into the caller's queue */ + list_add_tail(&packet->list, queue); + resources--; + } +} + +static int htc_issue_packets(struct htc_target *target, + struct htc_endpoint *ep, + struct list_head *pkt_queue) +{ + int status = 0; + u16 payload_len; + struct sk_buff *skb; + struct htc_frame_hdr *htc_hdr; + struct htc_packet *packet; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: queue: 0x%p, pkts %d\n", __func__, + pkt_queue, get_queue_depth(pkt_queue)); + + while (!list_empty(pkt_queue)) { + packet = list_first_entry(pkt_queue, struct htc_packet, list); + list_del(&packet->list); + + skb = packet->skb; + if (!skb) { + WARN_ON_ONCE(1); + status = -EINVAL; + break; + } + + payload_len = packet->act_len; + + /* setup HTC frame header */ + htc_hdr = (struct htc_frame_hdr *) skb_push(skb, + sizeof(*htc_hdr)); + if (!htc_hdr) { + WARN_ON_ONCE(1); + status = -EINVAL; + break; + } + + packet->info.tx.flags |= HTC_FLAGS_TX_FIXUP_NETBUF; + + /* Endianess? */ + put_unaligned((u16) payload_len, &htc_hdr->payld_len); + htc_hdr->flags = packet->info.tx.flags; + htc_hdr->eid = (u8) packet->endpoint; + htc_hdr->ctrl[0] = 0; + htc_hdr->ctrl[1] = (u8) packet->info.tx.seqno; + + spin_lock_bh(&target->tx_lock); + + /* store in look up queue to match completions */ + list_add_tail(&packet->list, &ep->pipe.tx_lookup_queue); + ep->ep_st.tx_issued += 1; + spin_unlock_bh(&target->tx_lock); + + status = ath6kl_hif_pipe_send(target->dev->ar, + ep->pipe.pipeid_ul, NULL, skb); + + if (status != 0) { + if (status != -ENOMEM) { + /* TODO: if more than 1 endpoint maps to the + * same PipeID, it is possible to run out of + * resources in the HIF layer. + * Don't emit the error + */ + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: failed status:%d\n", + __func__, status); + } + spin_lock_bh(&target->tx_lock); + list_del(&packet->list); + + /* reclaim credits */ + ep->cred_dist.credits += packet->info.tx.cred_used; + spin_unlock_bh(&target->tx_lock); + + /* put it back into the callers queue */ + list_add(&packet->list, pkt_queue); + break; + } + + } + + if (status != 0) { + while (!list_empty(pkt_queue)) { + if (status != -ENOMEM) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: failed pkt:0x%p status:%d\n", + __func__, packet, status); + } + + packet = list_first_entry(pkt_queue, + struct htc_packet, list); + list_del(&packet->list); + packet->status = status; + send_packet_completion(target, packet); + } + } + + return status; +} + +static enum htc_send_queue_result htc_try_send(struct htc_target *target, + struct htc_endpoint *ep, + struct list_head *txq) +{ + struct list_head send_queue; /* temp queue to hold packets */ + struct htc_packet *packet, *tmp_pkt; + struct ath6kl *ar = target->dev->ar; + enum htc_send_full_action action; + int tx_resources, overflow, txqueue_depth, i, good_pkts; + u8 pipeid; + + ath6kl_dbg(ATH6KL_DBG_HTC, "%s: (queue:0x%p depth:%d)\n", + __func__, txq, + (txq == NULL) ? 0 : get_queue_depth(txq)); + + /* init the local send queue */ + INIT_LIST_HEAD(&send_queue); + + /* + * txq equals to NULL means + * caller didn't provide a queue, just wants us to + * check queues and send + */ + if (txq != NULL) { + if (list_empty(txq)) { + /* empty queue */ + return HTC_SEND_QUEUE_DROP; + } + + spin_lock_bh(&target->tx_lock); + txqueue_depth = get_queue_depth(&ep->txq); + spin_unlock_bh(&target->tx_lock); + + if (txqueue_depth >= ep->max_txq_depth) { + /* we've already overflowed */ + overflow = get_queue_depth(txq); + } else { + /* get how much we will overflow by */ + overflow = txqueue_depth; + overflow += get_queue_depth(txq); + /* get how much we will overflow the TX queue by */ + overflow -= ep->max_txq_depth; + } + + /* if overflow is negative or zero, we are okay */ + if (overflow > 0) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: Endpoint %d, TX queue will overflow :%d, Tx Depth:%d, Max:%d\n", + __func__, ep->eid, overflow, txqueue_depth, + ep->max_txq_depth); + } + if ((overflow <= 0) || + (ep->ep_cb.tx_full == NULL)) { + /* + * all packets will fit or caller did not provide send + * full indication handler -- just move all of them + * to the local send_queue object + */ + list_splice_tail_init(txq, &send_queue); + } else { + good_pkts = get_queue_depth(txq) - overflow; + if (good_pkts < 0) { + WARN_ON_ONCE(1); + return HTC_SEND_QUEUE_DROP; + } + + /* we have overflowed, and a callback is provided */ + /* dequeue all non-overflow packets to the sendqueue */ + for (i = 0; i < good_pkts; i++) { + /* pop off caller's queue */ + packet = list_first_entry(txq, + struct htc_packet, + list); + list_del(&packet->list); + /* insert into local queue */ + list_add_tail(&packet->list, &send_queue); + } + + /* + * the caller's queue has all the packets that won't fit + * walk through the caller's queue and indicate each to + * the send full handler + */ + list_for_each_entry_safe(packet, tmp_pkt, + txq, list) { + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: Indicat overflowed TX pkts: %p\n", + __func__, packet); + action = ep->ep_cb.tx_full(ep->target, packet); + if (action == HTC_SEND_FULL_DROP) { + /* callback wants the packet dropped */ + ep->ep_st.tx_dropped += 1; + + /* leave this one in the caller's queue + * for cleanup */ + } else { + /* callback wants to keep this packet, + * remove from caller's queue */ + list_del(&packet->list); + /* put it in the send queue */ + list_add_tail(&packet->list, + &send_queue); + } + + } + + if (list_empty(&send_queue)) { + /* no packets made it in, caller will cleanup */ + return HTC_SEND_QUEUE_DROP; + } + } + } + + if (!ep->pipe.tx_credit_flow_enabled) { + tx_resources = + ath6kl_hif_pipe_get_free_queue_number(ar, + ep->pipe.pipeid_ul); + } else { + tx_resources = 0; + } + + spin_lock_bh(&target->tx_lock); + if (!list_empty(&send_queue)) { + /* transfer packets to tail */ + list_splice_tail_init(&send_queue, &ep->txq); + if (!list_empty(&send_queue)) { + WARN_ON_ONCE(1); + spin_unlock_bh(&target->tx_lock); + return HTC_SEND_QUEUE_DROP; + } + INIT_LIST_HEAD(&send_queue); + } + + /* increment tx processing count on entry */ + ep->tx_proc_cnt++; + + if (ep->tx_proc_cnt > 1) { + /* + * Another thread or task is draining the TX queues on this + * endpoint that thread will reset the tx processing count + * when the queue is drained. + */ + ep->tx_proc_cnt--; + spin_unlock_bh(&target->tx_lock); + return HTC_SEND_QUEUE_OK; + } + + /***** beyond this point only 1 thread may enter ******/ + + /* + * Now drain the endpoint TX queue for transmission as long as we have + * enough transmit resources. + */ + while (true) { + + if (get_queue_depth(&ep->txq) == 0) + break; + + if (ep->pipe.tx_credit_flow_enabled) { + /* + * Credit based mechanism provides flow control + * based on target transmit resource availability, + * we assume that the HIF layer will always have + * bus resources greater than target transmit + * resources. + */ + get_htc_packet_credit_based(target, ep, &send_queue); + } else { + /* + * Get all packets for this endpoint that we can + * for this pass. + */ + get_htc_packet(target, ep, &send_queue, tx_resources); + } + + if (get_queue_depth(&send_queue) == 0) { + /* + * Didn't get packets due to out of resources or TX + * queue was drained. + */ + break; + } + + spin_unlock_bh(&target->tx_lock); + + /* send what we can */ + htc_issue_packets(target, ep, &send_queue); + + if (!ep->pipe.tx_credit_flow_enabled) { + pipeid = ep->pipe.pipeid_ul; + tx_resources = + ath6kl_hif_pipe_get_free_queue_number(ar, pipeid); + } + + spin_lock_bh(&target->tx_lock); + + } + /* done with this endpoint, we can clear the count */ + ep->tx_proc_cnt = 0; + spin_unlock_bh(&target->tx_lock); + + return HTC_SEND_QUEUE_OK; +} + +/* htc control packet manipulation */ +static void destroy_htc_txctrl_packet(struct htc_packet *packet) +{ + struct sk_buff *skb; + skb = packet->skb; + if (skb != NULL) + dev_kfree_skb(skb); + + kfree(packet); +} + +static struct htc_packet *build_htc_txctrl_packet(void) +{ + struct htc_packet *packet = NULL; + struct sk_buff *skb; + + packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL); + if (packet == NULL) + return NULL; + + skb = __dev_alloc_skb(HTC_CONTROL_BUFFER_SIZE, GFP_KERNEL); + + if (skb == NULL) { + kfree(packet); + return NULL; + } + packet->skb = skb; + + return packet; +} + +static void htc_free_txctrl_packet(struct htc_target *target, + struct htc_packet *packet) +{ + destroy_htc_txctrl_packet(packet); +} + +static struct htc_packet *htc_alloc_txctrl_packet(struct htc_target *target) +{ + return build_htc_txctrl_packet(); +} + +static void htc_txctrl_complete(struct htc_target *target, + struct htc_packet *packet) +{ + htc_free_txctrl_packet(target, packet); +} + +#define MAX_MESSAGE_SIZE 1536 + +static int htc_setup_target_buffer_assignments(struct htc_target *target) +{ + int status, credits, credit_per_maxmsg, i; + struct htc_pipe_txcredit_alloc *entry; + unsigned int hif_usbaudioclass = 0; + + credit_per_maxmsg = MAX_MESSAGE_SIZE / target->tgt_cred_sz; + if (MAX_MESSAGE_SIZE % target->tgt_cred_sz) + credit_per_maxmsg++; + + /* TODO, this should be configured by the caller! */ + + credits = target->tgt_creds; + entry = &target->pipe.txcredit_alloc[0]; + + status = -ENOMEM; + + /* FIXME: hif_usbaudioclass is always zero */ + if (hif_usbaudioclass) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: For USB Audio Class- Total:%d\n", + __func__, credits); + entry++; + entry++; + /* Setup VO Service To have Max Credits */ + entry->service_id = WMI_DATA_VO_SVC; + entry->credit_alloc = (credits - 6); + if (entry->credit_alloc == 0) + entry->credit_alloc++; + + credits -= (int) entry->credit_alloc; + if (credits <= 0) + return status; + + entry++; + entry->service_id = WMI_CONTROL_SVC; + entry->credit_alloc = credit_per_maxmsg; + credits -= (int) entry->credit_alloc; + if (credits <= 0) + return status; + + /* leftovers go to best effort */ + entry++; + entry++; + entry->service_id = WMI_DATA_BE_SVC; + entry->credit_alloc = (u8) credits; + status = 0; + } else { + entry++; + entry->service_id = WMI_DATA_VI_SVC; + entry->credit_alloc = credits / 4; + if (entry->credit_alloc == 0) + entry->credit_alloc++; + + credits -= (int) entry->credit_alloc; + if (credits <= 0) + return status; + + entry++; + entry->service_id = WMI_DATA_VO_SVC; + entry->credit_alloc = credits / 4; + if (entry->credit_alloc == 0) + entry->credit_alloc++; + + credits -= (int) entry->credit_alloc; + if (credits <= 0) + return status; + + entry++; + entry->service_id = WMI_CONTROL_SVC; + entry->credit_alloc = credit_per_maxmsg; + credits -= (int) entry->credit_alloc; + if (credits <= 0) + return status; + + entry++; + entry->service_id = WMI_DATA_BK_SVC; + entry->credit_alloc = credit_per_maxmsg; + credits -= (int) entry->credit_alloc; + if (credits <= 0) + return status; + + /* leftovers go to best effort */ + entry++; + entry->service_id = WMI_DATA_BE_SVC; + entry->credit_alloc = (u8) credits; + status = 0; + } + + if (status == 0) { + for (i = 0; i < ENDPOINT_MAX; i++) { + if (target->pipe.txcredit_alloc[i].service_id != 0) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC Service Index : %d TX : 0x%2.2X : alloc:%d\n", + i, + target->pipe.txcredit_alloc[i]. + service_id, + target->pipe.txcredit_alloc[i]. + credit_alloc); + } + } + } + return status; +} + +/* process credit reports and call distribution function */ +static void htc_process_credit_report(struct htc_target *target, + struct htc_credit_report *rpt, + int num_entries, + enum htc_endpoint_id from_ep) +{ + int total_credits = 0, i; + struct htc_endpoint *ep; + + /* lock out TX while we update credits */ + spin_lock_bh(&target->tx_lock); + + for (i = 0; i < num_entries; i++, rpt++) { + if (rpt->eid >= ENDPOINT_MAX) { + WARN_ON_ONCE(1); + spin_unlock_bh(&target->tx_lock); + return; + } + + ep = &target->endpoint[rpt->eid]; + ep->cred_dist.credits += rpt->credits; + + if (ep->cred_dist.credits && get_queue_depth(&ep->txq)) { + spin_unlock_bh(&target->tx_lock); + htc_try_send(target, ep, NULL); + spin_lock_bh(&target->tx_lock); + } + + total_credits += rpt->credits; + } + ath6kl_dbg(ATH6KL_DBG_HTC, + "Report indicated %d credits to distribute\n", + total_credits); + + spin_unlock_bh(&target->tx_lock); +} + +/* flush endpoint TX queue */ +static void htc_flush_tx_endpoint(struct htc_target *target, + struct htc_endpoint *ep, u16 tag) +{ + struct htc_packet *packet; + + spin_lock_bh(&target->tx_lock); + while (get_queue_depth(&ep->txq)) { + packet = list_first_entry(&ep->txq, struct htc_packet, list); + list_del(&packet->list); + packet->status = 0; + send_packet_completion(target, packet); + } + spin_unlock_bh(&target->tx_lock); +} + +/* + * In the adapted HIF layer, struct sk_buff * are passed between HIF and HTC, + * since upper layers expects struct htc_packet containers we use the completed + * skb and lookup it's corresponding HTC packet buffer from a lookup list. + * This is extra overhead that can be fixed by re-aligning HIF interfaces with + * HTC. + */ +static struct htc_packet *htc_lookup_tx_packet(struct htc_target *target, + struct htc_endpoint *ep, + struct sk_buff *skb) +{ + struct htc_packet *packet, *tmp_pkt, *found_packet = NULL; + + spin_lock_bh(&target->tx_lock); + + /* + * interate from the front of tx lookup queue + * this lookup should be fast since lower layers completes in-order and + * so the completed packet should be at the head of the list generally + */ + list_for_each_entry_safe(packet, tmp_pkt, &ep->pipe.tx_lookup_queue, + list) { + /* check for removal */ + if (skb == packet->skb) { + /* found it */ + list_del(&packet->list); + found_packet = packet; + break; + } + } + + spin_unlock_bh(&target->tx_lock); + + return found_packet; +} + +static int ath6kl_htc_pipe_tx_complete(struct ath6kl *ar, struct sk_buff *skb) +{ + struct htc_target *target = ar->htc_target; + struct htc_frame_hdr *htc_hdr; + struct htc_endpoint *ep; + struct htc_packet *packet; + u8 ep_id, *netdata; + u32 netlen; + + netdata = skb->data; + netlen = skb->len; + + htc_hdr = (struct htc_frame_hdr *) netdata; + + ep_id = htc_hdr->eid; + ep = &target->endpoint[ep_id]; + + packet = htc_lookup_tx_packet(target, ep, skb); + if (packet == NULL) { + /* may have already been flushed and freed */ + ath6kl_err("HTC TX lookup failed!\n"); + } else { + /* will be giving this buffer back to upper layers */ + packet->status = 0; + send_packet_completion(target, packet); + } + skb = NULL; + + if (!ep->pipe.tx_credit_flow_enabled) { + /* + * note: when using TX credit flow, the re-checking of queues + * happens when credits flow back from the target. in the + * non-TX credit case, we recheck after the packet completes + */ + htc_try_send(target, ep, NULL); + } + + return 0; +} + +static int htc_send_packets_multiple(struct htc_target *target, + struct list_head *pkt_queue) +{ + struct htc_endpoint *ep; + struct htc_packet *packet, *tmp_pkt; + + if (list_empty(pkt_queue)) + return -EINVAL; + + /* get first packet to find out which ep the packets will go into */ + packet = list_first_entry(pkt_queue, struct htc_packet, list); + + if (packet->endpoint >= ENDPOINT_MAX) { + WARN_ON_ONCE(1); + return -EINVAL; + } + ep = &target->endpoint[packet->endpoint]; + + htc_try_send(target, ep, pkt_queue); + + /* do completion on any packets that couldn't get in */ + if (!list_empty(pkt_queue)) { + list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { + packet->status = -ENOMEM; + } + + do_send_completion(ep, pkt_queue); + } + + return 0; +} + +/* htc pipe rx path */ +static struct htc_packet *alloc_htc_packet_container(struct htc_target *target) +{ + struct htc_packet *packet; + spin_lock_bh(&target->rx_lock); + + if (target->pipe.htc_packet_pool == NULL) { + spin_unlock_bh(&target->rx_lock); + return NULL; + } + + packet = target->pipe.htc_packet_pool; + target->pipe.htc_packet_pool = (struct htc_packet *) packet->list.next; + + spin_unlock_bh(&target->rx_lock); + + packet->list.next = NULL; + return packet; +} + +static void free_htc_packet_container(struct htc_target *target, + struct htc_packet *packet) +{ + struct list_head *lh; + + spin_lock_bh(&target->rx_lock); + + if (target->pipe.htc_packet_pool == NULL) { + target->pipe.htc_packet_pool = packet; + packet->list.next = NULL; + } else { + lh = (struct list_head *) target->pipe.htc_packet_pool; + packet->list.next = lh; + target->pipe.htc_packet_pool = packet; + } + + spin_unlock_bh(&target->rx_lock); +} + +static int htc_process_trailer(struct htc_target *target, u8 *buffer, + int len, enum htc_endpoint_id from_ep) +{ + struct htc_credit_report *report; + struct htc_record_hdr *record; + u8 *record_buf, *orig_buf; + int orig_len, status; + + orig_buf = buffer; + orig_len = len; + status = 0; + + while (len > 0) { + if (len < sizeof(struct htc_record_hdr)) { + status = -EINVAL; + break; + } + + /* these are byte aligned structs */ + record = (struct htc_record_hdr *) buffer; + len -= sizeof(struct htc_record_hdr); + buffer += sizeof(struct htc_record_hdr); + + if (record->len > len) { + /* no room left in buffer for record */ + ath6kl_dbg(ATH6KL_DBG_HTC, + "invalid length: %d (id:%d) buffer has: %d bytes left\n", + record->len, record->rec_id, len); + status = -EINVAL; + break; + } + + /* start of record follows the header */ + record_buf = buffer; + + switch (record->rec_id) { + case HTC_RECORD_CREDITS: + if (record->len < sizeof(struct htc_credit_report)) { + WARN_ON_ONCE(1); + return -EINVAL; + } + + report = (struct htc_credit_report *) record_buf; + htc_process_credit_report(target, report, + record->len / sizeof(*report), + from_ep); + break; + default: + ath6kl_dbg(ATH6KL_DBG_HTC, + "unhandled record: id:%d length:%d\n", + record->rec_id, record->len); + break; + } + + if (status != 0) + break; + + /* advance buffer past this record for next time around */ + buffer += record->len; + len -= record->len; + } + + return status; +} + +static void do_recv_completion(struct htc_endpoint *ep, + struct list_head *queue_to_indicate) +{ + struct htc_packet *packet; + + if (list_empty(queue_to_indicate)) { + /* nothing to indicate */ + return; + } + + /* using legacy EpRecv */ + while (!list_empty(queue_to_indicate)) { + packet = list_first_entry(queue_to_indicate, + struct htc_packet, list); + list_del(&packet->list); + ep->ep_cb.rx(ep->target, packet); + } + + return; +} + +static void recv_packet_completion(struct htc_target *target, + struct htc_endpoint *ep, + struct htc_packet *packet) +{ + struct list_head container; + INIT_LIST_HEAD(&container); + list_add_tail(&packet->list, &container); + + /* do completion */ + do_recv_completion(ep, &container); +} + +static int ath6kl_htc_pipe_rx_complete(struct ath6kl *ar, struct sk_buff *skb, + u8 pipeid) +{ + struct htc_target *target = ar->htc_target; + u8 *netdata, *trailer, hdr_info; + struct htc_frame_hdr *htc_hdr; + u32 netlen, trailerlen = 0; + struct htc_packet *packet; + struct htc_endpoint *ep; + u16 payload_len; + int status = 0; + + netdata = skb->data; + netlen = skb->len; + + htc_hdr = (struct htc_frame_hdr *) netdata; + + ep = &target->endpoint[htc_hdr->eid]; + + if (htc_hdr->eid >= ENDPOINT_MAX) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC Rx: invalid EndpointID=%d\n", + htc_hdr->eid); + status = -EINVAL; + goto free_skb; + } + + payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len)); + + if (netlen < (payload_len + HTC_HDR_LENGTH)) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC Rx: insufficient length, got:%d expected =%u\n", + netlen, payload_len + HTC_HDR_LENGTH); + status = -EINVAL; + goto free_skb; + } + + /* get flags to check for trailer */ + hdr_info = htc_hdr->flags; + if (hdr_info & HTC_FLG_RX_TRAILER) { + /* extract the trailer length */ + hdr_info = htc_hdr->ctrl[0]; + if ((hdr_info < sizeof(struct htc_record_hdr)) || + (hdr_info > payload_len)) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "invalid header: payloadlen should be %d, CB[0]: %d\n", + payload_len, hdr_info); + status = -EINVAL; + goto free_skb; + } + + trailerlen = hdr_info; + /* process trailer after hdr/apps payload */ + trailer = (u8 *) htc_hdr + HTC_HDR_LENGTH + + payload_len - hdr_info; + status = htc_process_trailer(target, trailer, hdr_info, + htc_hdr->eid); + if (status != 0) + goto free_skb; + } + + if (((int) payload_len - (int) trailerlen) <= 0) { + /* zero length packet with trailer, just drop these */ + goto free_skb; + } + + if (htc_hdr->eid == ENDPOINT_0) { + /* handle HTC control message */ + if (target->htc_flags & HTC_OP_STATE_SETUP_COMPLETE) { + /* + * fatal: target should not send unsolicited + * messageson the endpoint 0 + */ + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC ignores Rx Ctrl after setup complete\n"); + status = -EINVAL; + goto free_skb; + } + + /* remove HTC header */ + skb_pull(skb, HTC_HDR_LENGTH); + + netdata = skb->data; + netlen = skb->len; + + spin_lock_bh(&target->rx_lock); + + target->pipe.ctrl_response_valid = true; + target->pipe.ctrl_response_len = min_t(int, netlen, + HTC_MAX_CTRL_MSG_LEN); + memcpy(target->pipe.ctrl_response_buf, netdata, + target->pipe.ctrl_response_len); + + spin_unlock_bh(&target->rx_lock); + + dev_kfree_skb(skb); + skb = NULL; + goto free_skb; + } + + /* + * TODO: the message based HIF architecture allocates net bufs + * for recv packets since it bridges that HIF to upper layers, + * which expects HTC packets, we form the packets here + */ + packet = alloc_htc_packet_container(target); + if (packet == NULL) { + status = -ENOMEM; + goto free_skb; + } + + packet->status = 0; + packet->endpoint = htc_hdr->eid; + packet->pkt_cntxt = skb; + + /* TODO: for backwards compatibility */ + packet->buf = skb_push(skb, 0) + HTC_HDR_LENGTH; + packet->act_len = netlen - HTC_HDR_LENGTH - trailerlen; + + /* + * TODO: this is a hack because the driver layer will set the + * actual len of the skb again which will just double the len + */ + skb_trim(skb, 0); + + recv_packet_completion(target, ep, packet); + + /* recover the packet container */ + free_htc_packet_container(target, packet); + skb = NULL; + +free_skb: + if (skb != NULL) + dev_kfree_skb(skb); + + return status; + +} + +static void htc_flush_rx_queue(struct htc_target *target, + struct htc_endpoint *ep) +{ + struct list_head container; + struct htc_packet *packet; + + spin_lock_bh(&target->rx_lock); + + while (1) { + if (list_empty(&ep->rx_bufq)) + break; + + packet = list_first_entry(&ep->rx_bufq, + struct htc_packet, list); + list_del(&packet->list); + + spin_unlock_bh(&target->rx_lock); + packet->status = -ECANCELED; + packet->act_len = 0; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "Flushing RX packet:0x%p, length:%d, ep:%d\n", + packet, packet->buf_len, + packet->endpoint); + + INIT_LIST_HEAD(&container); + list_add_tail(&packet->list, &container); + + /* give the packet back */ + do_recv_completion(ep, &container); + spin_lock_bh(&target->rx_lock); + } + + spin_unlock_bh(&target->rx_lock); +} + +/* polling routine to wait for a control packet to be received */ +static int htc_wait_recv_ctrl_message(struct htc_target *target) +{ + int count = HTC_TARGET_RESPONSE_POLL_COUNT; + + while (count > 0) { + spin_lock_bh(&target->rx_lock); + + if (target->pipe.ctrl_response_valid) { + target->pipe.ctrl_response_valid = false; + spin_unlock_bh(&target->rx_lock); + break; + } + + spin_unlock_bh(&target->rx_lock); + + count--; + + msleep_interruptible(HTC_TARGET_RESPONSE_POLL_WAIT); + } + + if (count <= 0) { + ath6kl_dbg(ATH6KL_DBG_HTC, "%s: Timeout!\n", __func__); + return -ECOMM; + } + + return 0; +} + +static void htc_rxctrl_complete(struct htc_target *context, + struct htc_packet *packet) +{ + /* TODO, can't really receive HTC control messages yet.... */ + ath6kl_dbg(ATH6KL_DBG_HTC, "%s: invalid call function\n", __func__); +} + +/* htc pipe initialization */ +static void reset_endpoint_states(struct htc_target *target) +{ + struct htc_endpoint *ep; + int i; + + for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { + ep = &target->endpoint[i]; + ep->svc_id = 0; + ep->len_max = 0; + ep->max_txq_depth = 0; + ep->eid = i; + INIT_LIST_HEAD(&ep->txq); + INIT_LIST_HEAD(&ep->pipe.tx_lookup_queue); + INIT_LIST_HEAD(&ep->rx_bufq); + ep->target = target; + ep->pipe.tx_credit_flow_enabled = (bool) 1; /* FIXME */ + } +} + +/* start HTC, this is called after all services are connected */ +static int htc_config_target_hif_pipe(struct htc_target *target) +{ + return 0; +} + +/* htc service functions */ +static u8 htc_get_credit_alloc(struct htc_target *target, u16 service_id) +{ + u8 allocation = 0; + int i; + + for (i = 0; i < ENDPOINT_MAX; i++) { + if (target->pipe.txcredit_alloc[i].service_id == service_id) + allocation = + target->pipe.txcredit_alloc[i].credit_alloc; + } + + if (allocation == 0) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "HTC Service TX : 0x%2.2X : allocation is zero!\n", + service_id); + } + + return allocation; +} + +static int ath6kl_htc_pipe_conn_service(struct htc_target *target, + struct htc_service_connect_req *conn_req, + struct htc_service_connect_resp *conn_resp) +{ + struct ath6kl *ar = target->dev->ar; + struct htc_packet *packet = NULL; + struct htc_conn_service_resp *resp_msg; + struct htc_conn_service_msg *conn_msg; + enum htc_endpoint_id assigned_epid = ENDPOINT_MAX; + bool disable_credit_flowctrl = false; + unsigned int max_msg_size = 0; + struct htc_endpoint *ep; + int length, status = 0; + struct sk_buff *skb; + u8 tx_alloc; + u16 flags; + + if (conn_req->svc_id == 0) { + WARN_ON_ONCE(1); + status = -EINVAL; + goto free_packet; + } + + if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) { + /* special case for pseudo control service */ + assigned_epid = ENDPOINT_0; + max_msg_size = HTC_MAX_CTRL_MSG_LEN; + tx_alloc = 0; + + } else { + + tx_alloc = htc_get_credit_alloc(target, conn_req->svc_id); + if (tx_alloc == 0) { + status = -ENOMEM; + goto free_packet; + } + + /* allocate a packet to send to the target */ + packet = htc_alloc_txctrl_packet(target); + + if (packet == NULL) { + WARN_ON_ONCE(1); + status = -ENOMEM; + goto free_packet; + } + + skb = packet->skb; + length = sizeof(struct htc_conn_service_msg); + + /* assemble connect service message */ + conn_msg = (struct htc_conn_service_msg *) skb_put(skb, + length); + if (conn_msg == NULL) { + WARN_ON_ONCE(1); + status = -EINVAL; + goto free_packet; + } + + memset(conn_msg, 0, + sizeof(struct htc_conn_service_msg)); + conn_msg->msg_id = cpu_to_le16(HTC_MSG_CONN_SVC_ID); + conn_msg->svc_id = cpu_to_le16(conn_req->svc_id); + conn_msg->conn_flags = cpu_to_le16(conn_req->conn_flags & + ~HTC_CONN_FLGS_SET_RECV_ALLOC_MASK); + + /* tell target desired recv alloc for this ep */ + flags = tx_alloc << HTC_CONN_FLGS_SET_RECV_ALLOC_SHIFT; + conn_msg->conn_flags |= cpu_to_le16(flags); + + if (conn_req->conn_flags & + HTC_CONN_FLGS_DISABLE_CRED_FLOW_CTRL) { + disable_credit_flowctrl = true; + } + + set_htc_pkt_info(packet, NULL, (u8 *) conn_msg, + length, + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + + status = ath6kl_htc_pipe_tx(target, packet); + + /* we don't own it anymore */ + packet = NULL; + if (status != 0) + goto free_packet; + + /* wait for response */ + status = htc_wait_recv_ctrl_message(target); + if (status != 0) + goto free_packet; + + /* we controlled the buffer creation so it has to be + * properly aligned + */ + resp_msg = (struct htc_conn_service_resp *) + target->pipe.ctrl_response_buf; + + if (resp_msg->msg_id != cpu_to_le16(HTC_MSG_CONN_SVC_RESP_ID) || + (target->pipe.ctrl_response_len < sizeof(*resp_msg))) { + /* this message is not valid */ + WARN_ON_ONCE(1); + status = -EINVAL; + goto free_packet; + } + + ath6kl_dbg(ATH6KL_DBG_TRC, + "%s: service 0x%X conn resp: status: %d ep: %d\n", + __func__, resp_msg->svc_id, resp_msg->status, + resp_msg->eid); + + conn_resp->resp_code = resp_msg->status; + /* check response status */ + if (resp_msg->status != HTC_SERVICE_SUCCESS) { + ath6kl_dbg(ATH6KL_DBG_HTC, + "Target failed service 0x%X connect request (status:%d)\n", + resp_msg->svc_id, resp_msg->status); + status = -EINVAL; + goto free_packet; + } + + assigned_epid = (enum htc_endpoint_id) resp_msg->eid; + max_msg_size = le16_to_cpu(resp_msg->max_msg_sz); + } + + /* the rest are parameter checks so set the error status */ + status = -EINVAL; + + if (assigned_epid >= ENDPOINT_MAX) { + WARN_ON_ONCE(1); + goto free_packet; + } + + if (max_msg_size == 0) { + WARN_ON_ONCE(1); + goto free_packet; + } + + ep = &target->endpoint[assigned_epid]; + ep->eid = assigned_epid; + if (ep->svc_id != 0) { + /* endpoint already in use! */ + WARN_ON_ONCE(1); + goto free_packet; + } + + /* return assigned endpoint to caller */ + conn_resp->endpoint = assigned_epid; + conn_resp->len_max = max_msg_size; + + /* setup the endpoint */ + ep->svc_id = conn_req->svc_id; /* this marks ep in use */ + ep->max_txq_depth = conn_req->max_txq_depth; + ep->len_max = max_msg_size; + ep->cred_dist.credits = tx_alloc; + ep->cred_dist.cred_sz = target->tgt_cred_sz; + ep->cred_dist.cred_per_msg = max_msg_size / target->tgt_cred_sz; + if (max_msg_size % target->tgt_cred_sz) + ep->cred_dist.cred_per_msg++; + + /* copy all the callbacks */ + ep->ep_cb = conn_req->ep_cb; + + /* initialize tx_drop_packet_threshold */ + ep->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM; + + status = ath6kl_hif_pipe_map_service(ar, ep->svc_id, + &ep->pipe.pipeid_ul, + &ep->pipe.pipeid_dl); + if (status != 0) + goto free_packet; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "SVC Ready: 0x%4.4X: ULpipe:%d DLpipe:%d id:%d\n", + ep->svc_id, ep->pipe.pipeid_ul, + ep->pipe.pipeid_dl, ep->eid); + + if (disable_credit_flowctrl && ep->pipe.tx_credit_flow_enabled) { + ep->pipe.tx_credit_flow_enabled = false; + ath6kl_dbg(ATH6KL_DBG_HTC, + "SVC: 0x%4.4X ep:%d TX flow control off\n", + ep->svc_id, assigned_epid); + } + +free_packet: + if (packet != NULL) + htc_free_txctrl_packet(target, packet); + return status; +} + +/* htc export functions */ +static void *ath6kl_htc_pipe_create(struct ath6kl *ar) +{ + int status = 0; + struct htc_endpoint *ep = NULL; + struct htc_target *target = NULL; + struct htc_packet *packet; + int i; + + target = kzalloc(sizeof(struct htc_target), GFP_KERNEL); + if (target == NULL) { + ath6kl_err("htc create unable to allocate memory\n"); + status = -ENOMEM; + goto fail_htc_create; + } + + spin_lock_init(&target->htc_lock); + spin_lock_init(&target->rx_lock); + spin_lock_init(&target->tx_lock); + + reset_endpoint_states(target); + + for (i = 0; i < HTC_PACKET_CONTAINER_ALLOCATION; i++) { + packet = kzalloc(sizeof(struct htc_packet), GFP_KERNEL); + + if (packet != NULL) + free_htc_packet_container(target, packet); + } + + target->dev = kzalloc(sizeof(*target->dev), GFP_KERNEL); + if (!target->dev) { + ath6kl_err("unable to allocate memory\n"); + status = -ENOMEM; + goto fail_htc_create; + } + target->dev->ar = ar; + target->dev->htc_cnxt = target; + + /* Get HIF default pipe for HTC message exchange */ + ep = &target->endpoint[ENDPOINT_0]; + + ath6kl_hif_pipe_get_default(ar, &ep->pipe.pipeid_ul, + &ep->pipe.pipeid_dl); + + return target; + +fail_htc_create: + if (status != 0) { + if (target != NULL) + ath6kl_htc_pipe_cleanup(target); + + target = NULL; + } + return target; +} + +/* cleanup the HTC instance */ +static void ath6kl_htc_pipe_cleanup(struct htc_target *target) +{ + struct htc_packet *packet; + + while (true) { + packet = alloc_htc_packet_container(target); + if (packet == NULL) + break; + kfree(packet); + } + + kfree(target->dev); + + /* kfree our instance */ + kfree(target); +} + +static int ath6kl_htc_pipe_start(struct htc_target *target) +{ + struct sk_buff *skb; + struct htc_setup_comp_ext_msg *setup; + struct htc_packet *packet; + + htc_config_target_hif_pipe(target); + + /* allocate a buffer to send */ + packet = htc_alloc_txctrl_packet(target); + if (packet == NULL) { + WARN_ON_ONCE(1); + return -ENOMEM; + } + + skb = packet->skb; + + /* assemble setup complete message */ + setup = (struct htc_setup_comp_ext_msg *) skb_put(skb, + sizeof(*setup)); + memset(setup, 0, sizeof(struct htc_setup_comp_ext_msg)); + setup->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID); + + ath6kl_dbg(ATH6KL_DBG_HTC, "HTC using TX credit flow control\n"); + + set_htc_pkt_info(packet, NULL, (u8 *) setup, + sizeof(struct htc_setup_comp_ext_msg), + ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); + + target->htc_flags |= HTC_OP_STATE_SETUP_COMPLETE; + + return ath6kl_htc_pipe_tx(target, packet); +} + +static void ath6kl_htc_pipe_stop(struct htc_target *target) +{ + int i; + struct htc_endpoint *ep; + + /* cleanup endpoints */ + for (i = 0; i < ENDPOINT_MAX; i++) { + ep = &target->endpoint[i]; + htc_flush_rx_queue(target, ep); + htc_flush_tx_endpoint(target, ep, HTC_TX_PACKET_TAG_ALL); + } + + reset_endpoint_states(target); + target->htc_flags &= ~HTC_OP_STATE_SETUP_COMPLETE; +} + +static int ath6kl_htc_pipe_get_rxbuf_num(struct htc_target *target, + enum htc_endpoint_id endpoint) +{ + int num; + + spin_lock_bh(&target->rx_lock); + num = get_queue_depth(&(target->endpoint[endpoint].rx_bufq)); + spin_unlock_bh(&target->rx_lock); + + return num; +} + +static int ath6kl_htc_pipe_tx(struct htc_target *target, + struct htc_packet *packet) +{ + struct list_head queue; + + ath6kl_dbg(ATH6KL_DBG_HTC, + "%s: endPointId: %d, buffer: 0x%p, length: %d\n", + __func__, packet->endpoint, packet->buf, + packet->act_len); + + INIT_LIST_HEAD(&queue); + list_add_tail(&packet->list, &queue); + + return htc_send_packets_multiple(target, &queue); +} + +static int ath6kl_htc_pipe_wait_target(struct htc_target *target) +{ + struct htc_ready_ext_msg *ready_msg; + struct htc_service_connect_req connect; + struct htc_service_connect_resp resp; + int status = 0; + + status = htc_wait_recv_ctrl_message(target); + + if (status != 0) + return status; + + if (target->pipe.ctrl_response_len < sizeof(*ready_msg)) { + ath6kl_dbg(ATH6KL_DBG_HTC, "invalid htc ready msg len:%d!\n", + target->pipe.ctrl_response_len); + return -ECOMM; + } + + ready_msg = (struct htc_ready_ext_msg *) target->pipe.ctrl_response_buf; + + if (ready_msg->ver2_0_info.msg_id != cpu_to_le16(HTC_MSG_READY_ID)) { + ath6kl_dbg(ATH6KL_DBG_HTC, "invalid htc ready msg : 0x%X !\n", + ready_msg->ver2_0_info.msg_id); + return -ECOMM; + } + + ath6kl_dbg(ATH6KL_DBG_HTC, + "Target Ready! : transmit resources : %d size:%d\n", + ready_msg->ver2_0_info.cred_cnt, + ready_msg->ver2_0_info.cred_sz); + + target->tgt_creds = le16_to_cpu(ready_msg->ver2_0_info.cred_cnt); + target->tgt_cred_sz = le16_to_cpu(ready_msg->ver2_0_info.cred_sz); + + if ((target->tgt_creds == 0) || (target->tgt_cred_sz == 0)) + return -ECOMM; + + htc_setup_target_buffer_assignments(target); + + /* setup our pseudo HTC control endpoint connection */ + memset(&connect, 0, sizeof(connect)); + memset(&resp, 0, sizeof(resp)); + connect.ep_cb.tx_complete = htc_txctrl_complete; + connect.ep_cb.rx = htc_rxctrl_complete; + connect.max_txq_depth = NUM_CONTROL_TX_BUFFERS; + connect.svc_id = HTC_CTRL_RSVD_SVC; + + /* connect fake service */ + status = ath6kl_htc_pipe_conn_service(target, &connect, &resp); + + return status; +} + +static void ath6kl_htc_pipe_flush_txep(struct htc_target *target, + enum htc_endpoint_id endpoint, u16 tag) +{ + struct htc_endpoint *ep = &target->endpoint[endpoint]; + + if (ep->svc_id == 0) { + WARN_ON_ONCE(1); + /* not in use.. */ + return; + } + + htc_flush_tx_endpoint(target, ep, tag); +} + +static int ath6kl_htc_pipe_add_rxbuf_multiple(struct htc_target *target, + struct list_head *pkt_queue) +{ + struct htc_packet *packet, *tmp_pkt, *first; + struct htc_endpoint *ep; + int status = 0; + + if (list_empty(pkt_queue)) + return -EINVAL; + + first = list_first_entry(pkt_queue, struct htc_packet, list); + + if (first->endpoint >= ENDPOINT_MAX) { + WARN_ON_ONCE(1); + return -EINVAL; + } + + ath6kl_dbg(ATH6KL_DBG_HTC, "%s: epid: %d, cnt:%d, len: %d\n", + __func__, first->endpoint, get_queue_depth(pkt_queue), + first->buf_len); + + ep = &target->endpoint[first->endpoint]; + + spin_lock_bh(&target->rx_lock); + + /* store receive packets */ + list_splice_tail_init(pkt_queue, &ep->rx_bufq); + + spin_unlock_bh(&target->rx_lock); + + if (status != 0) { + /* walk through queue and mark each one canceled */ + list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { + packet->status = -ECANCELED; + } + + do_recv_completion(ep, pkt_queue); + } + + return status; +} + +static void ath6kl_htc_pipe_activity_changed(struct htc_target *target, + enum htc_endpoint_id ep, + bool active) +{ + /* TODO */ +} + +static void ath6kl_htc_pipe_flush_rx_buf(struct htc_target *target) +{ + /* TODO */ +} + +static int ath6kl_htc_pipe_credit_setup(struct htc_target *target, + struct ath6kl_htc_credit_info *info) +{ + return 0; +} + +static const struct ath6kl_htc_ops ath6kl_htc_pipe_ops = { + .create = ath6kl_htc_pipe_create, + .wait_target = ath6kl_htc_pipe_wait_target, + .start = ath6kl_htc_pipe_start, + .conn_service = ath6kl_htc_pipe_conn_service, + .tx = ath6kl_htc_pipe_tx, + .stop = ath6kl_htc_pipe_stop, + .cleanup = ath6kl_htc_pipe_cleanup, + .flush_txep = ath6kl_htc_pipe_flush_txep, + .flush_rx_buf = ath6kl_htc_pipe_flush_rx_buf, + .activity_changed = ath6kl_htc_pipe_activity_changed, + .get_rxbuf_num = ath6kl_htc_pipe_get_rxbuf_num, + .add_rxbuf_multiple = ath6kl_htc_pipe_add_rxbuf_multiple, + .credit_setup = ath6kl_htc_pipe_credit_setup, + .tx_complete = ath6kl_htc_pipe_tx_complete, + .rx_complete = ath6kl_htc_pipe_rx_complete, +}; + +void ath6kl_htc_pipe_attach(struct ath6kl *ar) +{ + ar->htc_ops = &ath6kl_htc_pipe_ops; +} diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 03cae142f178..7eb0515f458a 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -16,17 +16,21 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/moduleparam.h> #include <linux/errno.h> #include <linux/export.h> #include <linux/of.h> #include <linux/mmc/sdio_func.h> +#include <linux/vmalloc.h> #include "core.h" #include "cfg80211.h" #include "target.h" #include "debug.h" #include "hif-ops.h" +#include "htc-ops.h" static const struct ath6kl_hw hw_list[] = { { @@ -115,6 +119,24 @@ static const struct ath6kl_hw hw_list[] = { .fw_board = AR6004_HW_1_1_BOARD_DATA_FILE, .fw_default_board = AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE, }, + { + .id = AR6004_HW_1_2_VERSION, + .name = "ar6004 hw 1.2", + .dataset_patch_addr = 0x436ecc, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x437000, + .reserved_ram_size = 9216, + .board_addr = 0x435c00, + .refclk_hz = 40000000, + .uarttx_pin = 11, + + .fw = { + .dir = AR6004_HW_1_2_FW_DIR, + .fw = AR6004_HW_1_2_FIRMWARE_FILE, + }, + .fw_board = AR6004_HW_1_2_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE, + }, }; /* @@ -256,6 +278,7 @@ static int ath6kl_init_service_ep(struct ath6kl *ar) memset(&connect, 0, sizeof(connect)); /* these fields are the same for all service endpoints */ + connect.ep_cb.tx_comp_multi = ath6kl_tx_complete; connect.ep_cb.rx = ath6kl_rx; connect.ep_cb.rx_refill = ath6kl_rx_refill; connect.ep_cb.tx_full = ath6kl_tx_queue_full; @@ -440,9 +463,9 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) P2P_FLAG_MACADDR_REQ | P2P_FLAG_HMODEL_REQ); if (ret) { - ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P " - "capabilities (%d) - assuming P2P not " - "supported\n", ret); + ath6kl_dbg(ATH6KL_DBG_TRC, + "failed to request P2P capabilities (%d) - assuming P2P not supported\n", + ret); ar->p2p = false; } } @@ -451,8 +474,9 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx) /* Enable Probe Request reporting for P2P */ ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, idx, true); if (ret) { - ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe " - "Request reporting (%d)\n", ret); + ath6kl_dbg(ATH6KL_DBG_TRC, + "failed to enable Probe Request reporting (%d)\n", + ret); } } @@ -485,22 +509,31 @@ int ath6kl_configure_target(struct ath6kl *ar) fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS); /* - * By default, submodes : + * Submodes when fw does not support dynamic interface + * switching: * vif[0] - AP/STA/IBSS * vif[1] - "P2P dev"/"P2P GO"/"P2P Client" * vif[2] - "P2P dev"/"P2P GO"/"P2P Client" + * Otherwise, All the interface are initialized to p2p dev. */ - for (i = 0; i < ar->max_norm_iface; i++) - fw_submode |= HI_OPTION_FW_SUBMODE_NONE << - (i * HI_OPTION_FW_SUBMODE_BITS); + if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, + ar->fw_capabilities)) { + for (i = 0; i < ar->vif_max; i++) + fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << + (i * HI_OPTION_FW_SUBMODE_BITS); + } else { + for (i = 0; i < ar->max_norm_iface; i++) + fw_submode |= HI_OPTION_FW_SUBMODE_NONE << + (i * HI_OPTION_FW_SUBMODE_BITS); - for (i = ar->max_norm_iface; i < ar->vif_max; i++) - fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << - (i * HI_OPTION_FW_SUBMODE_BITS); + for (i = ar->max_norm_iface; i < ar->vif_max; i++) + fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << + (i * HI_OPTION_FW_SUBMODE_BITS); - if (ar->p2p && ar->vif_max == 1) - fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; + if (ar->p2p && ar->vif_max == 1) + fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; + } if (ath6kl_bmi_write_hi32(ar, hi_app_host_interest, HTC_PROTOCOL_VERSION) != 0) { @@ -539,18 +572,20 @@ int ath6kl_configure_target(struct ath6kl *ar) * but possible in theory. */ - param = ar->hw.board_ext_data_addr; - ram_reserved_size = ar->hw.reserved_ram_size; + if (ar->target_type == TARGET_TYPE_AR6003) { + param = ar->hw.board_ext_data_addr; + ram_reserved_size = ar->hw.reserved_ram_size; - if (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) { - ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); - return -EIO; - } + if (ath6kl_bmi_write_hi32(ar, hi_board_ext_data, param) != 0) { + ath6kl_err("bmi_write_memory for hi_board_ext_data failed\n"); + return -EIO; + } - if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, - ram_reserved_size) != 0) { - ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); - return -EIO; + if (ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, + ram_reserved_size) != 0) { + ath6kl_err("bmi_write_memory for hi_end_ram_reserve_sz failed\n"); + return -EIO; + } } /* set the block size for the target */ @@ -924,13 +959,14 @@ static int ath6kl_fetch_fw_apin(struct ath6kl *ar, const char *name) if (ar->fw != NULL) break; - ar->fw = kmemdup(data, ie_len, GFP_KERNEL); + ar->fw = vmalloc(ie_len); if (ar->fw == NULL) { ret = -ENOMEM; goto out; } + memcpy(ar->fw, data, ie_len); ar->fw_len = ie_len; break; case ATH6KL_FW_IE_PATCH_IMAGE: @@ -1507,7 +1543,7 @@ int ath6kl_init_hw_start(struct ath6kl *ar) } /* setup credit distribution */ - ath6kl_credit_setup(ar->htc_target, &ar->credit_state_info); + ath6kl_htc_credit_setup(ar->htc_target, &ar->credit_state_info); /* start HTC */ ret = ath6kl_htc_start(ar->htc_target); diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 229e1922ebe4..e5524470529c 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -15,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "core.h" #include "hif-ops.h" #include "cfg80211.h" @@ -419,8 +421,8 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) if (!ik->valid) break; - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed addkey for " - "the initial group key for AP mode\n"); + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "Delayed addkey for the initial group key for AP mode\n"); memset(key_rsc, 0, sizeof(key_rsc)); res = ath6kl_wmi_addkey_cmd( ar->wmi, vif->fw_vif_idx, ik->key_index, ik->key_type, @@ -428,12 +430,19 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) ik->key, KEY_OP_INIT_VAL, NULL, SYNC_BOTH_WMIFLAG); if (res) { - ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "Delayed " - "addkey failed: %d\n", res); + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "Delayed addkey failed: %d\n", res); } break; } + if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) { + ar->want_ch_switch &= ~(1 << vif->fw_vif_idx); + /* we actually don't know the phymode, default to HT20 */ + ath6kl_cfg80211_ch_switch_notify(vif, channel, + WMI_11G_HT20); + } + ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); set_bit(CONNECTED, &vif->flags); netif_carrier_on(vif->ndev); @@ -539,7 +548,8 @@ void ath6kl_disconnect(struct ath6kl_vif *vif) /* WMI Event handlers */ -void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver) +void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver, + enum wmi_phy_cap cap) { struct ath6kl *ar = devt; @@ -549,6 +559,7 @@ void ath6kl_ready_event(void *devt, u8 *datap, u32 sw_ver, u32 abi_ver) ar->version.wlan_ver = sw_ver; ar->version.abi_ver = abi_ver; + ar->hw.cap = cap; snprintf(ar->wiphy->fw_version, sizeof(ar->wiphy->fw_version), @@ -582,6 +593,45 @@ void ath6kl_scan_complete_evt(struct ath6kl_vif *vif, int status) ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "scan complete: %d\n", status); } +static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) +{ + + struct ath6kl *ar = vif->ar; + + vif->next_chan = channel; + vif->profile.ch = cpu_to_le16(channel); + + switch (vif->nw_type) { + case AP_NETWORK: + return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, + &vif->profile); + default: + ath6kl_err("won't switch channels nw_type=%d\n", vif->nw_type); + return -ENOTSUPP; + } +} + +static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel) +{ + + struct ath6kl_vif *vif; + int res = 0; + + if (!ar->want_ch_switch) + return; + + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) + res = ath6kl_commit_ch_switch(vif, channel); + + if (res) + ath6kl_err("channel switch failed nw_type %d res %d\n", + vif->nw_type, res); + } + spin_unlock_bh(&ar->list_lock); +} + void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, u16 listen_int, u16 beacon_int, enum network_type net_type, u8 beacon_ie_len, @@ -599,9 +649,11 @@ void ath6kl_connect_event(struct ath6kl_vif *vif, u16 channel, u8 *bssid, memcpy(vif->bssid, bssid, sizeof(vif->bssid)); vif->bss_ch = channel; - if ((vif->nw_type == INFRA_NETWORK)) + if ((vif->nw_type == INFRA_NETWORK)) { ath6kl_wmi_listeninterval_cmd(ar->wmi, vif->fw_vif_idx, vif->listen_intvl_t, 0); + ath6kl_check_ch_switch(ar, channel); + } netif_wake_queue(vif->ndev); @@ -756,6 +808,10 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) stats->wow_evt_discarded += le16_to_cpu(tgt_stats->wow_stats.wow_evt_discarded); + stats->arp_received = le32_to_cpu(tgt_stats->arp_stats.arp_received); + stats->arp_replied = le32_to_cpu(tgt_stats->arp_stats.arp_replied); + stats->arp_matched = le32_to_cpu(tgt_stats->arp_stats.arp_matched); + if (test_bit(STATS_UPDATE_PEND, &vif->flags)) { clear_bit(STATS_UPDATE_PEND, &vif->flags); wake_up(&ar->event_wq); @@ -920,6 +976,11 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, struct ath6kl *ar = vif->ar; if (vif->nw_type == AP_NETWORK) { + /* disconnect due to other STA vif switching channels */ + if (reason == BSS_DISCONNECTED && + prot_reason_status == WMI_AP_REASON_STA_ROAM) + ar->want_ch_switch |= 1 << vif->fw_vif_idx; + if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) return; @@ -1084,7 +1145,7 @@ static int ath6kl_set_features(struct net_device *dev, static void ath6kl_set_multicast_list(struct net_device *ndev) { struct ath6kl_vif *vif = netdev_priv(ndev); - bool mc_all_on = false, mc_all_off = false; + bool mc_all_on = false; int mc_count = netdev_mc_count(ndev); struct netdev_hw_addr *ha; bool found; @@ -1096,24 +1157,41 @@ static void ath6kl_set_multicast_list(struct net_device *ndev) !test_bit(WLAN_ENABLED, &vif->flags)) return; + /* Enable multicast-all filter. */ mc_all_on = !!(ndev->flags & IFF_PROMISC) || !!(ndev->flags & IFF_ALLMULTI) || !!(mc_count > ATH6K_MAX_MC_FILTERS_PER_LIST); - mc_all_off = !(ndev->flags & IFF_MULTICAST) || mc_count == 0; + if (mc_all_on) + set_bit(NETDEV_MCAST_ALL_ON, &vif->flags); + else + clear_bit(NETDEV_MCAST_ALL_ON, &vif->flags); + + mc_all_on = mc_all_on || (vif->ar->state == ATH6KL_STATE_ON); - if (mc_all_on || mc_all_off) { - /* Enable/disable all multicast */ - ath6kl_dbg(ATH6KL_DBG_TRC, "%s multicast filter\n", - mc_all_on ? "enabling" : "disabling"); - ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, + if (!(ndev->flags & IFF_MULTICAST)) { + mc_all_on = false; + set_bit(NETDEV_MCAST_ALL_OFF, &vif->flags); + } else { + clear_bit(NETDEV_MCAST_ALL_OFF, &vif->flags); + } + + /* Enable/disable "multicast-all" filter*/ + ath6kl_dbg(ATH6KL_DBG_TRC, "%s multicast-all filter\n", + mc_all_on ? "enabling" : "disabling"); + + ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, vif->fw_vif_idx, mc_all_on); - if (ret) - ath6kl_warn("Failed to %s multicast receive\n", - mc_all_on ? "enable" : "disable"); + if (ret) { + ath6kl_warn("Failed to %s multicast-all receive\n", + mc_all_on ? "enable" : "disable"); return; } + if (test_bit(NETDEV_MCAST_ALL_ON, &vif->flags)) + return; + + /* Keep the driver and firmware mcast list in sync. */ list_for_each_entry_safe(mc_filter, tmp, &vif->mc_filter, list) { found = false; netdev_for_each_mc_addr(ha, ndev) { diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 53528648b425..05b95405f7b5 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -552,7 +552,7 @@ static int ath6kl_sdio_write_async(struct ath6kl *ar, u32 address, u8 *buffer, bus_req = ath6kl_sdio_alloc_busreq(ar_sdio); - if (!bus_req) + if (WARN_ON_ONCE(!bus_req)) return -ENOMEM; bus_req->address = address; @@ -915,6 +915,9 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) } cut_pwr: + if (func->card && func->card->host) + func->card->host->pm_flags &= ~MMC_PM_KEEP_POWER; + return ath6kl_cfg80211_suspend(ar, ATH6KL_CFG_SUSPEND_CUTPOWER, NULL); } @@ -985,9 +988,8 @@ static int ath6kl_set_addrwin_reg(struct ath6kl *ar, u32 reg_addr, u32 addr) } if (status) { - ath6kl_err("%s: failed to write initial bytes of 0x%x " - "to window reg: 0x%X\n", __func__, - addr, reg_addr); + ath6kl_err("%s: failed to write initial bytes of 0x%x to window reg: 0x%X\n", + __func__, addr, reg_addr); return status; } @@ -1076,8 +1078,8 @@ static int ath6kl_sdio_bmi_credits(struct ath6kl *ar) (u8 *)&ar->bmi.cmd_credits, 4, HIF_RD_SYNC_BYTE_INC); if (ret) { - ath6kl_err("Unable to decrement the command credit " - "count register: %d\n", ret); + ath6kl_err("Unable to decrement the command credit count register: %d\n", + ret); return ret; } @@ -1362,7 +1364,7 @@ static int ath6kl_sdio_probe(struct sdio_func *func, goto err_core_alloc; } - ret = ath6kl_core_init(ar); + ret = ath6kl_core_init(ar, ATH6KL_HTC_TYPE_MBOX); if (ret) { ath6kl_err("Failed to init ath6kl core\n"); goto err_core_alloc; @@ -1457,3 +1459,6 @@ MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_FW_DIR "/" AR6004_HW_1_1_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_FW_DIR "/" AR6004_HW_1_2_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl/testmode.c b/drivers/net/wireless/ath/ath6kl/testmode.c index 6675c92b542b..acc9aa832f76 100644 --- a/drivers/net/wireless/ath/ath6kl/testmode.c +++ b/drivers/net/wireless/ath/ath6kl/testmode.c @@ -55,8 +55,9 @@ void ath6kl_tm_rx_event(struct ath6kl *ar, void *buf, size_t buf_len) ath6kl_warn("failed to allocate testmode rx skb!\n"); return; } - NLA_PUT_U32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_TCMD); - NLA_PUT(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf); + if (nla_put_u32(skb, ATH6KL_TM_ATTR_CMD, ATH6KL_TM_CMD_TCMD) || + nla_put(skb, ATH6KL_TM_ATTR_DATA, buf_len, buf)) + goto nla_put_failure; cfg80211_testmode_event(skb, GFP_KERNEL); return; diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index f85353fd1792..67206aedea6c 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -15,8 +15,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "core.h" #include "debug.h" +#include "htc-ops.h" /* * tid - tid_mux0..tid_mux3 @@ -322,6 +325,7 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, cookie->map_no = 0; set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, eid, ATH6KL_CONTROL_PKT_TAG); + cookie->htc_pkt.skb = skb; /* * This interface is asynchronous, if there is an error, cleanup @@ -358,15 +362,11 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) skb, skb->data, skb->len); /* If target is not associated */ - if (!test_bit(CONNECTED, &vif->flags)) { - dev_kfree_skb(skb); - return 0; - } + if (!test_bit(CONNECTED, &vif->flags)) + goto fail_tx; - if (WARN_ON_ONCE(ar->state != ATH6KL_STATE_ON)) { - dev_kfree_skb(skb); - return 0; - } + if (WARN_ON_ONCE(ar->state != ATH6KL_STATE_ON)) + goto fail_tx; if (!test_bit(WMI_READY, &ar->flag)) goto fail_tx; @@ -490,6 +490,7 @@ int ath6kl_data_tx(struct sk_buff *skb, struct net_device *dev) cookie->map_no = map_no; set_htc_pkt_info(&cookie->htc_pkt, cookie, skb->data, skb->len, eid, htc_tag); + cookie->htc_pkt.skb = skb; ath6kl_dbg_dump(ATH6KL_DBG_RAW_BYTES, __func__, "tx ", skb->data, skb->len); @@ -570,7 +571,7 @@ void ath6kl_indicate_tx_activity(void *devt, u8 traffic_class, bool active) notify_htc: /* notify HTC, this may cause credit distribution changes */ - ath6kl_htc_indicate_activity_change(ar->htc_target, eid, active); + ath6kl_htc_activity_changed(ar->htc_target, eid, active); } enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, @@ -666,9 +667,10 @@ static void ath6kl_tx_clear_node_map(struct ath6kl_vif *vif, } } -void ath6kl_tx_complete(void *context, struct list_head *packet_queue) +void ath6kl_tx_complete(struct htc_target *target, + struct list_head *packet_queue) { - struct ath6kl *ar = context; + struct ath6kl *ar = target->dev->ar; struct sk_buff_head skb_queue; struct htc_packet *packet; struct sk_buff *skb; @@ -887,6 +889,7 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) skb->data = PTR_ALIGN(skb->data - 4, 4); set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_BUFFER_SIZE, endpoint); + packet->skb = skb; list_add_tail(&packet->list, &queue); } @@ -909,6 +912,8 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count) skb->data = PTR_ALIGN(skb->data - 4, 4); set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_AMSDU_BUFFER_SIZE, 0); + packet->skb = skb; + spin_lock_bh(&ar->lock); list_add_tail(&packet->list, &ar->amsdu_rx_buffer_queue); spin_unlock_bh(&ar->lock); @@ -1281,6 +1286,7 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) struct wmi_data_hdr *dhdr; int min_hdr_len; u8 meta_type, dot11_hdr = 0; + u8 pad_before_data_start; int status = packet->status; enum htc_endpoint_id ept = packet->endpoint; bool is_amsdu, prev_ps, ps_state = false; @@ -1492,6 +1498,10 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) seq_no = wmi_data_hdr_get_seqno(dhdr); meta_type = wmi_data_hdr_get_meta(dhdr); dot11_hdr = wmi_data_hdr_get_dot11(dhdr); + pad_before_data_start = + (le16_to_cpu(dhdr->info3) >> WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT) + & WMI_DATA_HDR_PAD_BEFORE_DATA_MASK; + skb_pull(skb, sizeof(struct wmi_data_hdr)); switch (meta_type) { @@ -1510,6 +1520,8 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) break; } + skb_pull(skb, pad_before_data_start); + if (dot11_hdr) status = ath6kl_wmi_dot11_hdr_remove(ar->wmi, skb); else if (!is_amsdu) @@ -1579,7 +1591,8 @@ void ath6kl_rx(struct htc_target *target, struct htc_packet *packet) /* aggregation code will handle the skb */ return; } - } + } else if (!is_broadcast_ether_addr(datap->h_dest)) + vif->net_stats.multicast++; ath6kl_deliver_frames_to_nw_stack(vif->ndev, skb); } diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 325b1224c2b1..3740c3d6ab88 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -21,15 +21,77 @@ #include "debug.h" #include "core.h" +/* constants */ +#define TX_URB_COUNT 32 +#define RX_URB_COUNT 32 +#define ATH6KL_USB_RX_BUFFER_SIZE 1700 + +/* tx/rx pipes for usb */ +enum ATH6KL_USB_PIPE_ID { + ATH6KL_USB_PIPE_TX_CTRL = 0, + ATH6KL_USB_PIPE_TX_DATA_LP, + ATH6KL_USB_PIPE_TX_DATA_MP, + ATH6KL_USB_PIPE_TX_DATA_HP, + ATH6KL_USB_PIPE_RX_CTRL, + ATH6KL_USB_PIPE_RX_DATA, + ATH6KL_USB_PIPE_RX_DATA2, + ATH6KL_USB_PIPE_RX_INT, + ATH6KL_USB_PIPE_MAX +}; + +#define ATH6KL_USB_PIPE_INVALID ATH6KL_USB_PIPE_MAX + +struct ath6kl_usb_pipe { + struct list_head urb_list_head; + struct usb_anchor urb_submitted; + u32 urb_alloc; + u32 urb_cnt; + u32 urb_cnt_thresh; + unsigned int usb_pipe_handle; + u32 flags; + u8 ep_address; + u8 logical_pipe_num; + struct ath6kl_usb *ar_usb; + u16 max_packet_size; + struct work_struct io_complete_work; + struct sk_buff_head io_comp_queue; + struct usb_endpoint_descriptor *ep_desc; +}; + +#define ATH6KL_USB_PIPE_FLAG_TX (1 << 0) + /* usb device object */ struct ath6kl_usb { + /* protects pipe->urb_list_head and pipe->urb_cnt */ + spinlock_t cs_lock; + struct usb_device *udev; struct usb_interface *interface; + struct ath6kl_usb_pipe pipes[ATH6KL_USB_PIPE_MAX]; u8 *diag_cmd_buffer; u8 *diag_resp_buffer; struct ath6kl *ar; }; +/* usb urb object */ +struct ath6kl_urb_context { + struct list_head link; + struct ath6kl_usb_pipe *pipe; + struct sk_buff *skb; + struct ath6kl *ar; +}; + +/* USB endpoint definitions */ +#define ATH6KL_USB_EP_ADDR_APP_CTRL_IN 0x81 +#define ATH6KL_USB_EP_ADDR_APP_DATA_IN 0x82 +#define ATH6KL_USB_EP_ADDR_APP_DATA2_IN 0x83 +#define ATH6KL_USB_EP_ADDR_APP_INT_IN 0x84 + +#define ATH6KL_USB_EP_ADDR_APP_CTRL_OUT 0x01 +#define ATH6KL_USB_EP_ADDR_APP_DATA_LP_OUT 0x02 +#define ATH6KL_USB_EP_ADDR_APP_DATA_MP_OUT 0x03 +#define ATH6KL_USB_EP_ADDR_APP_DATA_HP_OUT 0x04 + /* diagnostic command defnitions */ #define ATH6KL_USB_CONTROL_REQ_SEND_BMI_CMD 1 #define ATH6KL_USB_CONTROL_REQ_RECV_BMI_RESP 2 @@ -55,11 +117,493 @@ struct ath6kl_usb_ctrl_diag_resp_read { __le32 value; } __packed; +/* function declarations */ +static void ath6kl_usb_recv_complete(struct urb *urb); + +#define ATH6KL_USB_IS_BULK_EP(attr) (((attr) & 3) == 0x02) +#define ATH6KL_USB_IS_INT_EP(attr) (((attr) & 3) == 0x03) +#define ATH6KL_USB_IS_ISOC_EP(attr) (((attr) & 3) == 0x01) +#define ATH6KL_USB_IS_DIR_IN(addr) ((addr) & 0x80) + +/* pipe/urb operations */ +static struct ath6kl_urb_context * +ath6kl_usb_alloc_urb_from_pipe(struct ath6kl_usb_pipe *pipe) +{ + struct ath6kl_urb_context *urb_context = NULL; + unsigned long flags; + + spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); + if (!list_empty(&pipe->urb_list_head)) { + urb_context = + list_first_entry(&pipe->urb_list_head, + struct ath6kl_urb_context, link); + list_del(&urb_context->link); + pipe->urb_cnt--; + } + spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); + + return urb_context; +} + +static void ath6kl_usb_free_urb_to_pipe(struct ath6kl_usb_pipe *pipe, + struct ath6kl_urb_context *urb_context) +{ + unsigned long flags; + + spin_lock_irqsave(&pipe->ar_usb->cs_lock, flags); + pipe->urb_cnt++; + + list_add(&urb_context->link, &pipe->urb_list_head); + spin_unlock_irqrestore(&pipe->ar_usb->cs_lock, flags); +} + +static void ath6kl_usb_cleanup_recv_urb(struct ath6kl_urb_context *urb_context) +{ + if (urb_context->skb != NULL) { + dev_kfree_skb(urb_context->skb); + urb_context->skb = NULL; + } + + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); +} + +static inline struct ath6kl_usb *ath6kl_usb_priv(struct ath6kl *ar) +{ + return ar->hif_priv; +} + +/* pipe resource allocation/cleanup */ +static int ath6kl_usb_alloc_pipe_resources(struct ath6kl_usb_pipe *pipe, + int urb_cnt) +{ + struct ath6kl_urb_context *urb_context; + int status = 0, i; + + INIT_LIST_HEAD(&pipe->urb_list_head); + init_usb_anchor(&pipe->urb_submitted); + + for (i = 0; i < urb_cnt; i++) { + urb_context = kzalloc(sizeof(struct ath6kl_urb_context), + GFP_KERNEL); + if (urb_context == NULL) + /* FIXME: set status to -ENOMEM */ + break; + + urb_context->pipe = pipe; + + /* + * we are only allocate the urb contexts here, the actual URB + * is allocated from the kernel as needed to do a transaction + */ + pipe->urb_alloc++; + ath6kl_usb_free_urb_to_pipe(pipe, urb_context); + } + + ath6kl_dbg(ATH6KL_DBG_USB, + "ath6kl usb: alloc resources lpipe:%d hpipe:0x%X urbs:%d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc); + + return status; +} + +static void ath6kl_usb_free_pipe_resources(struct ath6kl_usb_pipe *pipe) +{ + struct ath6kl_urb_context *urb_context; + + if (pipe->ar_usb == NULL) { + /* nothing allocated for this pipe */ + return; + } + + ath6kl_dbg(ATH6KL_DBG_USB, + "ath6kl usb: free resources lpipe:%d" + "hpipe:0x%X urbs:%d avail:%d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc, pipe->urb_cnt); + + if (pipe->urb_alloc != pipe->urb_cnt) { + ath6kl_dbg(ATH6KL_DBG_USB, + "ath6kl usb: urb leak! lpipe:%d" + "hpipe:0x%X urbs:%d avail:%d\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->urb_alloc, pipe->urb_cnt); + } + + while (true) { + urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe); + if (urb_context == NULL) + break; + kfree(urb_context); + } + +} + +static void ath6kl_usb_cleanup_pipe_resources(struct ath6kl_usb *ar_usb) +{ + int i; + + for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) + ath6kl_usb_free_pipe_resources(&ar_usb->pipes[i]); + +} + +static u8 ath6kl_usb_get_logical_pipe_num(struct ath6kl_usb *ar_usb, + u8 ep_address, int *urb_count) +{ + u8 pipe_num = ATH6KL_USB_PIPE_INVALID; + + switch (ep_address) { + case ATH6KL_USB_EP_ADDR_APP_CTRL_IN: + pipe_num = ATH6KL_USB_PIPE_RX_CTRL; + *urb_count = RX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA_IN: + pipe_num = ATH6KL_USB_PIPE_RX_DATA; + *urb_count = RX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_INT_IN: + pipe_num = ATH6KL_USB_PIPE_RX_INT; + *urb_count = RX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA2_IN: + pipe_num = ATH6KL_USB_PIPE_RX_DATA2; + *urb_count = RX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_CTRL_OUT: + pipe_num = ATH6KL_USB_PIPE_TX_CTRL; + *urb_count = TX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA_LP_OUT: + pipe_num = ATH6KL_USB_PIPE_TX_DATA_LP; + *urb_count = TX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA_MP_OUT: + pipe_num = ATH6KL_USB_PIPE_TX_DATA_MP; + *urb_count = TX_URB_COUNT; + break; + case ATH6KL_USB_EP_ADDR_APP_DATA_HP_OUT: + pipe_num = ATH6KL_USB_PIPE_TX_DATA_HP; + *urb_count = TX_URB_COUNT; + break; + default: + /* note: there may be endpoints not currently used */ + break; + } + + return pipe_num; +} + +static int ath6kl_usb_setup_pipe_resources(struct ath6kl_usb *ar_usb) +{ + struct usb_interface *interface = ar_usb->interface; + struct usb_host_interface *iface_desc = interface->cur_altsetting; + struct usb_endpoint_descriptor *endpoint; + struct ath6kl_usb_pipe *pipe; + int i, urbcount, status = 0; + u8 pipe_num; + + ath6kl_dbg(ATH6KL_DBG_USB, "setting up USB Pipes using interface\n"); + + /* walk decriptors and setup pipes */ + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (ATH6KL_USB_IS_BULK_EP(endpoint->bmAttributes)) { + ath6kl_dbg(ATH6KL_DBG_USB, + "%s Bulk Ep:0x%2.2X maxpktsz:%d\n", + ATH6KL_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "RX" : "TX", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize)); + } else if (ATH6KL_USB_IS_INT_EP(endpoint->bmAttributes)) { + ath6kl_dbg(ATH6KL_DBG_USB, + "%s Int Ep:0x%2.2X maxpktsz:%d interval:%d\n", + ATH6KL_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "RX" : "TX", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize), + endpoint->bInterval); + } else if (ATH6KL_USB_IS_ISOC_EP(endpoint->bmAttributes)) { + /* TODO for ISO */ + ath6kl_dbg(ATH6KL_DBG_USB, + "%s ISOC Ep:0x%2.2X maxpktsz:%d interval:%d\n", + ATH6KL_USB_IS_DIR_IN + (endpoint->bEndpointAddress) ? + "RX" : "TX", endpoint->bEndpointAddress, + le16_to_cpu(endpoint->wMaxPacketSize), + endpoint->bInterval); + } + urbcount = 0; + + pipe_num = + ath6kl_usb_get_logical_pipe_num(ar_usb, + endpoint->bEndpointAddress, + &urbcount); + if (pipe_num == ATH6KL_USB_PIPE_INVALID) + continue; + + pipe = &ar_usb->pipes[pipe_num]; + if (pipe->ar_usb != NULL) { + /* hmmm..pipe was already setup */ + continue; + } + + pipe->ar_usb = ar_usb; + pipe->logical_pipe_num = pipe_num; + pipe->ep_address = endpoint->bEndpointAddress; + pipe->max_packet_size = le16_to_cpu(endpoint->wMaxPacketSize); + + if (ATH6KL_USB_IS_BULK_EP(endpoint->bmAttributes)) { + if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvbulkpipe(ar_usb->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndbulkpipe(ar_usb->udev, + pipe->ep_address); + } + } else if (ATH6KL_USB_IS_INT_EP(endpoint->bmAttributes)) { + if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvintpipe(ar_usb->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndintpipe(ar_usb->udev, + pipe->ep_address); + } + } else if (ATH6KL_USB_IS_ISOC_EP(endpoint->bmAttributes)) { + /* TODO for ISO */ + if (ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) { + pipe->usb_pipe_handle = + usb_rcvisocpipe(ar_usb->udev, + pipe->ep_address); + } else { + pipe->usb_pipe_handle = + usb_sndisocpipe(ar_usb->udev, + pipe->ep_address); + } + } + + pipe->ep_desc = endpoint; + + if (!ATH6KL_USB_IS_DIR_IN(pipe->ep_address)) + pipe->flags |= ATH6KL_USB_PIPE_FLAG_TX; + + status = ath6kl_usb_alloc_pipe_resources(pipe, urbcount); + if (status != 0) + break; + } + + return status; +} + +/* pipe operations */ +static void ath6kl_usb_post_recv_transfers(struct ath6kl_usb_pipe *recv_pipe, + int buffer_length) +{ + struct ath6kl_urb_context *urb_context; + struct urb *urb; + int usb_status; + + while (true) { + urb_context = ath6kl_usb_alloc_urb_from_pipe(recv_pipe); + if (urb_context == NULL) + break; + + urb_context->skb = dev_alloc_skb(buffer_length); + if (urb_context->skb == NULL) + goto err_cleanup_urb; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urb == NULL) + goto err_cleanup_urb; + + usb_fill_bulk_urb(urb, + recv_pipe->ar_usb->udev, + recv_pipe->usb_pipe_handle, + urb_context->skb->data, + buffer_length, + ath6kl_usb_recv_complete, urb_context); + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb: bulk recv submit:%d, 0x%X (ep:0x%2.2X), %d bytes buf:0x%p\n", + recv_pipe->logical_pipe_num, + recv_pipe->usb_pipe_handle, recv_pipe->ep_address, + buffer_length, urb_context->skb); + + usb_anchor_urb(urb, &recv_pipe->urb_submitted); + usb_status = usb_submit_urb(urb, GFP_ATOMIC); + + if (usb_status) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb : usb bulk recv failed %d\n", + usb_status); + usb_unanchor_urb(urb); + usb_free_urb(urb); + goto err_cleanup_urb; + } + usb_free_urb(urb); + } + return; + +err_cleanup_urb: + ath6kl_usb_cleanup_recv_urb(urb_context); + return; +} + +static void ath6kl_usb_flush_all(struct ath6kl_usb *ar_usb) +{ + int i; + + for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) { + if (ar_usb->pipes[i].ar_usb != NULL) + usb_kill_anchored_urbs(&ar_usb->pipes[i].urb_submitted); + } + + /* + * Flushing any pending I/O may schedule work this call will block + * until all scheduled work runs to completion. + */ + flush_scheduled_work(); +} + +static void ath6kl_usb_start_recv_pipes(struct ath6kl_usb *ar_usb) +{ + /* + * note: control pipe is no longer used + * ar_usb->pipes[ATH6KL_USB_PIPE_RX_CTRL].urb_cnt_thresh = + * ar_usb->pipes[ATH6KL_USB_PIPE_RX_CTRL].urb_alloc/2; + * ath6kl_usb_post_recv_transfers(&ar_usb-> + * pipes[ATH6KL_USB_PIPE_RX_CTRL], + * ATH6KL_USB_RX_BUFFER_SIZE); + */ + + ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_cnt_thresh = + ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA].urb_alloc / 2; + ath6kl_usb_post_recv_transfers(&ar_usb->pipes[ATH6KL_USB_PIPE_RX_DATA], + ATH6KL_USB_RX_BUFFER_SIZE); +} + +/* hif usb rx/tx completion functions */ +static void ath6kl_usb_recv_complete(struct urb *urb) +{ + struct ath6kl_urb_context *urb_context = urb->context; + struct ath6kl_usb_pipe *pipe = urb_context->pipe; + struct sk_buff *skb = NULL; + int status = 0; + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s: recv pipe: %d, stat:%d, len:%d urb:0x%p\n", __func__, + pipe->logical_pipe_num, urb->status, urb->actual_length, + urb); + + if (urb->status != 0) { + status = -EIO; + switch (urb->status) { + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* + * no need to spew these errors when device + * removed or urb killed due to driver shutdown + */ + status = -ECANCELED; + break; + default: + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s recv pipe: %d (ep:0x%2.2X), failed:%d\n", + __func__, pipe->logical_pipe_num, + pipe->ep_address, urb->status); + break; + } + goto cleanup_recv_urb; + } + + if (urb->actual_length == 0) + goto cleanup_recv_urb; + + skb = urb_context->skb; + + /* we are going to pass it up */ + urb_context->skb = NULL; + skb_put(skb, urb->actual_length); + + /* note: queue implements a lock */ + skb_queue_tail(&pipe->io_comp_queue, skb); + schedule_work(&pipe->io_complete_work); + +cleanup_recv_urb: + ath6kl_usb_cleanup_recv_urb(urb_context); + + if (status == 0 && + pipe->urb_cnt >= pipe->urb_cnt_thresh) { + /* our free urbs are piling up, post more transfers */ + ath6kl_usb_post_recv_transfers(pipe, ATH6KL_USB_RX_BUFFER_SIZE); + } +} + +static void ath6kl_usb_usb_transmit_complete(struct urb *urb) +{ + struct ath6kl_urb_context *urb_context = urb->context; + struct ath6kl_usb_pipe *pipe = urb_context->pipe; + struct sk_buff *skb; + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s: pipe: %d, stat:%d, len:%d\n", + __func__, pipe->logical_pipe_num, urb->status, + urb->actual_length); + + if (urb->status != 0) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s: pipe: %d, failed:%d\n", + __func__, pipe->logical_pipe_num, urb->status); + } + + skb = urb_context->skb; + urb_context->skb = NULL; + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, urb_context); + + /* note: queue implements a lock */ + skb_queue_tail(&pipe->io_comp_queue, skb); + schedule_work(&pipe->io_complete_work); +} + +static void ath6kl_usb_io_comp_work(struct work_struct *work) +{ + struct ath6kl_usb_pipe *pipe = container_of(work, + struct ath6kl_usb_pipe, + io_complete_work); + struct ath6kl_usb *ar_usb; + struct sk_buff *skb; + + ar_usb = pipe->ar_usb; + + while ((skb = skb_dequeue(&pipe->io_comp_queue))) { + if (pipe->flags & ATH6KL_USB_PIPE_FLAG_TX) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb xmit callback buf:0x%p\n", skb); + ath6kl_core_tx_complete(ar_usb->ar, skb); + } else { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb recv callback buf:0x%p\n", skb); + ath6kl_core_rx_complete(ar_usb->ar, skb, + pipe->logical_pipe_num); + } + } +} + #define ATH6KL_USB_MAX_DIAG_CMD (sizeof(struct ath6kl_usb_ctrl_diag_cmd_write)) #define ATH6KL_USB_MAX_DIAG_RESP (sizeof(struct ath6kl_usb_ctrl_diag_resp_read)) static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb) { + ath6kl_usb_flush_all(ar_usb); + + ath6kl_usb_cleanup_pipe_resources(ar_usb); + usb_set_intfdata(ar_usb->interface, NULL); kfree(ar_usb->diag_cmd_buffer); @@ -70,19 +614,28 @@ static void ath6kl_usb_destroy(struct ath6kl_usb *ar_usb) static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface) { - struct ath6kl_usb *ar_usb = NULL; struct usb_device *dev = interface_to_usbdev(interface); + struct ath6kl_usb *ar_usb; + struct ath6kl_usb_pipe *pipe; int status = 0; + int i; ar_usb = kzalloc(sizeof(struct ath6kl_usb), GFP_KERNEL); if (ar_usb == NULL) goto fail_ath6kl_usb_create; - memset(ar_usb, 0, sizeof(struct ath6kl_usb)); usb_set_intfdata(interface, ar_usb); + spin_lock_init(&(ar_usb->cs_lock)); ar_usb->udev = dev; ar_usb->interface = interface; + for (i = 0; i < ATH6KL_USB_PIPE_MAX; i++) { + pipe = &ar_usb->pipes[i]; + INIT_WORK(&pipe->io_complete_work, + ath6kl_usb_io_comp_work); + skb_queue_head_init(&pipe->io_comp_queue); + } + ar_usb->diag_cmd_buffer = kzalloc(ATH6KL_USB_MAX_DIAG_CMD, GFP_KERNEL); if (ar_usb->diag_cmd_buffer == NULL) { status = -ENOMEM; @@ -96,6 +649,8 @@ static struct ath6kl_usb *ath6kl_usb_create(struct usb_interface *interface) goto fail_ath6kl_usb_create; } + status = ath6kl_usb_setup_pipe_resources(ar_usb); + fail_ath6kl_usb_create: if (status != 0) { ath6kl_usb_destroy(ar_usb); @@ -114,11 +669,177 @@ static void ath6kl_usb_device_detached(struct usb_interface *interface) ath6kl_stop_txrx(ar_usb->ar); + /* Delay to wait for the target to reboot */ + mdelay(20); ath6kl_core_cleanup(ar_usb->ar); - ath6kl_usb_destroy(ar_usb); } +/* exported hif usb APIs for htc pipe */ +static void hif_start(struct ath6kl *ar) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + int i; + + ath6kl_usb_start_recv_pipes(device); + + /* set the TX resource avail threshold for each TX pipe */ + for (i = ATH6KL_USB_PIPE_TX_CTRL; + i <= ATH6KL_USB_PIPE_TX_DATA_HP; i++) { + device->pipes[i].urb_cnt_thresh = + device->pipes[i].urb_alloc / 2; + } +} + +static int ath6kl_usb_send(struct ath6kl *ar, u8 PipeID, + struct sk_buff *hdr_skb, struct sk_buff *skb) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + struct ath6kl_usb_pipe *pipe = &device->pipes[PipeID]; + struct ath6kl_urb_context *urb_context; + int usb_status, status = 0; + struct urb *urb; + u8 *data; + u32 len; + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, "+%s pipe : %d, buf:0x%p\n", + __func__, PipeID, skb); + + urb_context = ath6kl_usb_alloc_urb_from_pipe(pipe); + + if (urb_context == NULL) { + /* + * TODO: it is possible to run out of urbs if + * 2 endpoints map to the same pipe ID + */ + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "%s pipe:%d no urbs left. URB Cnt : %d\n", + __func__, PipeID, pipe->urb_cnt); + status = -ENOMEM; + goto fail_hif_send; + } + + urb_context->skb = skb; + + data = skb->data; + len = skb->len; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (urb == NULL) { + status = -ENOMEM; + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, + urb_context); + goto fail_hif_send; + } + + usb_fill_bulk_urb(urb, + device->udev, + pipe->usb_pipe_handle, + data, + len, + ath6kl_usb_usb_transmit_complete, urb_context); + + if ((len % pipe->max_packet_size) == 0) { + /* hit a max packet boundary on this pipe */ + urb->transfer_flags |= URB_ZERO_PACKET; + } + + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "athusb bulk send submit:%d, 0x%X (ep:0x%2.2X), %d bytes\n", + pipe->logical_pipe_num, pipe->usb_pipe_handle, + pipe->ep_address, len); + + usb_anchor_urb(urb, &pipe->urb_submitted); + usb_status = usb_submit_urb(urb, GFP_ATOMIC); + + if (usb_status) { + ath6kl_dbg(ATH6KL_DBG_USB_BULK, + "ath6kl usb : usb bulk transmit failed %d\n", + usb_status); + usb_unanchor_urb(urb); + ath6kl_usb_free_urb_to_pipe(urb_context->pipe, + urb_context); + status = -EINVAL; + } + usb_free_urb(urb); + +fail_hif_send: + return status; +} + +static void hif_stop(struct ath6kl *ar) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + + ath6kl_usb_flush_all(device); +} + +static void ath6kl_usb_get_default_pipe(struct ath6kl *ar, + u8 *ul_pipe, u8 *dl_pipe) +{ + *ul_pipe = ATH6KL_USB_PIPE_TX_CTRL; + *dl_pipe = ATH6KL_USB_PIPE_RX_CTRL; +} + +static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id, + u8 *ul_pipe, u8 *dl_pipe) +{ + int status = 0; + + switch (svc_id) { + case HTC_CTRL_RSVD_SVC: + case WMI_CONTROL_SVC: + *ul_pipe = ATH6KL_USB_PIPE_TX_CTRL; + /* due to large control packets, shift to data pipe */ + *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; + break; + case WMI_DATA_BE_SVC: + case WMI_DATA_BK_SVC: + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP; + /* + * Disable rxdata2 directly, it will be enabled + * if FW enable rxdata2 + */ + *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; + break; + case WMI_DATA_VI_SVC: + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; + /* + * Disable rxdata2 directly, it will be enabled + * if FW enable rxdata2 + */ + *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; + break; + case WMI_DATA_VO_SVC: + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_HP; + /* + * Disable rxdata2 directly, it will be enabled + * if FW enable rxdata2 + */ + *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; + break; + default: + status = -EPERM; + break; + } + + return status; +} + +static u16 ath6kl_usb_get_free_queue_number(struct ath6kl *ar, u8 pipe_id) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + + return device->pipes[pipe_id].urb_cnt; +} + +static void hif_detach_htc(struct ath6kl *ar) +{ + struct ath6kl_usb *device = ath6kl_usb_priv(ar); + + ath6kl_usb_flush_all(device); +} + static int ath6kl_usb_submit_ctrl_out(struct ath6kl_usb *ar_usb, u8 req, u16 value, u16 index, void *data, u32 size) @@ -301,14 +1022,29 @@ static int ath6kl_usb_bmi_write(struct ath6kl *ar, u8 *buf, u32 len) static int ath6kl_usb_power_on(struct ath6kl *ar) { + hif_start(ar); return 0; } static int ath6kl_usb_power_off(struct ath6kl *ar) { + hif_detach_htc(ar); return 0; } +static void ath6kl_usb_stop(struct ath6kl *ar) +{ + hif_stop(ar); +} + +static void ath6kl_usb_cleanup_scatter(struct ath6kl *ar) +{ + /* + * USB doesn't support it. Just return. + */ + return; +} + static const struct ath6kl_hif_ops ath6kl_usb_ops = { .diag_read32 = ath6kl_usb_diag_read32, .diag_write32 = ath6kl_usb_diag_write32, @@ -316,6 +1052,12 @@ static const struct ath6kl_hif_ops ath6kl_usb_ops = { .bmi_write = ath6kl_usb_bmi_write, .power_on = ath6kl_usb_power_on, .power_off = ath6kl_usb_power_off, + .stop = ath6kl_usb_stop, + .pipe_send = ath6kl_usb_send, + .pipe_get_default = ath6kl_usb_get_default_pipe, + .pipe_map_service = ath6kl_usb_map_service_pipe, + .pipe_get_free_queue_number = ath6kl_usb_get_free_queue_number, + .cleanup_scatter = ath6kl_usb_cleanup_scatter, }; /* ath6kl usb driver registered functions */ @@ -368,7 +1110,7 @@ static int ath6kl_usb_probe(struct usb_interface *interface, ar_usb->ar = ar; - ret = ath6kl_core_init(ar); + ret = ath6kl_core_init(ar, ATH6KL_HTC_TYPE_PIPE); if (ret) { ath6kl_err("Failed to init ath6kl core: %d\n", ret); goto err_core_free; @@ -392,6 +1134,46 @@ static void ath6kl_usb_remove(struct usb_interface *interface) ath6kl_usb_device_detached(interface); } +#ifdef CONFIG_PM + +static int ath6kl_usb_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct ath6kl_usb *device; + device = usb_get_intfdata(interface); + + ath6kl_usb_flush_all(device); + return 0; +} + +static int ath6kl_usb_resume(struct usb_interface *interface) +{ + struct ath6kl_usb *device; + device = usb_get_intfdata(interface); + + ath6kl_usb_post_recv_transfers(&device->pipes[ATH6KL_USB_PIPE_RX_DATA], + ATH6KL_USB_RX_BUFFER_SIZE); + ath6kl_usb_post_recv_transfers(&device->pipes[ATH6KL_USB_PIPE_RX_DATA2], + ATH6KL_USB_RX_BUFFER_SIZE); + + return 0; +} + +static int ath6kl_usb_reset_resume(struct usb_interface *intf) +{ + if (usb_get_intfdata(intf)) + ath6kl_usb_remove(intf); + return 0; +} + +#else + +#define ath6kl_usb_suspend NULL +#define ath6kl_usb_resume NULL +#define ath6kl_usb_reset_resume NULL + +#endif + /* table of devices that work with this driver */ static struct usb_device_id ath6kl_usb_ids[] = { {USB_DEVICE(0x0cf3, 0x9374)}, @@ -403,8 +1185,13 @@ MODULE_DEVICE_TABLE(usb, ath6kl_usb_ids); static struct usb_driver ath6kl_usb_driver = { .name = "ath6kl_usb", .probe = ath6kl_usb_probe, + .suspend = ath6kl_usb_suspend, + .resume = ath6kl_usb_resume, + .reset_resume = ath6kl_usb_reset_resume, .disconnect = ath6kl_usb_remove, .id_table = ath6kl_usb_ids, + .supports_autosuspend = true, + .disable_hub_initiated_lpm = 1, }; static int ath6kl_usb_init(void) @@ -430,3 +1217,6 @@ MODULE_FIRMWARE(AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 2b442332cd0f..ee8ec2394c2c 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -16,6 +16,7 @@ */ #include <linux/ip.h> +#include <linux/in.h> #include "core.h" #include "debug.h" #include "testmode.h" @@ -289,6 +290,13 @@ int ath6kl_wmi_implicit_create_pstream(struct wmi *wmi, u8 if_idx, layer2_priority); } else usr_pri = layer2_priority & 0x7; + + /* + * Queue the EAPOL frames in the same WMM_AC_VO queue + * as that of management frames. + */ + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) + usr_pri = WMI_VOICE_USER_PRIORITY; } /* @@ -460,8 +468,9 @@ static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap, freq, dur); chan = ieee80211_get_channel(ar->wiphy, freq); if (!chan) { - ath6kl_dbg(ATH6KL_DBG_WMI, "remain_on_chnl: Unknown channel " - "(freq=%u)\n", freq); + ath6kl_dbg(ATH6KL_DBG_WMI, + "remain_on_chnl: Unknown channel (freq=%u)\n", + freq); return -EINVAL; } id = vif->last_roc_id; @@ -488,12 +497,14 @@ static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi, ev = (struct wmi_cancel_remain_on_chnl_event *) datap; freq = le32_to_cpu(ev->freq); dur = le32_to_cpu(ev->duration); - ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: freq=%u dur=%u " - "status=%u\n", freq, dur, ev->status); + ath6kl_dbg(ATH6KL_DBG_WMI, + "cancel_remain_on_chnl: freq=%u dur=%u status=%u\n", + freq, dur, ev->status); chan = ieee80211_get_channel(ar->wiphy, freq); if (!chan) { - ath6kl_dbg(ATH6KL_DBG_WMI, "cancel_remain_on_chnl: Unknown " - "channel (freq=%u)\n", freq); + ath6kl_dbg(ATH6KL_DBG_WMI, + "cancel_remain_on_chnl: Unknown channel (freq=%u)\n", + freq); return -EINVAL; } if (vif->last_cancel_roc_id && @@ -548,12 +559,12 @@ static int ath6kl_wmi_rx_probe_req_event_rx(struct wmi *wmi, u8 *datap, int len, freq = le32_to_cpu(ev->freq); dlen = le16_to_cpu(ev->len); if (datap + len < ev->data + dlen) { - ath6kl_err("invalid wmi_p2p_rx_probe_req_event: " - "len=%d dlen=%u\n", len, dlen); + ath6kl_err("invalid wmi_p2p_rx_probe_req_event: len=%d dlen=%u\n", + len, dlen); return -EINVAL; } - ath6kl_dbg(ATH6KL_DBG_WMI, "rx_probe_req: len=%u freq=%u " - "probe_req_report=%d\n", + ath6kl_dbg(ATH6KL_DBG_WMI, + "rx_probe_req: len=%u freq=%u probe_req_report=%d\n", dlen, freq, vif->probe_req_report); if (vif->probe_req_report || vif->nw_type == AP_NETWORK) @@ -592,8 +603,8 @@ static int ath6kl_wmi_rx_action_event_rx(struct wmi *wmi, u8 *datap, int len, freq = le32_to_cpu(ev->freq); dlen = le16_to_cpu(ev->len); if (datap + len < ev->data + dlen) { - ath6kl_err("invalid wmi_rx_action_event: " - "len=%d dlen=%u\n", len, dlen); + ath6kl_err("invalid wmi_rx_action_event: len=%d dlen=%u\n", + len, dlen); return -EINVAL; } ath6kl_dbg(ATH6KL_DBG_WMI, "rx_action: len=%u freq=%u\n", dlen, freq); @@ -687,7 +698,7 @@ static int ath6kl_wmi_ready_event_rx(struct wmi *wmi, u8 *datap, int len) ath6kl_ready_event(wmi->parent_dev, ev->mac_addr, le32_to_cpu(ev->sw_version), - le32_to_cpu(ev->abi_version)); + le32_to_cpu(ev->abi_version), ev->phy_cap); return 0; } @@ -777,16 +788,15 @@ static int ath6kl_wmi_connect_event_rx(struct wmi *wmi, u8 *datap, int len, /* AP mode start/STA connected event */ struct net_device *dev = vif->ndev; if (memcmp(dev->dev_addr, ev->u.ap_bss.bssid, ETH_ALEN) == 0) { - ath6kl_dbg(ATH6KL_DBG_WMI, "%s: freq %d bssid %pM " - "(AP started)\n", + ath6kl_dbg(ATH6KL_DBG_WMI, + "%s: freq %d bssid %pM (AP started)\n", __func__, le16_to_cpu(ev->u.ap_bss.ch), ev->u.ap_bss.bssid); ath6kl_connect_ap_mode_bss( vif, le16_to_cpu(ev->u.ap_bss.ch)); } else { - ath6kl_dbg(ATH6KL_DBG_WMI, "%s: aid %u mac_addr %pM " - "auth=%u keymgmt=%u cipher=%u apsd_info=%u " - "(STA connected)\n", + ath6kl_dbg(ATH6KL_DBG_WMI, + "%s: aid %u mac_addr %pM auth=%u keymgmt=%u cipher=%u apsd_info=%u (STA connected)\n", __func__, ev->u.ap_sta.aid, ev->u.ap_sta.mac_addr, ev->u.ap_sta.auth, @@ -1229,8 +1239,9 @@ static int ath6kl_wmi_neighbor_report_event_rx(struct wmi *wmi, u8 *datap, ev = (struct wmi_neighbor_report_event *) datap; if (sizeof(*ev) + ev->num_neighbors * sizeof(struct wmi_neighbor_info) > len) { - ath6kl_dbg(ATH6KL_DBG_WMI, "truncated neighbor event " - "(num=%d len=%d)\n", ev->num_neighbors, len); + ath6kl_dbg(ATH6KL_DBG_WMI, + "truncated neighbor event (num=%d len=%d)\n", + ev->num_neighbors, len); return -EINVAL; } for (i = 0; i < ev->num_neighbors; i++) { @@ -1814,12 +1825,14 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, u32 home_dwell_time, u32 force_scan_interval, s8 num_chan, u16 *ch_list, u32 no_cck, u32 *rates) { + struct ieee80211_supported_band *sband; struct sk_buff *skb; struct wmi_begin_scan_cmd *sc; - s8 size; + s8 size, *supp_rates; int i, band, ret; struct ath6kl *ar = wmi->parent_dev; int num_rates; + u32 ratemask; size = sizeof(struct wmi_begin_scan_cmd); @@ -1846,10 +1859,13 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, sc->num_ch = num_chan; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - struct ieee80211_supported_band *sband = - ar->wiphy->bands[band]; - u32 ratemask = rates[band]; - u8 *supp_rates = sc->supp_rates[band].rates; + sband = ar->wiphy->bands[band]; + + if (!sband) + continue; + + ratemask = rates[band]; + supp_rates = sc->supp_rates[band].rates; num_rates = 0; for (i = 0; i < sband->n_bitrates; i++) { @@ -2129,8 +2145,8 @@ int ath6kl_wmi_addkey_cmd(struct wmi *wmi, u8 if_idx, u8 key_index, struct wmi_add_cipher_key_cmd *cmd; int ret; - ath6kl_dbg(ATH6KL_DBG_WMI, "addkey cmd: key_index=%u key_type=%d " - "key_usage=%d key_len=%d key_op_ctrl=%d\n", + ath6kl_dbg(ATH6KL_DBG_WMI, + "addkey cmd: key_index=%u key_type=%d key_usage=%d key_len=%d key_op_ctrl=%d\n", key_index, key_type, key_usage, key_len, key_op_ctrl); if ((key_index > WMI_MAX_KEY_INDEX) || (key_len > WMI_MAX_KEY_LEN) || @@ -2882,6 +2898,43 @@ int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, return ret; } +int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx, + enum ieee80211_band band, + struct ath6kl_htcap *htcap) +{ + struct sk_buff *skb; + struct wmi_set_htcap_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_htcap_cmd *) skb->data; + + /* + * NOTE: Band in firmware matches enum ieee80211_band, it is unlikely + * this will be changed in firmware. If at all there is any change in + * band value, the host needs to be fixed. + */ + cmd->band = band; + cmd->ht_enable = !!htcap->ht_enable; + cmd->ht20_sgi = !!(htcap->cap_info & IEEE80211_HT_CAP_SGI_20); + cmd->ht40_supported = + !!(htcap->cap_info & IEEE80211_HT_CAP_SUP_WIDTH_20_40); + cmd->ht40_sgi = !!(htcap->cap_info & IEEE80211_HT_CAP_SGI_40); + cmd->intolerant_40mhz = + !!(htcap->cap_info & IEEE80211_HT_CAP_40MHZ_INTOLERANT); + cmd->max_ampdu_len_exp = htcap->ampdu_factor; + + ath6kl_dbg(ATH6KL_DBG_WMI, + "Set htcap: band:%d ht_enable:%d 40mhz:%d sgi_20mhz:%d sgi_40mhz:%d 40mhz_intolerant:%d ampdu_len_exp:%d\n", + cmd->band, cmd->ht_enable, cmd->ht40_supported, + cmd->ht20_sgi, cmd->ht40_sgi, cmd->intolerant_40mhz, + cmd->max_ampdu_len_exp); + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_HT_CAP_CMDID, + NO_SYNC_WMIFLAG); +} + int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len) { struct sk_buff *skb; @@ -3010,8 +3063,8 @@ int ath6kl_wmi_ap_profile_commit(struct wmi *wmip, u8 if_idx, res = ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_CONFIG_COMMIT_CMDID, NO_SYNC_WMIFLAG); - ath6kl_dbg(ATH6KL_DBG_WMI, "%s: nw_type=%u auth_mode=%u ch=%u " - "ctrl_flags=0x%x-> res=%d\n", + ath6kl_dbg(ATH6KL_DBG_WMI, + "%s: nw_type=%u auth_mode=%u ch=%u ctrl_flags=0x%x-> res=%d\n", __func__, p->nw_type, p->auth_mode, le16_to_cpu(p->ch), le32_to_cpu(p->ctrl_flags), res); return res; @@ -3032,6 +3085,9 @@ int ath6kl_wmi_ap_set_mlme(struct wmi *wmip, u8 if_idx, u8 cmd, const u8 *mac, cm->reason = cpu_to_le16(reason); cm->cmd = cmd; + ath6kl_dbg(ATH6KL_DBG_WMI, "ap_set_mlme: cmd=%d reason=%d\n", cm->cmd, + cm->reason); + return ath6kl_wmi_cmd_send(wmip, if_idx, skb, WMI_AP_SET_MLME_CMDID, NO_SYNC_WMIFLAG); } @@ -3168,8 +3224,9 @@ int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, if (!skb) return -ENOMEM; - ath6kl_dbg(ATH6KL_DBG_WMI, "set_appie_cmd: mgmt_frm_type=%u " - "ie_len=%u\n", mgmt_frm_type, ie_len); + ath6kl_dbg(ATH6KL_DBG_WMI, + "set_appie_cmd: mgmt_frm_type=%u ie_len=%u\n", + mgmt_frm_type, ie_len); p = (struct wmi_set_appie_cmd *) skb->data; p->mgmt_frm_type = mgmt_frm_type; p->ie_len = ie_len; @@ -3181,6 +3238,29 @@ int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, NO_SYNC_WMIFLAG); } +int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field, + const u8 *ie_info, u8 ie_len) +{ + struct sk_buff *skb; + struct wmi_set_ie_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p) + ie_len); + if (!skb) + return -ENOMEM; + + ath6kl_dbg(ATH6KL_DBG_WMI, "set_ie_cmd: ie_id=%u ie_ie_field=%u ie_len=%u\n", + ie_id, ie_field, ie_len); + p = (struct wmi_set_ie_cmd *) skb->data; + p->ie_id = ie_id; + p->ie_field = ie_field; + p->ie_len = ie_len; + if (ie_info && ie_len > 0) + memcpy(p->ie_info, ie_info, ie_len); + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_IE_CMDID, + NO_SYNC_WMIFLAG); +} + int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable) { struct sk_buff *skb; @@ -3247,8 +3327,9 @@ static int ath6kl_wmi_send_action_cmd(struct wmi *wmi, u8 if_idx, u32 id, wmi->last_mgmt_tx_frame = buf; wmi->last_mgmt_tx_frame_len = data_len; - ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u " - "len=%u\n", id, freq, wait, data_len); + ath6kl_dbg(ATH6KL_DBG_WMI, + "send_action_cmd: id=%u freq=%u wait=%u len=%u\n", + id, freq, wait, data_len); p = (struct wmi_send_action_cmd *) skb->data; p->id = cpu_to_le32(id); p->freq = cpu_to_le32(freq); @@ -3285,8 +3366,9 @@ static int __ath6kl_wmi_send_mgmt_cmd(struct wmi *wmi, u8 if_idx, u32 id, wmi->last_mgmt_tx_frame = buf; wmi->last_mgmt_tx_frame_len = data_len; - ath6kl_dbg(ATH6KL_DBG_WMI, "send_action_cmd: id=%u freq=%u wait=%u " - "len=%u\n", id, freq, wait, data_len); + ath6kl_dbg(ATH6KL_DBG_WMI, + "send_action_cmd: id=%u freq=%u wait=%u len=%u\n", + id, freq, wait, data_len); p = (struct wmi_send_mgmt_cmd *) skb->data; p->id = cpu_to_le32(id); p->freq = cpu_to_le32(freq); @@ -3339,8 +3421,9 @@ int ath6kl_wmi_send_probe_response_cmd(struct wmi *wmi, u8 if_idx, u32 freq, if (!skb) return -ENOMEM; - ath6kl_dbg(ATH6KL_DBG_WMI, "send_probe_response_cmd: freq=%u dst=%pM " - "len=%u\n", freq, dst, data_len); + ath6kl_dbg(ATH6KL_DBG_WMI, + "send_probe_response_cmd: freq=%u dst=%pM len=%u\n", + freq, dst, data_len); p = (struct wmi_p2p_probe_response_cmd *) skb->data; p->freq = cpu_to_le32(freq); memcpy(p->destination_addr, dst, ETH_ALEN); @@ -3392,6 +3475,23 @@ int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx) WMI_CANCEL_REMAIN_ON_CHNL_CMDID); } +int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout) +{ + struct sk_buff *skb; + struct wmi_set_inact_period_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_inact_period_cmd *) skb->data; + cmd->inact_period = cpu_to_le32(inact_timeout); + cmd->num_null_func = 0; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_AP_CONN_INACT_CMDID, + NO_SYNC_WMIFLAG); +} + static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) { struct wmix_cmd_hdr *cmd; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 4092e3e80790..9076bec3a2ba 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -106,6 +106,8 @@ struct wmi_data_sync_bufs { #define WMM_AC_VI 2 /* video */ #define WMM_AC_VO 3 /* voice */ +#define WMI_VOICE_USER_PRIORITY 0x7 + struct wmi { u16 stream_exist_for_ac[WMM_NUM_AC]; u8 fat_pipe_exist; @@ -182,6 +184,9 @@ enum wmi_data_hdr_flags { #define WMI_DATA_HDR_META_MASK 0x7 #define WMI_DATA_HDR_META_SHIFT 13 +#define WMI_DATA_HDR_PAD_BEFORE_DATA_MASK 0xFF +#define WMI_DATA_HDR_PAD_BEFORE_DATA_SHIFT 0x8 + /* Macros for operating on WMI_DATA_HDR (info3) field */ #define WMI_DATA_HDR_IF_IDX_MASK 0xF @@ -423,6 +428,7 @@ enum wmi_cmd_id { WMI_SET_FRAMERATES_CMDID, WMI_SET_AP_PS_CMDID, WMI_SET_QOS_SUPP_CMDID, + WMI_SET_IE_CMDID, /* WMI_THIN_RESERVED_... mark the start and end * values for WMI_THIN_RESERVED command IDs. These @@ -629,6 +635,11 @@ enum wmi_mgmt_frame_type { WMI_NUM_MGMT_FRAME }; +enum wmi_ie_field_type { + WMI_RSN_IE_CAPB = 0x1, + WMI_IE_FULL = 0xFF, /* indicats full IE */ +}; + /* WMI_CONNECT_CMDID */ enum network_type { INFRA_NETWORK = 0x01, @@ -1142,6 +1153,7 @@ enum wmi_phy_mode { WMI_11AG_MODE = 0x3, WMI_11B_MODE = 0x4, WMI_11GONLY_MODE = 0x5, + WMI_11G_HT20 = 0x6, }; #define WMI_MAX_CHANNELS 32 @@ -1268,6 +1280,16 @@ struct wmi_mcast_filter_add_del_cmd { u8 mcast_mac[ATH6KL_MCAST_FILTER_MAC_ADDR_SIZE]; } __packed; +struct wmi_set_htcap_cmd { + u8 band; + u8 ht_enable; + u8 ht40_supported; + u8 ht20_sgi; + u8 ht40_sgi; + u8 intolerant_40mhz; + u8 max_ampdu_len_exp; +} __packed; + /* Command Replies */ /* WMI_GET_CHANNEL_LIST_CMDID reply */ @@ -1397,6 +1419,16 @@ struct wmi_ready_event_2 { u8 phy_cap; } __packed; +/* WMI_PHY_CAPABILITY */ +enum wmi_phy_cap { + WMI_11A_CAP = 0x01, + WMI_11G_CAP = 0x02, + WMI_11AG_CAP = 0x03, + WMI_11AN_CAP = 0x04, + WMI_11GN_CAP = 0x05, + WMI_11AGN_CAP = 0x06, +}; + /* Connect Event */ struct wmi_connect_event { union { @@ -1449,6 +1481,17 @@ enum wmi_disconnect_reason { IBSS_MERGE = 0xe, }; +/* AP mode disconnect proto_reasons */ +enum ap_disconnect_reason { + WMI_AP_REASON_STA_LEFT = 101, + WMI_AP_REASON_FROM_HOST = 102, + WMI_AP_REASON_COMM_TIMEOUT = 103, + WMI_AP_REASON_MAX_STA = 104, + WMI_AP_REASON_ACL = 105, + WMI_AP_REASON_STA_ROAM = 106, + WMI_AP_REASON_DFS_CHANNEL = 107, +}; + #define ATH6KL_COUNTRY_RD_SHIFT 16 struct ath6kl_wmi_regdomain { @@ -1913,6 +1956,14 @@ struct wmi_set_appie_cmd { u8 ie_info[0]; } __packed; +struct wmi_set_ie_cmd { + u8 ie_id; + u8 ie_field; /* enum wmi_ie_field_type */ + u8 ie_len; + u8 reserved; + u8 ie_info[0]; +} __packed; + /* Notify the WSC registration status to the target */ #define WSC_REG_ACTIVE 1 #define WSC_REG_INACTIVE 0 @@ -2141,6 +2192,11 @@ struct wmi_ap_hidden_ssid_cmd { u8 hidden_ssid; } __packed; +struct wmi_set_inact_period_cmd { + __le32 inact_period; + u8 num_null_func; +} __packed; + /* AP mode events */ struct wmi_ap_set_apsd_cmd { u8 enable; @@ -2465,6 +2521,9 @@ int ath6kl_wmi_get_roam_tbl_cmd(struct wmi *wmi); int ath6kl_wmi_set_wmm_txop(struct wmi *wmi, u8 if_idx, enum wmi_txop_cfg cfg); int ath6kl_wmi_set_keepalive_cmd(struct wmi *wmi, u8 if_idx, u8 keep_alive_intvl); +int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx, + enum ieee80211_band band, + struct ath6kl_htcap *htcap); int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len); s32 ath6kl_wmi_get_rate(s8 rate_index); @@ -2515,6 +2574,9 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx, int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, const u8 *ie, u8 ie_len); +int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field, + const u8 *ie_info, u8 ie_len); + /* P2P */ int ath6kl_wmi_disable_11b_rates_cmd(struct wmi *wmi, bool disable); @@ -2538,6 +2600,8 @@ int ath6kl_wmi_cancel_remain_on_chnl_cmd(struct wmi *wmi, u8 if_idx); int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, const u8 *ie, u8 ie_len); +int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout); + void ath6kl_wmi_sscan_timer(unsigned long ptr); struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx); diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 27d95fe5ade0..3f0b84723789 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -11,7 +11,10 @@ ath9k-$(CONFIG_ATH9K_PCI) += pci.o 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 +ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += \ + dfs.o \ + dfs_pattern_detector.o \ + dfs_pri_detector.o obj-$(CONFIG_ATH9K) += ath9k.o diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index 7e0ea4e98334..b4c77f9d7470 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -46,8 +46,8 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = { { 5, 4, 1 }, /* lvl 5 */ { 6, 5, 1 }, /* lvl 6 */ { 7, 6, 1 }, /* lvl 7 */ - { 7, 7, 1 }, /* lvl 8 */ - { 7, 8, 0 } /* lvl 9 */ + { 7, 6, 0 }, /* lvl 8 */ + { 7, 7, 0 } /* lvl 9 */ }; #define ATH9K_ANI_OFDM_NUM_LEVEL \ ARRAY_SIZE(ofdm_level_table) @@ -91,8 +91,8 @@ static const struct ani_cck_level_entry cck_level_table[] = { { 4, 0 }, /* lvl 4 */ { 5, 0 }, /* lvl 5 */ { 6, 0 }, /* lvl 6 */ - { 7, 0 }, /* lvl 7 (only for high rssi) */ - { 8, 0 } /* lvl 8 (only for high rssi) */ + { 6, 0 }, /* lvl 7 (only for high rssi) */ + { 7, 0 } /* lvl 8 (only for high rssi) */ }; #define ATH9K_ANI_CCK_NUM_LEVEL \ @@ -274,7 +274,9 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) aniState->rssiThrLow, aniState->rssiThrHigh); if (aniState->update_ani) - aniState->ofdmNoiseImmunityLevel = immunityLevel; + aniState->ofdmNoiseImmunityLevel = + (immunityLevel > ATH9K_ANI_OFDM_DEF_LEVEL) ? + immunityLevel : ATH9K_ANI_OFDM_DEF_LEVEL; entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel]; entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel]; @@ -290,16 +292,9 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) ATH9K_ANI_FIRSTEP_LEVEL, entry_ofdm->fir_step_level); - if ((ah->opmode != NL80211_IFTYPE_STATION && - ah->opmode != NL80211_IFTYPE_ADHOC) || - aniState->noiseFloor <= aniState->rssiThrHigh) { - if (aniState->ofdmWeakSigDetectOff) - /* force on ofdm weak sig detect */ - ath9k_hw_ani_control(ah, - ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - true); - else if (aniState->ofdmWeakSigDetectOff == - entry_ofdm->ofdm_weak_signal_on) + if ((aniState->noiseFloor >= aniState->rssiThrHigh) && + (!aniState->ofdmWeakSigDetectOff != + entry_ofdm->ofdm_weak_signal_on)) { ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, entry_ofdm->ofdm_weak_signal_on); @@ -347,7 +342,9 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI; if (aniState->update_ani) - aniState->cckNoiseImmunityLevel = immunityLevel; + aniState->cckNoiseImmunityLevel = + (immunityLevel > ATH9K_ANI_CCK_DEF_LEVEL) ? + immunityLevel : ATH9K_ANI_CCK_DEF_LEVEL; entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel]; entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel]; @@ -717,26 +714,30 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan) ofdmPhyErrRate, aniState->cckNoiseImmunityLevel, cckPhyErrRate, aniState->ofdmsTurn); - if (aniState->listenTime > 5 * ah->aniperiod) { - if (ofdmPhyErrRate <= ah->config.ofdm_trig_low && - cckPhyErrRate <= ah->config.cck_trig_low) { + if (aniState->listenTime > ah->aniperiod) { + if (cckPhyErrRate < ah->config.cck_trig_low && + ((ofdmPhyErrRate < ah->config.ofdm_trig_low && + aniState->ofdmNoiseImmunityLevel < + ATH9K_ANI_OFDM_DEF_LEVEL) || + (ofdmPhyErrRate < ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI && + aniState->ofdmNoiseImmunityLevel >= + ATH9K_ANI_OFDM_DEF_LEVEL))) { ath9k_hw_ani_lower_immunity(ah); aniState->ofdmsTurn = !aniState->ofdmsTurn; - } - ath9k_ani_restart(ah); - } else if (aniState->listenTime > ah->aniperiod) { - /* check to see if need to raise immunity */ - if (ofdmPhyErrRate > ah->config.ofdm_trig_high && - (cckPhyErrRate <= ah->config.cck_trig_high || - aniState->ofdmsTurn)) { + } else if ((ofdmPhyErrRate > ah->config.ofdm_trig_high && + aniState->ofdmNoiseImmunityLevel >= + ATH9K_ANI_OFDM_DEF_LEVEL) || + (ofdmPhyErrRate > + ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI && + aniState->ofdmNoiseImmunityLevel < + ATH9K_ANI_OFDM_DEF_LEVEL)) { ath9k_hw_ani_ofdm_err_trigger(ah); - ath9k_ani_restart(ah); aniState->ofdmsTurn = false; } else if (cckPhyErrRate > ah->config.cck_trig_high) { ath9k_hw_ani_cck_err_trigger(ah); - ath9k_ani_restart(ah); aniState->ofdmsTurn = true; } + ath9k_ani_restart(ah); } } EXPORT_SYMBOL(ath9k_hw_ani_monitor); diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index 83029d6c7b22..72e2b874e179 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -25,11 +25,13 @@ /* units are errors per second */ #define ATH9K_ANI_OFDM_TRIG_HIGH_OLD 500 -#define ATH9K_ANI_OFDM_TRIG_HIGH_NEW 1000 +#define ATH9K_ANI_OFDM_TRIG_HIGH_NEW 3500 +#define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000 /* units are errors per second */ #define ATH9K_ANI_OFDM_TRIG_LOW_OLD 200 #define ATH9K_ANI_OFDM_TRIG_LOW_NEW 400 +#define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900 /* units are errors per second */ #define ATH9K_ANI_CCK_TRIG_HIGH_OLD 200 @@ -53,7 +55,7 @@ #define ATH9K_ANI_RSSI_THR_LOW 7 #define ATH9K_ANI_PERIOD_OLD 100 -#define ATH9K_ANI_PERIOD_NEW 1000 +#define ATH9K_ANI_PERIOD_NEW 300 /* in ms */ #define ATH9K_ANI_POLLINTERVAL_OLD 100 diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index aba088005b22..c7492c6a2519 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -245,7 +245,6 @@ static int ar5008_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) REG_WRITE(ah, AR_PHY(0x37), reg32); ah->curchan = chan; - ah->curchan_rad_index = -1; return 0; } @@ -619,19 +618,10 @@ static void ar5008_hw_init_bb(struct ath_hw *ah, u32 synthDelay; synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; - if (IS_CHAN_B(chan)) - synthDelay = (4 * synthDelay) / 22; - else - synthDelay /= 10; - - if (IS_CHAN_HALF_RATE(chan)) - synthDelay *= 2; - else if (IS_CHAN_QUARTER_RATE(chan)) - synthDelay *= 4; REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); - udelay(synthDelay + BASE_ACTIVATE_DELAY); + ath9k_hw_synth_delay(ah, chan, synthDelay); } static void ar5008_hw_init_chain_masks(struct ath_hw *ah) @@ -949,12 +939,8 @@ static bool ar5008_hw_rfbus_req(struct ath_hw *ah) static void ar5008_hw_rfbus_done(struct ath_hw *ah) { u32 synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; - if (IS_CHAN_B(ah->curchan)) - synthDelay = (4 * synthDelay) / 22; - else - synthDelay /= 10; - udelay(synthDelay + BASE_ACTIVATE_DELAY); + ath9k_hw_synth_delay(ah, ah->curchan, synthDelay); REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); } @@ -1047,46 +1033,8 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah, break; } case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{ - static const int m1ThreshLow[] = { 127, 50 }; - static const int m2ThreshLow[] = { 127, 40 }; - static const int m1Thresh[] = { 127, 0x4d }; - static const int m2Thresh[] = { 127, 0x40 }; - static const int m2CountThr[] = { 31, 16 }; - static const int m2CountThrLow[] = { 63, 48 }; u32 on = param ? 1 : 0; - REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, - AR_PHY_SFCORR_LOW_M1_THRESH_LOW, - m1ThreshLow[on]); - REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, - AR_PHY_SFCORR_LOW_M2_THRESH_LOW, - m2ThreshLow[on]); - REG_RMW_FIELD(ah, AR_PHY_SFCORR, - AR_PHY_SFCORR_M1_THRESH, - m1Thresh[on]); - REG_RMW_FIELD(ah, AR_PHY_SFCORR, - AR_PHY_SFCORR_M2_THRESH, - m2Thresh[on]); - REG_RMW_FIELD(ah, AR_PHY_SFCORR, - AR_PHY_SFCORR_M2COUNT_THR, - m2CountThr[on]); - REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, - AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, - m2CountThrLow[on]); - - REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, - AR_PHY_SFCORR_EXT_M1_THRESH_LOW, - m1ThreshLow[on]); - REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, - AR_PHY_SFCORR_EXT_M2_THRESH_LOW, - m2ThreshLow[on]); - REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, - AR_PHY_SFCORR_EXT_M1_THRESH, - m1Thresh[on]); - REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, - AR_PHY_SFCORR_EXT_M2_THRESH, - m2Thresh[on]); - if (on) REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c index aa2abaf31cba..8d78253c26ce 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c @@ -136,6 +136,7 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) } if (sync_cause) { + ath9k_debug_sync_cause(common, sync_cause); fatal_int = (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index 3cbbb033fcea..846dd7974eb8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -152,7 +152,6 @@ static int ar9002_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) REG_WRITE(ah, AR_PHY_SYNTH_CONTROL, reg32); ah->curchan = chan; - ah->curchan_rad_index = -1; return 0; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h index 46c79a3d4737..952cb2b4656b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h @@ -777,11 +777,11 @@ static const u32 ar9300Common_rx_gain_table_2p2[][2] = { {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, - {0x0000a080, 0x22222229}, - {0x0000a084, 0x1d1d1d1d}, - {0x0000a088, 0x1d1d1d1d}, - {0x0000a08c, 0x1d1d1d1d}, - {0x0000a090, 0x171d1d1d}, + {0x0000a080, 0x1a1a1a1a}, + {0x0000a084, 0x1a1a1a1a}, + {0x0000a088, 0x1a1a1a1a}, + {0x0000a08c, 0x1a1a1a1a}, + {0x0000a090, 0x171a1a1a}, {0x0000a094, 0x11111717}, {0x0000a098, 0x00030311}, {0x0000a09c, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 63089cc1fafd..9fdd70fcaf5b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -892,34 +892,6 @@ static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah) AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); } -static bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan) -{ - struct ath9k_rtt_hist *hist; - u32 *table; - int i; - bool restore; - - if (!ah->caldata) - return false; - - hist = &ah->caldata->rtt_hist; - if (!hist->num_readings) - return false; - - ar9003_hw_rtt_enable(ah); - ar9003_hw_rtt_set_mask(ah, 0x00); - for (i = 0; i < AR9300_MAX_CHAINS; i++) { - if (!(ah->rxchainmask & (1 << i))) - continue; - table = &hist->table[i][hist->num_readings][0]; - ar9003_hw_rtt_load_hist(ah, i, table); - } - restore = ar9003_hw_rtt_force_restore(ah); - ar9003_hw_rtt_disable(ah); - - return restore; -} - static bool ar9003_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -942,9 +914,10 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, if (!ar9003_hw_rtt_restore(ah, chan)) run_rtt_cal = true; - ath_dbg(common, CALIBRATE, "RTT restore %s\n", - run_rtt_cal ? "failed" : "succeed"); + if (run_rtt_cal) + ath_dbg(common, CALIBRATE, "RTT calibration to be done\n"); } + run_agc_cal = run_rtt_cal; if (run_rtt_cal) { @@ -1000,10 +973,12 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, if (mci && IS_CHAN_2GHZ(chan) && run_agc_cal) ar9003_mci_init_cal_req(ah, &is_reusable); - txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); - REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); - udelay(5); - REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + if (!(IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan))) { + txiqcal_done = ar9003_hw_tx_iq_cal_run(ah); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_DIS); + udelay(5); + REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); + } skip_tx_iqcal: if (run_agc_cal || !(ah->ah_flags & AH_FASTCC)) { @@ -1067,17 +1042,14 @@ skip_tx_iqcal: #undef CL_TAB_ENTRY if (run_rtt_cal && caldata) { - struct ath9k_rtt_hist *hist = &caldata->rtt_hist; - if (is_reusable && (hist->num_readings < RTT_HIST_MAX)) { - u32 *table; + if (is_reusable) { + if (!ath9k_hw_rfbus_req(ah)) + ath_err(ath9k_hw_common(ah), + "Could not stop baseband\n"); + else + ar9003_hw_rtt_fill_hist(ah); - hist->num_readings++; - for (i = 0; i < AR9300_MAX_CHAINS; i++) { - if (!(ah->rxchainmask & (1 << i))) - continue; - table = &hist->table[i][hist->num_readings][0]; - ar9003_hw_rtt_fill_hist(ah, i, table); - } + ath9k_hw_rfbus_done(ah); } ar9003_hw_rtt_disable(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 6bb4db052bb0..dfb0441f406c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -30,11 +30,6 @@ #define CTL_11A_EXT (CTL_11A | EXT_ADDITIVE) #define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) #define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) -#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ -#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 9 /* 10*log10(3)*2 */ -#define PWRINCR_3_TO_1_CHAIN 9 /* 10*log(3)*2 */ -#define PWRINCR_3_TO_2_CHAIN 3 /* floor(10*log(3/2)*2) */ -#define PWRINCR_2_TO_1_CHAIN 6 /* 10*log(2)*2 */ #define SUB_NUM_CTL_MODES_AT_5G_40 2 /* excluding HT40, EXT-OFDM */ #define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */ @@ -2936,15 +2931,6 @@ static const struct ar9300_eeprom *ar9003_eeprom_struct_find_by_id(int id) #undef N_LOOP } - -static u16 ath9k_hw_fbin2freq(u8 fbin, bool is2GHz) -{ - if (fbin == AR5416_BCHAN_UNUSED) - return fbin; - - return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); -} - static int ath9k_hw_ar9300_check_eeprom(struct ath_hw *ah) { return 0; @@ -3823,7 +3809,7 @@ static bool is_pmu_set(struct ath_hw *ah, u32 pmu_reg, int pmu_set) return true; } -static void ar9003_hw_internal_regulator_apply(struct ath_hw *ah) +void ar9003_hw_internal_regulator_apply(struct ath_hw *ah) { int internal_regulator = ath9k_hw_ar9300_get_eeprom(ah, EEP_INTERNAL_REGULATOR); @@ -4070,7 +4056,7 @@ static u8 ar9003_hw_eeprom_get_tgt_pwr(struct ath_hw *ah, * targetpower piers stored on eeprom */ for (i = 0; i < numPiers; i++) { - freqArray[i] = FBIN2FREQ(pFreqBin[i], is2GHz); + freqArray[i] = ath9k_hw_fbin2freq(pFreqBin[i], is2GHz); targetPowerArray[i] = pEepromTargetPwr[i].tPow2x[rateIndex]; } @@ -4106,7 +4092,7 @@ static u8 ar9003_hw_eeprom_get_ht20_tgt_pwr(struct ath_hw *ah, * from targetpower piers stored on eeprom */ for (i = 0; i < numPiers; i++) { - freqArray[i] = FBIN2FREQ(pFreqBin[i], is2GHz); + freqArray[i] = ath9k_hw_fbin2freq(pFreqBin[i], is2GHz); targetPowerArray[i] = pEepromTargetPwr[i].tPow2x[rateIndex]; } @@ -4142,7 +4128,7 @@ static u8 ar9003_hw_eeprom_get_ht40_tgt_pwr(struct ath_hw *ah, * targetpower piers stored on eeprom */ for (i = 0; i < numPiers; i++) { - freqArray[i] = FBIN2FREQ(pFreqBin[i], is2GHz); + freqArray[i] = ath9k_hw_fbin2freq(pFreqBin[i], is2GHz); targetPowerArray[i] = pEepromTargetPwr[i].tPow2x[rateIndex]; } @@ -4167,7 +4153,7 @@ static u8 ar9003_hw_eeprom_get_cck_tgt_pwr(struct ath_hw *ah, * targetpower piers stored on eeprom */ for (i = 0; i < numPiers; i++) { - freqArray[i] = FBIN2FREQ(pFreqBin[i], 1); + freqArray[i] = ath9k_hw_fbin2freq(pFreqBin[i], 1); targetPowerArray[i] = pEepromTargetPwr[i].tPow2x[rateIndex]; } @@ -4295,18 +4281,10 @@ static int ar9003_hw_tx_power_regwrite(struct ath_hw *ah, u8 * pPwrArray) #undef POW_SM } -static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq, - u8 *targetPowerValT2) +static void ar9003_hw_get_legacy_target_powers(struct ath_hw *ah, u16 freq, + u8 *targetPowerValT2, + bool is2GHz) { - /* XXX: hard code for now, need to get from eeprom struct */ - u8 ht40PowerIncForPdadc = 0; - bool is2GHz = false; - unsigned int i = 0; - struct ath_common *common = ath9k_hw_common(ah); - - if (freq < 4000) - is2GHz = true; - targetPowerValT2[ALL_TARGET_LEGACY_6_24] = ar9003_hw_eeprom_get_tgt_pwr(ah, LEGACY_TARGET_RATE_6_24, freq, is2GHz); @@ -4319,6 +4297,11 @@ static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq, targetPowerValT2[ALL_TARGET_LEGACY_54] = ar9003_hw_eeprom_get_tgt_pwr(ah, LEGACY_TARGET_RATE_54, freq, is2GHz); +} + +static void ar9003_hw_get_cck_target_powers(struct ath_hw *ah, u16 freq, + u8 *targetPowerValT2) +{ targetPowerValT2[ALL_TARGET_LEGACY_1L_5L] = ar9003_hw_eeprom_get_cck_tgt_pwr(ah, LEGACY_TARGET_RATE_1L_5L, freq); @@ -4328,6 +4311,11 @@ static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq, ar9003_hw_eeprom_get_cck_tgt_pwr(ah, LEGACY_TARGET_RATE_11L, freq); targetPowerValT2[ALL_TARGET_LEGACY_11S] = ar9003_hw_eeprom_get_cck_tgt_pwr(ah, LEGACY_TARGET_RATE_11S, freq); +} + +static void ar9003_hw_get_ht20_target_powers(struct ath_hw *ah, u16 freq, + u8 *targetPowerValT2, bool is2GHz) +{ targetPowerValT2[ALL_TARGET_HT20_0_8_16] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_0_8_16, freq, is2GHz); @@ -4370,6 +4358,16 @@ static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq, targetPowerValT2[ALL_TARGET_HT20_23] = ar9003_hw_eeprom_get_ht20_tgt_pwr(ah, HT_TARGET_RATE_23, freq, is2GHz); +} + +static void ar9003_hw_get_ht40_target_powers(struct ath_hw *ah, + u16 freq, + u8 *targetPowerValT2, + bool is2GHz) +{ + /* XXX: hard code for now, need to get from eeprom struct */ + u8 ht40PowerIncForPdadc = 0; + targetPowerValT2[ALL_TARGET_HT40_0_8_16] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_0_8_16, freq, is2GHz) + ht40PowerIncForPdadc; @@ -4413,6 +4411,26 @@ static void ar9003_hw_set_target_power_eeprom(struct ath_hw *ah, u16 freq, targetPowerValT2[ALL_TARGET_HT40_23] = ar9003_hw_eeprom_get_ht40_tgt_pwr(ah, HT_TARGET_RATE_23, freq, is2GHz) + ht40PowerIncForPdadc; +} + +static void ar9003_hw_get_target_power_eeprom(struct ath_hw *ah, + struct ath9k_channel *chan, + u8 *targetPowerValT2) +{ + bool is2GHz = IS_CHAN_2GHZ(chan); + unsigned int i = 0; + struct ath_common *common = ath9k_hw_common(ah); + u16 freq = chan->channel; + + if (is2GHz) + ar9003_hw_get_cck_target_powers(ah, freq, targetPowerValT2); + + ar9003_hw_get_legacy_target_powers(ah, freq, targetPowerValT2, is2GHz); + ar9003_hw_get_ht20_target_powers(ah, freq, targetPowerValT2, is2GHz); + + if (IS_CHAN_HT40(chan)) + ar9003_hw_get_ht40_target_powers(ah, freq, targetPowerValT2, + is2GHz); for (i = 0; i < ar9300RateSize; i++) { ath_dbg(common, EEPROM, "TPC[%02d] 0x%08x\n", @@ -4464,7 +4482,7 @@ static int ar9003_hw_cal_pier_get(struct ath_hw *ah, is2GHz = 1; } - *pfrequency = FBIN2FREQ(*pCalPier, is2GHz); + *pfrequency = ath9k_hw_fbin2freq(*pCalPier, is2GHz); *pcorrection = pCalPierStruct->refPower; *ptemperature = pCalPierStruct->tempMeas; *pvoltage = pCalPierStruct->voltMeas; @@ -4789,34 +4807,9 @@ static void ar9003_hw_set_power_per_rate_table(struct ath_hw *ah, bool is2ghz = IS_CHAN_2GHZ(chan); ath9k_hw_get_channel_centers(ah, chan, ¢ers); - scaledPower = powerLimit - antenna_reduction; - - /* - * Reduce scaled Power by number of chains active to get - * to per chain tx power level - */ - switch (ar5416_get_ntxchains(ah->txchainmask)) { - case 1: - break; - case 2: - if (scaledPower > REDUCE_SCALED_POWER_BY_TWO_CHAIN) - scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN; - else - scaledPower = 0; - break; - case 3: - if (scaledPower > REDUCE_SCALED_POWER_BY_THREE_CHAIN) - scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN; - else - scaledPower = 0; - break; - } + scaledPower = ath9k_hw_get_scaled_power(ah, powerLimit, + antenna_reduction); - scaledPower = max((u16)0, scaledPower); - - /* - * Get target powers from EEPROM - our baseline for TX Power - */ if (is2ghz) { /* Setup for CTL modes */ /* CTL_11B, CTL_11G, CTL_2GHT20 */ @@ -4988,7 +4981,12 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, unsigned int i = 0, paprd_scale_factor = 0; u8 pwr_idx, min_pwridx = 0; - ar9003_hw_set_target_power_eeprom(ah, chan->channel, targetPowerValT2); + memset(targetPowerValT2, 0 , sizeof(targetPowerValT2)); + + /* + * Get target powers from EEPROM - our baseline for TX Power + */ + ar9003_hw_get_target_power_eeprom(ah, chan, targetPowerValT2); if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) { if (IS_CHAN_2GHZ(chan)) @@ -5060,8 +5058,6 @@ static void ath9k_hw_ar9300_set_txpower(struct ath_hw *ah, i, targetPowerValT2[i]); } - ah->txpower_limit = regulatory->max_power_level; - /* Write target power array to registers */ ar9003_hw_tx_power_regwrite(ah, targetPowerValT2); ar9003_hw_calibration_apply(ah, chan->channel); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h index bb223fe82816..8396d150ce01 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h @@ -42,7 +42,6 @@ #define AR9300_EEPMISC_WOW 0x02 #define AR9300_CUSTOMER_DATA_SIZE 20 -#define FBIN2FREQ(x, y) ((y) ? (2300 + x) : (4800 + 5 * x)) #define AR9300_MAX_CHAINS 3 #define AR9300_ANT_16S 25 #define AR9300_FUTURE_MODAL_SZ 6 @@ -335,4 +334,7 @@ u8 *ar9003_get_spur_chan_ptr(struct ath_hw *ah, bool is_2ghz); unsigned int ar9003_get_paprd_scale_factor(struct ath_hw *ah, struct ath9k_channel *chan); + +void ar9003_hw_internal_regulator_apply(struct ath_hw *ah); + #endif diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 0f56e322dd3b..a0e3394b10dc 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -305,11 +305,6 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9462_common_rx_gain_table_2p0, ARRAY_SIZE(ar9462_common_rx_gain_table_2p0), 2); - INIT_INI_ARRAY(&ah->ini_BTCOEX_MAX_TXPWR, - ar9462_2p0_BTCOEX_MAX_TXPWR_table, - ARRAY_SIZE(ar9462_2p0_BTCOEX_MAX_TXPWR_table), - 2); - /* Awake -> Sleep Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdes, PCIE_PLL_ON_CREQ_DIS_L1_2P0, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index a66a13b76848..d9e0824af093 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -306,6 +306,8 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) ar9003_mci_get_isr(ah, masked); if (sync_cause) { + ath9k_debug_sync_cause(common, sync_cause); + if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) { REG_WRITE(ah, AR_RC, AR_RC_HOSTIF); REG_WRITE(ah, AR_RC, 0); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 3cac293a2849..ffbb180f91e1 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -756,7 +756,7 @@ int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (caldata) { caldata->done_txiqcal_once = false; caldata->done_txclcal_once = false; - caldata->rtt_hist.num_readings = 0; + caldata->rtt_done = false; } if (!ath9k_hw_init_cal(ah, chan)) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index deb6cfb2959a..11abb972be1f 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -152,7 +152,6 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) REG_WRITE(ah, AR_PHY_65NM_CH0_SYNTH7, reg32); ah->curchan = chan; - ah->curchan_rad_index = -1; return 0; } @@ -209,11 +208,12 @@ static void ar9003_hw_spur_mitigate_mrc_cck(struct ath_hw *ah, continue; negative = 0; if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah)) - cur_bb_spur = FBIN2FREQ(spur_fbin_ptr[i], - IS_CHAN_2GHZ(chan)) - synth_freq; + cur_bb_spur = ath9k_hw_fbin2freq(spur_fbin_ptr[i], + IS_CHAN_2GHZ(chan)); else - cur_bb_spur = spur_freq[i] - synth_freq; + cur_bb_spur = spur_freq[i]; + cur_bb_spur -= synth_freq; if (cur_bb_spur < 0) { negative = 1; cur_bb_spur = -cur_bb_spur; @@ -373,7 +373,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah, else spur_subchannel_sd = 0; - spur_freq_sd = (freq_offset << 9) / 11; + spur_freq_sd = ((freq_offset + 10) << 9) / 11; } else { if (REG_READ_FIELD(ah, AR_PHY_GEN_CTRL, @@ -382,7 +382,7 @@ static void ar9003_hw_spur_ofdm_work(struct ath_hw *ah, else spur_subchannel_sd = 1; - spur_freq_sd = (freq_offset << 9) / 11; + spur_freq_sd = ((freq_offset - 10) << 9) / 11; } @@ -443,7 +443,8 @@ static void ar9003_hw_spur_mitigate_ofdm(struct ath_hw *ah, ar9003_hw_spur_ofdm_clear(ah); for (i = 0; i < AR_EEPROM_MODAL_SPURS && spurChansPtr[i]; i++) { - freq_offset = FBIN2FREQ(spurChansPtr[i], mode) - synth_freq; + freq_offset = ath9k_hw_fbin2freq(spurChansPtr[i], mode); + freq_offset -= synth_freq; if (abs(freq_offset) < range) { ar9003_hw_spur_ofdm_work(ah, chan, freq_offset); break; @@ -525,22 +526,10 @@ static void ar9003_hw_init_bb(struct ath_hw *ah, * Value is in 100ns increments. */ synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; - if (IS_CHAN_B(chan)) - synthDelay = (4 * synthDelay) / 22; - else - synthDelay /= 10; /* Activate the PHY (includes baseband activate + synthesizer on) */ REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN); - - /* - * There is an issue if the AP starts the calibration before - * the base band timeout completes. This could result in the - * rx_clear false triggering. As a workaround we add delay an - * extra BASE_ACTIVATE_DELAY usecs to ensure this condition - * does not happen. - */ - udelay(synthDelay + BASE_ACTIVATE_DELAY); + ath9k_hw_synth_delay(ah, chan, synthDelay); } static void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx) @@ -684,9 +673,6 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites); - if (AR_SREV_9462(ah)) - ar9003_hw_prog_ini(ah, &ah->ini_BTCOEX_MAX_TXPWR, 1); - if (chan->channel == 2484) ar9003_hw_prog_ini(ah, &ah->ini_japan2484, 1); @@ -725,6 +711,14 @@ static void ar9003_hw_set_rfmode(struct ath_hw *ah, if (IS_CHAN_A_FAST_CLOCK(ah, chan)) rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); + if (IS_CHAN_QUARTER_RATE(chan)) + rfMode |= AR_PHY_MODE_QUARTER; + if (IS_CHAN_HALF_RATE(chan)) + rfMode |= AR_PHY_MODE_HALF; + + if (rfMode & (AR_PHY_MODE_QUARTER | AR_PHY_MODE_HALF)) + REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL, + AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW, 3); REG_WRITE(ah, AR_PHY_MODE, rfMode); } @@ -795,12 +789,8 @@ static bool ar9003_hw_rfbus_req(struct ath_hw *ah) static void ar9003_hw_rfbus_done(struct ath_hw *ah) { u32 synthDelay = REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY; - if (IS_CHAN_B(ah->curchan)) - synthDelay = (4 * synthDelay) / 22; - else - synthDelay /= 10; - udelay(synthDelay + BASE_ACTIVATE_DELAY); + ath9k_hw_synth_delay(ah, ah->curchan, synthDelay); REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); } @@ -823,55 +813,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah, * on == 0 means more noise imm */ u32 on = param ? 1 : 0; - /* - * make register setting for default - * (weak sig detect ON) come from INI file - */ - int m1ThreshLow = on ? - aniState->iniDef.m1ThreshLow : m1ThreshLow_off; - int m2ThreshLow = on ? - aniState->iniDef.m2ThreshLow : m2ThreshLow_off; - int m1Thresh = on ? - aniState->iniDef.m1Thresh : m1Thresh_off; - int m2Thresh = on ? - aniState->iniDef.m2Thresh : m2Thresh_off; - int m2CountThr = on ? - aniState->iniDef.m2CountThr : m2CountThr_off; - int m2CountThrLow = on ? - aniState->iniDef.m2CountThrLow : m2CountThrLow_off; - int m1ThreshLowExt = on ? - aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off; - int m2ThreshLowExt = on ? - aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off; - int m1ThreshExt = on ? - aniState->iniDef.m1ThreshExt : m1ThreshExt_off; - int m2ThreshExt = on ? - aniState->iniDef.m2ThreshExt : m2ThreshExt_off; - - REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, - AR_PHY_SFCORR_LOW_M1_THRESH_LOW, - m1ThreshLow); - REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, - AR_PHY_SFCORR_LOW_M2_THRESH_LOW, - m2ThreshLow); - REG_RMW_FIELD(ah, AR_PHY_SFCORR, - AR_PHY_SFCORR_M1_THRESH, m1Thresh); - REG_RMW_FIELD(ah, AR_PHY_SFCORR, - AR_PHY_SFCORR_M2_THRESH, m2Thresh); - REG_RMW_FIELD(ah, AR_PHY_SFCORR, - AR_PHY_SFCORR_M2COUNT_THR, m2CountThr); - REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW, - AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, - m2CountThrLow); - - REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, - AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLowExt); - REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, - AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLowExt); - REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, - AR_PHY_SFCORR_EXT_M1_THRESH, m1ThreshExt); - REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, - AR_PHY_SFCORR_EXT_M2_THRESH, m2ThreshExt); if (on) REG_SET_BIT(ah, AR_PHY_SFCORR_LOW, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index d834d97fe727..7268a48a92a1 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -468,6 +468,9 @@ #define AR_PHY_ADDAC_PARA_CTL (AR_SM_BASE + 0x150) #define AR_PHY_XPA_CFG (AR_SM_BASE + 0x158) +#define AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW 3 +#define AR_PHY_FRAME_CTL_CF_OVERLAP_WINDOW_S 0 + #define AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A 0x0001FC00 #define AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_IDX_A_S 10 #define AR_PHY_SPUR_MASK_A_CF_PUNC_MASK_A 0x3FF diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c index 458bedf0b0ae..74de3539c2c8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_rtt.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.c @@ -15,6 +15,7 @@ */ #include "hw.h" +#include "hw-ops.h" #include "ar9003_phy.h" #include "ar9003_rtt.h" @@ -69,7 +70,7 @@ bool ar9003_hw_rtt_force_restore(struct ath_hw *ah) } static void ar9003_hw_rtt_load_hist_entry(struct ath_hw *ah, u8 chain, - u32 index, u32 data28) + u32 index, u32 data28) { u32 val; @@ -100,12 +101,21 @@ static void ar9003_hw_rtt_load_hist_entry(struct ath_hw *ah, u8 chain, RTT_ACCESS_TIMEOUT); } -void ar9003_hw_rtt_load_hist(struct ath_hw *ah, u8 chain, u32 *table) +void ar9003_hw_rtt_load_hist(struct ath_hw *ah) { - int i; + int chain, i; - for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) - ar9003_hw_rtt_load_hist_entry(ah, chain, i, table[i]); + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { + if (!(ah->rxchainmask & (1 << chain))) + continue; + for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) { + ar9003_hw_rtt_load_hist_entry(ah, chain, i, + ah->caldata->rtt_table[chain][i]); + ath_dbg(ath9k_hw_common(ah), CALIBRATE, + "Load RTT value at idx %d, chain %d: 0x%x\n", + i, chain, ah->caldata->rtt_table[chain][i]); + } + } } static int ar9003_hw_rtt_fill_hist_entry(struct ath_hw *ah, u8 chain, u32 index) @@ -128,27 +138,71 @@ static int ar9003_hw_rtt_fill_hist_entry(struct ath_hw *ah, u8 chain, u32 index) RTT_ACCESS_TIMEOUT)) return RTT_BAD_VALUE; - val = REG_READ(ah, AR_PHY_RTT_TABLE_SW_INTF_1_B(chain)); + val = MS(REG_READ(ah, AR_PHY_RTT_TABLE_SW_INTF_1_B(chain)), + AR_PHY_RTT_SW_RTT_TABLE_DATA); + return val; } -void ar9003_hw_rtt_fill_hist(struct ath_hw *ah, u8 chain, u32 *table) +void ar9003_hw_rtt_fill_hist(struct ath_hw *ah) { - int i; + int chain, i; + + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { + if (!(ah->rxchainmask & (1 << chain))) + continue; + for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) { + ah->caldata->rtt_table[chain][i] = + ar9003_hw_rtt_fill_hist_entry(ah, chain, i); + ath_dbg(ath9k_hw_common(ah), CALIBRATE, + "RTT value at idx %d, chain %d is: 0x%x\n", + i, chain, ah->caldata->rtt_table[chain][i]); + } + } - for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) - table[i] = ar9003_hw_rtt_fill_hist_entry(ah, chain, i); + ah->caldata->rtt_done = true; } void ar9003_hw_rtt_clear_hist(struct ath_hw *ah) { - int i, j; + int chain, i; - for (i = 0; i < AR9300_MAX_CHAINS; i++) { - if (!(ah->rxchainmask & (1 << i))) + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { + if (!(ah->rxchainmask & (1 << chain))) continue; - for (j = 0; j < MAX_RTT_TABLE_ENTRY; j++) - ar9003_hw_rtt_load_hist_entry(ah, i, j, 0); + for (i = 0; i < MAX_RTT_TABLE_ENTRY; i++) + ar9003_hw_rtt_load_hist_entry(ah, chain, i, 0); } + + if (ah->caldata) + ah->caldata->rtt_done = false; +} + +bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan) +{ + bool restore; + + if (!ah->caldata) + return false; + + if (!ah->caldata->rtt_done) + return false; + + ar9003_hw_rtt_enable(ah); + ar9003_hw_rtt_set_mask(ah, 0x10); + + if (!ath9k_hw_rfbus_req(ah)) { + ath_err(ath9k_hw_common(ah), "Could not stop baseband\n"); + restore = false; + goto fail; + } + + ar9003_hw_rtt_load_hist(ah); + restore = ar9003_hw_rtt_force_restore(ah); + +fail: + ath9k_hw_rfbus_done(ah); + ar9003_hw_rtt_disable(ah); + return restore; } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_rtt.h b/drivers/net/wireless/ath/ath9k/ar9003_rtt.h index 030758d087d6..a43b30d723a4 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_rtt.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_rtt.h @@ -21,8 +21,9 @@ void ar9003_hw_rtt_enable(struct ath_hw *ah); void ar9003_hw_rtt_disable(struct ath_hw *ah); void ar9003_hw_rtt_set_mask(struct ath_hw *ah, u32 rtt_mask); bool ar9003_hw_rtt_force_restore(struct ath_hw *ah); -void ar9003_hw_rtt_load_hist(struct ath_hw *ah, u8 chain, u32 *table); -void ar9003_hw_rtt_fill_hist(struct ath_hw *ah, u8 chain, u32 *table); +void ar9003_hw_rtt_load_hist(struct ath_hw *ah); +void ar9003_hw_rtt_fill_hist(struct ath_hw *ah); void ar9003_hw_rtt_clear_hist(struct ath_hw *ah); +bool ar9003_hw_rtt_restore(struct ath_hw *ah, struct ath9k_channel *chan); #endif diff --git a/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h b/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h index f11d9b2677fd..1bd3a3d22101 100644 --- a/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9330_1p1_initvals.h @@ -1,5 +1,6 @@ /* - * Copyright (c) 2011 Atheros Communications Inc. + * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 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 @@ -18,7 +19,7 @@ #define INITVALS_9330_1P1_H static const u32 ar9331_1p1_baseband_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8005, 0xd00a8005}, {0x00009820, 0x206a002e, 0x206a002e, 0x206a002e, 0x206a002e}, {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0}, @@ -27,10 +28,10 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = { {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044}, {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a4, 0x037216a4}, - {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020}, + {0x00009e04, 0x00202020, 0x00202020, 0x00202020, 0x00202020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e, 0x7ec80d2e}, - {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, + {0x00009e14, 0x31365d5e, 0x3136605e, 0x3136605e, 0x31365d5e}, {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, @@ -55,7 +56,7 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = { {0x0000a288, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a28c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, - {0x0000a2d0, 0x00071981, 0x00071981, 0x00071981, 0x00071981}, + {0x0000a2d0, 0x00071982, 0x00071982, 0x00071982, 0x00071982}, {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0xf999a83a, 0xf999a83a}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000ae04, 0x00802020, 0x00802020, 0x00802020, 0x00802020}, @@ -63,7 +64,7 @@ static const u32 ar9331_1p1_baseband_postamble[][5] = { }; static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p1[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52}, {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84}, @@ -155,7 +156,7 @@ static const u32 ar9331_modes_lowest_ob_db_tx_gain_1p1[][5] = { }; static const u32 ar9331_modes_high_ob_db_tx_gain_1p1[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a2dc, 0xffaa9a52, 0xffaa9a52, 0xffaa9a52, 0xffaa9a52}, {0x0000a2e0, 0xffb31c84, 0xffb31c84, 0xffb31c84, 0xffb31c84}, @@ -245,7 +246,7 @@ static const u32 ar9331_modes_high_ob_db_tx_gain_1p1[][5] = { }; static const u32 ar9331_modes_low_ob_db_tx_gain_1p1[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52}, {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84}, @@ -377,14 +378,14 @@ static const u32 ar9331_1p1_radio_core[][2] = { {0x000160b4, 0x92480040}, {0x000160c0, 0x006db6db}, {0x000160c4, 0x0186db60}, - {0x000160c8, 0x6db6db6c}, + {0x000160c8, 0x6db4db6c}, {0x000160cc, 0x6de6c300}, {0x000160d0, 0x14500820}, {0x00016100, 0x04cb0001}, {0x00016104, 0xfff80015}, {0x00016108, 0x00080010}, {0x0001610c, 0x00170000}, - {0x00016140, 0x10804000}, + {0x00016140, 0x10800000}, {0x00016144, 0x01884080}, {0x00016148, 0x000080c0}, {0x00016280, 0x01000015}, @@ -417,7 +418,7 @@ static const u32 ar9331_1p1_radio_core[][2] = { }; static const u32 ar9331_1p1_soc_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00007010, 0x00000022, 0x00000022, 0x00000022, 0x00000022}, }; @@ -691,7 +692,7 @@ static const u32 ar9331_1p1_baseband_core[][2] = { }; static const u32 ar9331_modes_high_power_tx_gain_1p1[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x0000a2d8, 0x7999a83a, 0x7999a83a, 0x7999a83a, 0x7999a83a}, {0x0000a2dc, 0xffff2a52, 0xffff2a52, 0xffff2a52, 0xffff2a52}, {0x0000a2e0, 0xffffcc84, 0xffffcc84, 0xffffcc84, 0xffffcc84}, @@ -783,7 +784,7 @@ static const u32 ar9331_modes_high_power_tx_gain_1p1[][5] = { }; static const u32 ar9331_1p1_mac_postamble[][5] = { - /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x00001030, 0x00000230, 0x00000460, 0x000002c0, 0x00000160}, {0x00001070, 0x00000168, 0x000002d0, 0x00000318, 0x0000018c}, {0x000010b0, 0x00000e60, 0x00001cc0, 0x00007c70, 0x00003e38}, @@ -973,26 +974,27 @@ static const u32 ar9331_1p1_mac_core[][2] = { static const u32 ar9331_common_rx_gain_1p1[][2] = { /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x00830082}, - {0x0000a014, 0x01810180}, - {0x0000a018, 0x01830182}, - {0x0000a01c, 0x01850184}, - {0x0000a020, 0x01890188}, - {0x0000a024, 0x018b018a}, - {0x0000a028, 0x018d018c}, - {0x0000a02c, 0x01910190}, - {0x0000a030, 0x01930192}, - {0x0000a034, 0x01950194}, - {0x0000a038, 0x038a0196}, - {0x0000a03c, 0x038c038b}, - {0x0000a040, 0x0390038d}, - {0x0000a044, 0x03920391}, - {0x0000a048, 0x03940393}, - {0x0000a04c, 0x03960395}, + {0x00009e18, 0x05000000}, + {0x0000a000, 0x00060005}, + {0x0000a004, 0x00810080}, + {0x0000a008, 0x00830082}, + {0x0000a00c, 0x00850084}, + {0x0000a010, 0x01820181}, + {0x0000a014, 0x01840183}, + {0x0000a018, 0x01880185}, + {0x0000a01c, 0x018a0189}, + {0x0000a020, 0x02850284}, + {0x0000a024, 0x02890288}, + {0x0000a028, 0x028b028a}, + {0x0000a02c, 0x03850384}, + {0x0000a030, 0x03890388}, + {0x0000a034, 0x038b038a}, + {0x0000a038, 0x038d038c}, + {0x0000a03c, 0x03910390}, + {0x0000a040, 0x03930392}, + {0x0000a044, 0x03950394}, + {0x0000a048, 0x00000396}, + {0x0000a04c, 0x00000000}, {0x0000a050, 0x00000000}, {0x0000a054, 0x00000000}, {0x0000a058, 0x00000000}, @@ -1005,15 +1007,15 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = { {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, - {0x0000a080, 0x22222229}, - {0x0000a084, 0x1d1d1d1d}, - {0x0000a088, 0x1d1d1d1d}, - {0x0000a08c, 0x1d1d1d1d}, - {0x0000a090, 0x171d1d1d}, - {0x0000a094, 0x11111717}, - {0x0000a098, 0x00030311}, - {0x0000a09c, 0x00000000}, - {0x0000a0a0, 0x00000000}, + {0x0000a080, 0x28282828}, + {0x0000a084, 0x28282828}, + {0x0000a088, 0x28282828}, + {0x0000a08c, 0x28282828}, + {0x0000a090, 0x28282828}, + {0x0000a094, 0x24242428}, + {0x0000a098, 0x171e1e1e}, + {0x0000a09c, 0x02020b0b}, + {0x0000a0a0, 0x02020202}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, @@ -1021,27 +1023,27 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = { {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x01000101}, - {0x0000a0c8, 0x011e011f}, - {0x0000a0cc, 0x011c011d}, - {0x0000a0d0, 0x02030204}, - {0x0000a0d4, 0x02010202}, - {0x0000a0d8, 0x021f0200}, - {0x0000a0dc, 0x0302021e}, - {0x0000a0e0, 0x03000301}, - {0x0000a0e4, 0x031e031f}, - {0x0000a0e8, 0x0402031d}, - {0x0000a0ec, 0x04000401}, - {0x0000a0f0, 0x041e041f}, - {0x0000a0f4, 0x0502041d}, - {0x0000a0f8, 0x05000501}, - {0x0000a0fc, 0x051e051f}, - {0x0000a100, 0x06010602}, - {0x0000a104, 0x061f0600}, - {0x0000a108, 0x061d061e}, - {0x0000a10c, 0x07020703}, - {0x0000a110, 0x07000701}, + {0x0000a0c0, 0x22072208}, + {0x0000a0c4, 0x22052206}, + {0x0000a0c8, 0x22032204}, + {0x0000a0cc, 0x22012202}, + {0x0000a0d0, 0x221f2200}, + {0x0000a0d4, 0x221d221e}, + {0x0000a0d8, 0x33023303}, + {0x0000a0dc, 0x33003301}, + {0x0000a0e0, 0x331e331f}, + {0x0000a0e4, 0x4402331d}, + {0x0000a0e8, 0x44004401}, + {0x0000a0ec, 0x441e441f}, + {0x0000a0f0, 0x55025503}, + {0x0000a0f4, 0x55005501}, + {0x0000a0f8, 0x551e551f}, + {0x0000a0fc, 0x6602551d}, + {0x0000a100, 0x66006601}, + {0x0000a104, 0x661e661f}, + {0x0000a108, 0x7703661d}, + {0x0000a10c, 0x77017702}, + {0x0000a110, 0x00007700}, {0x0000a114, 0x00000000}, {0x0000a118, 0x00000000}, {0x0000a11c, 0x00000000}, @@ -1054,26 +1056,26 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = { {0x0000a138, 0x00000000}, {0x0000a13c, 0x00000000}, {0x0000a140, 0x001f0000}, - {0x0000a144, 0x01000101}, - {0x0000a148, 0x011e011f}, - {0x0000a14c, 0x011c011d}, - {0x0000a150, 0x02030204}, - {0x0000a154, 0x02010202}, - {0x0000a158, 0x021f0200}, - {0x0000a15c, 0x0302021e}, - {0x0000a160, 0x03000301}, - {0x0000a164, 0x031e031f}, - {0x0000a168, 0x0402031d}, - {0x0000a16c, 0x04000401}, - {0x0000a170, 0x041e041f}, - {0x0000a174, 0x0502041d}, - {0x0000a178, 0x05000501}, - {0x0000a17c, 0x051e051f}, - {0x0000a180, 0x06010602}, - {0x0000a184, 0x061f0600}, - {0x0000a188, 0x061d061e}, - {0x0000a18c, 0x07020703}, - {0x0000a190, 0x07000701}, + {0x0000a144, 0x111f1100}, + {0x0000a148, 0x111d111e}, + {0x0000a14c, 0x111b111c}, + {0x0000a150, 0x22032204}, + {0x0000a154, 0x22012202}, + {0x0000a158, 0x221f2200}, + {0x0000a15c, 0x221d221e}, + {0x0000a160, 0x33013302}, + {0x0000a164, 0x331f3300}, + {0x0000a168, 0x4402331e}, + {0x0000a16c, 0x44004401}, + {0x0000a170, 0x441e441f}, + {0x0000a174, 0x55015502}, + {0x0000a178, 0x551f5500}, + {0x0000a17c, 0x6602551e}, + {0x0000a180, 0x66006601}, + {0x0000a184, 0x661e661f}, + {0x0000a188, 0x7703661d}, + {0x0000a18c, 0x77017702}, + {0x0000a190, 0x00007700}, {0x0000a194, 0x00000000}, {0x0000a198, 0x00000000}, {0x0000a19c, 0x00000000}, @@ -1100,14 +1102,14 @@ static const u32 ar9331_common_rx_gain_1p1[][2] = { {0x0000a1f0, 0x00000396}, {0x0000a1f4, 0x00000396}, {0x0000a1f8, 0x00000396}, - {0x0000a1fc, 0x00000196}, + {0x0000a1fc, 0x00000296}, }; static const u32 ar9331_common_tx_gain_offset1_1[][1] = { - {0}, - {3}, - {0}, - {0}, + {0x00000000}, + {0x00000003}, + {0x00000000}, + {0x00000000}, }; static const u32 ar9331_1p1_chansel_xtal_25M[] = { diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index b6ba1e8149be..1d6658e139b5 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -1115,9 +1115,9 @@ static const u32 ar9462_2p0_mac_core[][2] = { {0x000081f8, 0x00000000}, {0x000081fc, 0x00000000}, {0x00008240, 0x00100000}, - {0x00008244, 0x0010f400}, + {0x00008244, 0x0010f424}, {0x00008248, 0x00000800}, - {0x0000824c, 0x0001e800}, + {0x0000824c, 0x0001e848}, {0x00008250, 0x00000000}, {0x00008254, 0x00000000}, {0x00008258, 0x00000000}, @@ -1448,16 +1448,4 @@ static const u32 ar9462_common_mixed_rx_gain_table_2p0[][2] = { {0x0000b1fc, 0x00000196}, }; -static const u32 ar9462_2p0_BTCOEX_MAX_TXPWR_table[][2] = { - /* Addr allmodes */ - {0x000018c0, 0x10101010}, - {0x000018c4, 0x10101010}, - {0x000018c8, 0x10101010}, - {0x000018cc, 0x10101010}, - {0x000018d0, 0x10101010}, - {0x000018d4, 0x10101010}, - {0x000018d8, 0x10101010}, - {0x000018dc, 0x10101010}, -}; - #endif /* INITVALS_9462_2P0_H */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 8c84049682ab..a277cf6f339d 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -26,6 +26,7 @@ #include "debug.h" #include "common.h" #include "mci.h" +#include "dfs.h" /* * Header for the ath9k.ko driver core *only* -- hw code nor any other driver @@ -369,7 +370,7 @@ struct ath_vif { * number of beacon intervals, the game's up. */ #define BSTUCK_THRESH 9 -#define ATH_BCBUF 4 +#define ATH_BCBUF 8 #define ATH_DEFAULT_BINTVAL 100 /* TU */ #define ATH_DEFAULT_BMISS_LIMIT 10 #define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) @@ -430,6 +431,8 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status); void ath_reset_work(struct work_struct *work); void ath_hw_check(struct work_struct *work); void ath_hw_pll_work(struct work_struct *work); +void ath_rx_poll(unsigned long data); +void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon); void ath_paprd_calibrate(struct work_struct *work); void ath_ani_calibrate(unsigned long data); void ath_start_ani(struct ath_common *common); @@ -670,6 +673,7 @@ struct ath_softc { struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; struct delayed_work hw_pll_work; + struct timer_list rx_poll_timer; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex btcoex; @@ -680,6 +684,7 @@ struct ath_softc { struct ath_ant_comb ant_comb; u8 ant_tx, ant_rx; + struct dfs_pattern_detector *dfs_detector; }; void ath9k_tasklet(unsigned long data); diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 626418222c85..11bc55e3d697 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -91,7 +91,7 @@ static void ath_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif, info.txpower = MAX_RATE_POWER; info.keyix = ATH9K_TXKEYIX_INVALID; info.keytype = ATH9K_KEY_TYPE_CLEAR; - info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_INTREQ; + info.flags = ATH9K_TXDESC_NOACK | ATH9K_TXDESC_CLRDMASK; info.buf_addr[0] = bf->bf_buf_addr; info.buf_len[0] = roundup(skb->len, 4); @@ -359,6 +359,11 @@ void ath_beacon_tasklet(unsigned long data) int slot; u32 bfaddr, bc = 0; + if (work_pending(&sc->hw_reset_work)) { + ath_dbg(common, RESET, + "reset work is pending, skip beaconing now\n"); + return; + } /* * Check if the previous beacon has gone out. If * not don't try to post another, skip this period @@ -369,6 +374,9 @@ void ath_beacon_tasklet(unsigned long data) if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0) { sc->beacon.bmisscnt++; + if (!ath9k_hw_check_alive(ah)) + ieee80211_queue_work(sc->hw, &sc->hw_check_work); + if (sc->beacon.bmisscnt < BSTUCK_THRESH * sc->nbcnvifs) { ath_dbg(common, BSTUCK, "missed %u consecutive beacons\n", @@ -378,6 +386,7 @@ void ath_beacon_tasklet(unsigned long data) ath9k_hw_bstuck_nfcal(ah); } else if (sc->beacon.bmisscnt >= BSTUCK_THRESH) { ath_dbg(common, BSTUCK, "beacon is officially stuck\n"); + sc->beacon.bmisscnt = 0; sc->sc_flags |= SC_OP_TSF_RESET; ieee80211_queue_work(sc->hw, &sc->hw_reset_work); } @@ -650,6 +659,8 @@ static void ath_beacon_config_adhoc(struct ath_softc *sc, u32 tsf, intval, nexttbtt; ath9k_reset_beacon_status(sc); + if (!(sc->sc_flags & SC_OP_BEACONS)) + ath9k_hw_settsf64(ah, sc->beacon.bc_tstamp); intval = TU_TO_USEC(conf->beacon_interval); tsf = roundup(ath9k_hw_gettsf32(ah) + TU_TO_USEC(FUDGE), intval); @@ -806,8 +817,10 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status) { struct ath_hw *ah = sc->sc_ah; - if (!ath_has_valid_bslot(sc)) + if (!ath_has_valid_bslot(sc)) { + sc->sc_flags &= ~SC_OP_BEACONS; return; + } ath9k_ps_wakeup(sc); if (status) { diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index ec3271993411..1ca6da80d4ad 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -108,9 +108,7 @@ void ath9k_hw_btcoex_init_scheme(struct ath_hw *ah) return; } - if (AR_SREV_9462(ah)) { - btcoex_hw->scheme = ATH_BTCOEX_CFG_MCI; - } else if (AR_SREV_9300_20_OR_LATER(ah)) { + if (AR_SREV_9300_20_OR_LATER(ah)) { btcoex_hw->scheme = ATH_BTCOEX_CFG_3WIRE; btcoex_hw->btactive_gpio = ATH_BTACTIVE_GPIO_9300; btcoex_hw->wlanactive_gpio = ATH_WLANACTIVE_GPIO_9300; @@ -284,11 +282,12 @@ void ath9k_hw_btcoex_enable(struct ath_hw *ah) ath9k_hw_btcoex_enable_2wire(ah); break; case ATH_BTCOEX_CFG_3WIRE: + if (AR_SREV_9462(ah)) { + ath9k_hw_btcoex_enable_mci(ah); + return; + } ath9k_hw_btcoex_enable_3wire(ah); break; - case ATH_BTCOEX_CFG_MCI: - ath9k_hw_btcoex_enable_mci(ah); - return; } REG_RMW(ah, AR_GPIO_PDPU, @@ -305,11 +304,12 @@ void ath9k_hw_btcoex_disable(struct ath_hw *ah) int i; btcoex_hw->enabled = false; - if (btcoex_hw->scheme == ATH_BTCOEX_CFG_MCI) { + if (AR_SREV_9462(ah)) { ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) REG_WRITE(ah, AR_MCI_COEX_WL_WEIGHTS(i), btcoex_hw->wlan_weight[i]); + return; } ath9k_hw_set_gpio(ah, btcoex_hw->wlanactive_gpio, 0); diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index 8f93aef4414f..3a1e1cfabd5e 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -51,7 +51,6 @@ enum ath_btcoex_scheme { ATH_BTCOEX_CFG_NONE, ATH_BTCOEX_CFG_2WIRE, ATH_BTCOEX_CFG_3WIRE, - ATH_BTCOEX_CFG_MCI, }; struct ath9k_hw_mci { diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index ff47b32ecaf4..fde700c4e490 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -380,63 +380,75 @@ static ssize_t read_file_interrupt(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; - char buf[512]; unsigned int len = 0; + int rv; + int mxlen = 4000; + char *buf = kmalloc(mxlen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + +#define PR_IS(a, s) \ + do { \ + len += snprintf(buf + len, mxlen - len, \ + "%21s: %10u\n", a, \ + sc->debug.stats.istats.s); \ + } while (0) if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "RXLP", sc->debug.stats.istats.rxlp); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "RXHP", sc->debug.stats.istats.rxhp); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "WATCHDOG", - sc->debug.stats.istats.bb_watchdog); + PR_IS("RXLP", rxlp); + PR_IS("RXHP", rxhp); + PR_IS("WATHDOG", bb_watchdog); } else { - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "RX", sc->debug.stats.istats.rxok); + PR_IS("RX", rxok); } - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "RXEOL", sc->debug.stats.istats.rxeol); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "RXORN", sc->debug.stats.istats.rxorn); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "TX", sc->debug.stats.istats.txok); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "TXURN", sc->debug.stats.istats.txurn); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "MIB", sc->debug.stats.istats.mib); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "RXPHY", sc->debug.stats.istats.rxphyerr); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "RXKCM", sc->debug.stats.istats.rx_keycache_miss); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "SWBA", sc->debug.stats.istats.swba); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "BMISS", sc->debug.stats.istats.bmiss); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "BNR", sc->debug.stats.istats.bnr); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "CST", sc->debug.stats.istats.cst); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "GTT", sc->debug.stats.istats.gtt); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "TIM", sc->debug.stats.istats.tim); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "CABEND", sc->debug.stats.istats.cabend); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "DTIMSYNC", sc->debug.stats.istats.dtimsync); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "DTIM", sc->debug.stats.istats.dtim); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "TSFOOR", sc->debug.stats.istats.tsfoor); - len += snprintf(buf + len, sizeof(buf) - len, - "%8s: %10u\n", "TOTAL", sc->debug.stats.istats.total); - - - if (len > sizeof(buf)) - len = sizeof(buf); - - return simple_read_from_buffer(user_buf, count, ppos, buf, len); + PR_IS("RXEOL", rxeol); + PR_IS("RXORN", rxorn); + PR_IS("TX", txok); + PR_IS("TXURN", txurn); + PR_IS("MIB", mib); + PR_IS("RXPHY", rxphyerr); + PR_IS("RXKCM", rx_keycache_miss); + PR_IS("SWBA", swba); + PR_IS("BMISS", bmiss); + PR_IS("BNR", bnr); + PR_IS("CST", cst); + PR_IS("GTT", gtt); + PR_IS("TIM", tim); + PR_IS("CABEND", cabend); + PR_IS("DTIMSYNC", dtimsync); + PR_IS("DTIM", dtim); + PR_IS("TSFOOR", tsfoor); + PR_IS("TOTAL", total); + + len += snprintf(buf + len, mxlen - len, + "SYNC_CAUSE stats:\n"); + + PR_IS("Sync-All", sync_cause_all); + PR_IS("RTC-IRQ", sync_rtc_irq); + PR_IS("MAC-IRQ", sync_mac_irq); + PR_IS("EEPROM-Illegal-Access", eeprom_illegal_access); + PR_IS("APB-Timeout", apb_timeout); + PR_IS("PCI-Mode-Conflict", pci_mode_conflict); + PR_IS("HOST1-Fatal", host1_fatal); + PR_IS("HOST1-Perr", host1_perr); + PR_IS("TRCV-FIFO-Perr", trcv_fifo_perr); + PR_IS("RADM-CPL-EP", radm_cpl_ep); + PR_IS("RADM-CPL-DLLP-Abort", radm_cpl_dllp_abort); + PR_IS("RADM-CPL-TLP-Abort", radm_cpl_tlp_abort); + PR_IS("RADM-CPL-ECRC-Err", radm_cpl_ecrc_err); + PR_IS("RADM-CPL-Timeout", radm_cpl_timeout); + PR_IS("Local-Bus-Timeout", local_timeout); + PR_IS("PM-Access", pm_access); + PR_IS("MAC-Awake", mac_awake); + PR_IS("MAC-Asleep", mac_asleep); + PR_IS("MAC-Sleep-Access", mac_sleep_access); + + if (len > mxlen) + len = mxlen; + + rv = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + return rv; } static const struct file_operations fops_interrupt = { @@ -524,6 +536,7 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, PR("hw-put-tx-buf: ", puttxbuf); PR("hw-tx-start: ", txstart); PR("hw-tx-proc-desc: ", txprocdesc); + PR("TX-Failed: ", txfailed); len += snprintf(buf + len, size - len, "%s%11p%11p%10p%10p\n", "txq-memory-address:", sc->tx.txq_map[WME_AC_BE], @@ -880,6 +893,13 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, len += snprintf(buf + len, size - len, "%22s : %10u\n", s, \ sc->debug.stats.rxstats.phy_err_stats[p]); +#define RXS_ERR(s, e) \ + do { \ + len += snprintf(buf + len, size - len, \ + "%22s : %10u\n", s, \ + sc->debug.stats.rxstats.e); \ + } while (0) + struct ath_softc *sc = file->private_data; char *buf; unsigned int len = 0, size = 1600; @@ -889,27 +909,18 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; - len += snprintf(buf + len, size - len, - "%22s : %10u\n", "CRC ERR", - sc->debug.stats.rxstats.crc_err); - len += snprintf(buf + len, size - len, - "%22s : %10u\n", "DECRYPT CRC ERR", - sc->debug.stats.rxstats.decrypt_crc_err); - len += snprintf(buf + len, size - len, - "%22s : %10u\n", "PHY ERR", - sc->debug.stats.rxstats.phy_err); - len += snprintf(buf + len, size - len, - "%22s : %10u\n", "MIC ERR", - sc->debug.stats.rxstats.mic_err); - len += snprintf(buf + len, size - len, - "%22s : %10u\n", "PRE-DELIM CRC ERR", - sc->debug.stats.rxstats.pre_delim_crc_err); - len += snprintf(buf + len, size - len, - "%22s : %10u\n", "POST-DELIM CRC ERR", - sc->debug.stats.rxstats.post_delim_crc_err); - len += snprintf(buf + len, size - len, - "%22s : %10u\n", "DECRYPT BUSY ERR", - sc->debug.stats.rxstats.decrypt_busy_err); + RXS_ERR("CRC ERR", crc_err); + RXS_ERR("DECRYPT CRC ERR", decrypt_crc_err); + RXS_ERR("PHY ERR", phy_err); + RXS_ERR("MIC ERR", mic_err); + RXS_ERR("PRE-DELIM CRC ERR", pre_delim_crc_err); + RXS_ERR("POST-DELIM CRC ERR", post_delim_crc_err); + RXS_ERR("DECRYPT BUSY ERR", decrypt_busy_err); + RXS_ERR("RX-LENGTH-ERR", rx_len_err); + RXS_ERR("RX-OOM-ERR", rx_oom_err); + RXS_ERR("RX-RATE-ERR", rx_rate_err); + RXS_ERR("RX-DROP-RXFLUSH", rx_drop_rxflush); + RXS_ERR("RX-TOO-MANY-FRAGS", rx_too_many_frags_err); PHY_ERR("UNDERRUN ERR", ATH9K_PHYERR_UNDERRUN); PHY_ERR("TIMING ERR", ATH9K_PHYERR_TIMING); @@ -938,12 +949,10 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, PHY_ERR("HT-LENGTH ERR", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); PHY_ERR("HT-RATE ERR", ATH9K_PHYERR_HT_RATE_ILLEGAL); - len += snprintf(buf + len, size - len, - "%22s : %10u\n", "RX-Pkts-All", - sc->debug.stats.rxstats.rx_pkts_all); - len += snprintf(buf + len, size - len, - "%22s : %10u\n", "RX-Bytes-All", - sc->debug.stats.rxstats.rx_bytes_all); + RXS_ERR("RX-Pkts-All", rx_pkts_all); + RXS_ERR("RX-Bytes-All", rx_bytes_all); + RXS_ERR("RX-Beacons", rx_beacons); + RXS_ERR("RX-Frags", rx_frags); if (len > size) len = size; @@ -953,12 +962,12 @@ static ssize_t read_file_recv(struct file *file, char __user *user_buf, return retval; +#undef RXS_ERR #undef PHY_ERR } void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) { -#define RX_STAT_INC(c) sc->debug.stats.rxstats.c++ #define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++ #define RX_SAMP_DBG(c) (sc->debug.bb_mac_samp[sc->debug.sampidx].rs\ [sc->debug.rsidx].c) @@ -1004,7 +1013,6 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) #endif -#undef RX_STAT_INC #undef RX_PHY_ERR_INC #undef RX_SAMP_DBG } diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 64fcfad467bf..c34da09d9103 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -60,6 +60,7 @@ struct ath_buf; * @tsfoor: TSF out of range, indicates that the corrected TSF received * from a beacon differs from the PCU's internal TSF by more than a * (programmable) threshold + * @local_timeout: Internal bus timeout. */ struct ath_interrupt_stats { u32 total; @@ -85,8 +86,30 @@ struct ath_interrupt_stats { u32 dtim; u32 bb_watchdog; u32 tsfoor; + + /* Sync-cause stats */ + u32 sync_cause_all; + u32 sync_rtc_irq; + u32 sync_mac_irq; + u32 eeprom_illegal_access; + u32 apb_timeout; + u32 pci_mode_conflict; + u32 host1_fatal; + u32 host1_perr; + u32 trcv_fifo_perr; + u32 radm_cpl_ep; + u32 radm_cpl_dllp_abort; + u32 radm_cpl_tlp_abort; + u32 radm_cpl_ecrc_err; + u32 radm_cpl_timeout; + u32 local_timeout; + u32 pm_access; + u32 mac_awake; + u32 mac_asleep; + u32 mac_sleep_access; }; + /** * struct ath_tx_stats - Statistics about TX * @tx_pkts_all: No. of total frames transmitted, including ones that @@ -113,6 +136,7 @@ struct ath_interrupt_stats { * @puttxbuf: Number of times hardware was given txbuf to write. * @txstart: Number of times hardware was told to start tx. * @txprocdesc: Number of times tx descriptor was processed + * @txfailed: Out-of-memory or other errors in xmit path. */ struct ath_tx_stats { u32 tx_pkts_all; @@ -135,8 +159,11 @@ struct ath_tx_stats { u32 puttxbuf; u32 txstart; u32 txprocdesc; + u32 txfailed; }; +#define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++) + /** * struct ath_rx_stats - RX Statistics * @rx_pkts_all: No. of total frames received, including ones that @@ -153,6 +180,13 @@ struct ath_tx_stats { * @post_delim_crc_err: Post-Frame delimiter CRC error detections * @decrypt_busy_err: Decryption interruptions counter * @phy_err_stats: Individual PHY error statistics + * @rx_len_err: No. of frames discarded due to bad length. + * @rx_oom_err: No. of frames dropped due to OOM issues. + * @rx_rate_err: No. of frames dropped due to rate errors. + * @rx_too_many_frags_err: Frames dropped due to too-many-frags received. + * @rx_drop_rxflush: No. of frames dropped due to RX-FLUSH. + * @rx_beacons: No. of beacons received. + * @rx_frags: No. of rx-fragements received. */ struct ath_rx_stats { u32 rx_pkts_all; @@ -165,6 +199,13 @@ struct ath_rx_stats { u32 post_delim_crc_err; u32 decrypt_busy_err; u32 phy_err_stats[ATH9K_PHYERR_MAX]; + u32 rx_len_err; + u32 rx_oom_err; + u32 rx_rate_err; + u32 rx_too_many_frags_err; + u32 rx_drop_rxflush; + u32 rx_beacons; + u32 rx_frags; }; enum ath_reset_type { @@ -174,6 +215,7 @@ enum ath_reset_type { RESET_TYPE_TX_ERROR, RESET_TYPE_TX_HANG, RESET_TYPE_PLL_HANG, + RESET_TYPE_MAC_HANG, __RESET_TYPE_MAX }; @@ -247,6 +289,8 @@ void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs); #else +#define RX_STAT_INC(c) /* NOP */ + static inline int ath9k_init_debug(struct ath_hw *ah) { return 0; diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c index f4f56aff1e9d..ecc81792f2dc 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.c +++ b/drivers/net/wireless/ath/ath9k/dfs.c @@ -21,17 +21,6 @@ #include "dfs.h" #include "dfs_debug.h" -/* - * TODO: move into or synchronize this with generic header - * as soon as IF is defined - */ -struct dfs_radar_pulse { - u16 freq; - u64 ts; - u32 width; - u8 rssi; -}; - /* internal struct to pass radar data */ struct ath_radar_data { u8 pulse_bw_info; @@ -60,44 +49,44 @@ static u32 dur_to_usecs(struct ath_hw *ah, u32 dur) #define EXT_CH_RADAR_FOUND 0x02 static bool ath9k_postprocess_radar_event(struct ath_softc *sc, - struct ath_radar_data *are, - struct dfs_radar_pulse *drp) + struct ath_radar_data *ard, + struct pulse_event *pe) { u8 rssi; u16 dur; ath_dbg(ath9k_hw_common(sc->sc_ah), DFS, "pulse_bw_info=0x%x, pri,ext len/rssi=(%u/%u, %u/%u)\n", - are->pulse_bw_info, - are->pulse_length_pri, are->rssi, - are->pulse_length_ext, are->ext_rssi); + ard->pulse_bw_info, + ard->pulse_length_pri, ard->rssi, + ard->pulse_length_ext, ard->ext_rssi); /* * Only the last 2 bits of the BW info are relevant, they indicate * which channel the radar was detected in. */ - are->pulse_bw_info &= 0x03; + ard->pulse_bw_info &= 0x03; - switch (are->pulse_bw_info) { + switch (ard->pulse_bw_info) { case PRI_CH_RADAR_FOUND: /* radar in ctrl channel */ - dur = are->pulse_length_pri; + dur = ard->pulse_length_pri; DFS_STAT_INC(sc, pri_phy_errors); /* * cannot use ctrl channel RSSI * if extension channel is stronger */ - rssi = (are->ext_rssi >= (are->rssi + 3)) ? 0 : are->rssi; + rssi = (ard->ext_rssi >= (ard->rssi + 3)) ? 0 : ard->rssi; break; case EXT_CH_RADAR_FOUND: /* radar in extension channel */ - dur = are->pulse_length_ext; + dur = ard->pulse_length_ext; DFS_STAT_INC(sc, ext_phy_errors); /* * cannot use extension channel RSSI * if control channel is stronger */ - rssi = (are->rssi >= (are->ext_rssi + 12)) ? 0 : are->ext_rssi; + rssi = (ard->rssi >= (ard->ext_rssi + 12)) ? 0 : ard->ext_rssi; break; case (PRI_CH_RADAR_FOUND | EXT_CH_RADAR_FOUND): /* @@ -107,14 +96,14 @@ ath9k_postprocess_radar_event(struct ath_softc *sc, * Radiated testing, when pulse is on DC, different pri and * ext durations are reported, so take the larger of the two */ - if (are->pulse_length_ext >= are->pulse_length_pri) - dur = are->pulse_length_ext; + if (ard->pulse_length_ext >= ard->pulse_length_pri) + dur = ard->pulse_length_ext; else - dur = are->pulse_length_pri; + dur = ard->pulse_length_pri; DFS_STAT_INC(sc, dc_phy_errors); /* when both are present use stronger one */ - rssi = (are->rssi < are->ext_rssi) ? are->ext_rssi : are->rssi; + rssi = (ard->rssi < ard->ext_rssi) ? ard->ext_rssi : ard->rssi; break; default: /* @@ -137,8 +126,8 @@ ath9k_postprocess_radar_event(struct ath_softc *sc, */ /* convert duration to usecs */ - drp->width = dur_to_usecs(sc->sc_ah, dur); - drp->rssi = rssi; + pe->width = dur_to_usecs(sc->sc_ah, dur); + pe->rssi = rssi; DFS_STAT_INC(sc, pulses_detected); return true; @@ -155,15 +144,17 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, struct ath_radar_data ard; u16 datalen; char *vdata_end; - struct dfs_radar_pulse drp; + struct pulse_event pe; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - if ((!(rs->rs_phyerr != ATH9K_PHYERR_RADAR)) && - (!(rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT))) { + DFS_STAT_INC(sc, pulses_total); + if ((rs->rs_phyerr != ATH9K_PHYERR_RADAR) && + (rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT)) { ath_dbg(common, DFS, "Error: rs_phyer=0x%x not a radar error\n", rs->rs_phyerr); + DFS_STAT_INC(sc, pulses_no_dfs); return; } @@ -189,27 +180,22 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, ard.pulse_bw_info = vdata_end[-1]; ard.pulse_length_ext = vdata_end[-2]; ard.pulse_length_pri = vdata_end[-3]; - - ath_dbg(common, DFS, - "bw_info=%d, length_pri=%d, length_ext=%d, " - "rssi_pri=%d, rssi_ext=%d\n", - ard.pulse_bw_info, ard.pulse_length_pri, ard.pulse_length_ext, - ard.rssi, ard.ext_rssi); - - drp.freq = ah->curchan->channel; - drp.ts = mactime; - if (ath9k_postprocess_radar_event(sc, &ard, &drp)) { + pe.freq = ah->curchan->channel; + pe.ts = mactime; + if (ath9k_postprocess_radar_event(sc, &ard, &pe)) { + struct dfs_pattern_detector *pd = sc->dfs_detector; static u64 last_ts; ath_dbg(common, DFS, "ath9k_dfs_process_phyerr: channel=%d, ts=%llu, " "width=%d, rssi=%d, delta_ts=%llu\n", - drp.freq, drp.ts, drp.width, drp.rssi, drp.ts-last_ts); - last_ts = drp.ts; - /* - * TODO: forward pulse to pattern detector - * - * ieee80211_add_radar_pulse(drp.freq, drp.ts, - * drp.width, drp.rssi); - */ + pe.freq, pe.ts, pe.width, pe.rssi, pe.ts-last_ts); + last_ts = pe.ts; + DFS_STAT_INC(sc, pulses_processed); + if (pd != NULL && pd->add_pulse(pd, &pe)) { + DFS_STAT_INC(sc, radar_detected); + /* + * TODO: forward radar event to DFS management layer + */ + } } } diff --git a/drivers/net/wireless/ath/ath9k/dfs.h b/drivers/net/wireless/ath/ath9k/dfs.h index c2412857f122..3c839f06a06a 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.h +++ b/drivers/net/wireless/ath/ath9k/dfs.h @@ -17,6 +17,7 @@ #ifndef ATH9K_DFS_H #define ATH9K_DFS_H +#include "dfs_pattern_detector.h" #if defined(CONFIG_ATH9K_DFS_CERTIFIED) /** @@ -31,13 +32,14 @@ * * The radar information provided as raw payload data is validated and * filtered for false pulses. Events passing all tests are forwarded to - * the upper layer for pattern detection. + * the DFS detector for pattern detection. */ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, struct ath_rx_status *rs, u64 mactime); #else -static inline void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, - struct ath_rx_status *rs, u64 mactime) { } +static inline void +ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, + struct ath_rx_status *rs, u64 mactime) { } #endif #endif /* ATH9K_DFS_H */ diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.c b/drivers/net/wireless/ath/ath9k/dfs_debug.c index 4364c103ed33..55d28072adeb 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.c +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.c @@ -21,9 +21,15 @@ #include "ath9k.h" #include "dfs_debug.h" + +struct ath_dfs_pool_stats global_dfs_pool_stats = { 0 }; + #define ATH9K_DFS_STAT(s, p) \ len += snprintf(buf + len, size - len, "%28s : %10u\n", s, \ sc->debug.stats.dfs_stats.p); +#define ATH9K_DFS_POOL_STAT(s, p) \ + len += snprintf(buf + len, size - len, "%28s : %10u\n", s, \ + global_dfs_pool_stats.p); static ssize_t read_file_dfs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -43,6 +49,9 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, hw_ver->macVersion, hw_ver->macRev, (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_DFS) ? "enabled" : "disabled"); + len += snprintf(buf + len, size - len, "Pulse detector statistics:\n"); + ATH9K_DFS_STAT("pulse events reported ", pulses_total); + ATH9K_DFS_STAT("invalid pulse events ", pulses_no_dfs); ATH9K_DFS_STAT("DFS pulses detected ", pulses_detected); ATH9K_DFS_STAT("Datalen discards ", datalen_discards); ATH9K_DFS_STAT("RSSI discards ", rssi_discards); @@ -50,6 +59,18 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, ATH9K_DFS_STAT("Primary channel pulses ", pri_phy_errors); ATH9K_DFS_STAT("Secondary channel pulses", ext_phy_errors); ATH9K_DFS_STAT("Dual channel pulses ", dc_phy_errors); + len += snprintf(buf + len, size - len, "Radar detector statistics " + "(current DFS region: %d)\n", sc->dfs_detector->region); + ATH9K_DFS_STAT("Pulse events processed ", pulses_processed); + ATH9K_DFS_STAT("Radars detected ", radar_detected); + len += snprintf(buf + len, size - len, "Global Pool statistics:\n"); + ATH9K_DFS_POOL_STAT("Pool references ", pool_reference); + ATH9K_DFS_POOL_STAT("Pulses allocated ", pulse_allocated); + ATH9K_DFS_POOL_STAT("Pulses alloc error ", pulse_alloc_error); + ATH9K_DFS_POOL_STAT("Pulses in use ", pulse_used); + ATH9K_DFS_POOL_STAT("Seqs. allocated ", pseq_allocated); + ATH9K_DFS_POOL_STAT("Seqs. alloc error ", pseq_alloc_error); + ATH9K_DFS_POOL_STAT("Seqs. in use ", pseq_used); if (len > size) len = size; @@ -60,8 +81,33 @@ static ssize_t read_file_dfs(struct file *file, char __user *user_buf, return retval; } +/* magic number to prevent accidental reset of DFS statistics */ +#define DFS_STATS_RESET_MAGIC 0x80000000 +static ssize_t write_file_dfs(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if (val == DFS_STATS_RESET_MAGIC) + memset(&sc->debug.stats.dfs_stats, 0, + sizeof(sc->debug.stats.dfs_stats)); + return count; +} + static const struct file_operations fops_dfs_stats = { .read = read_file_dfs, + .write = write_file_dfs, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, diff --git a/drivers/net/wireless/ath/ath9k/dfs_debug.h b/drivers/net/wireless/ath/ath9k/dfs_debug.h index 4911724cb445..e36810a4b585 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_debug.h +++ b/drivers/net/wireless/ath/ath9k/dfs_debug.h @@ -22,17 +22,23 @@ #include "hw.h" /** - * struct ath_dfs_stats - DFS Statistics - * - * @pulses_detected: No. of pulses detected so far - * @datalen_discards: No. of pulses discarded due to invalid datalen - * @rssi_discards: No. of pulses discarded due to invalid RSSI - * @bwinfo_discards: No. of pulses discarded due to invalid BW info - * @pri_phy_errors: No. of pulses reported for primary channel - * @ext_phy_errors: No. of pulses reported for extension channel - * @dc_phy_errors: No. of pulses reported for primary + extension channel + * struct ath_dfs_stats - DFS Statistics per wiphy + * @pulses_total: pulses reported by HW + * @pulses_no_dfs: pulses wrongly reported as DFS + * @pulses_detected: pulses detected so far + * @datalen_discards: pulses discarded due to invalid datalen + * @rssi_discards: pulses discarded due to invalid RSSI + * @bwinfo_discards: pulses discarded due to invalid BW info + * @pri_phy_errors: pulses reported for primary channel + * @ext_phy_errors: pulses reported for extension channel + * @dc_phy_errors: pulses reported for primary + extension channel + * @pulses_processed: pulses forwarded to detector + * @radar_detected: radars detected */ struct ath_dfs_stats { + /* pulse stats */ + u32 pulses_total; + u32 pulses_no_dfs; u32 pulses_detected; u32 datalen_discards; u32 rssi_discards; @@ -40,18 +46,39 @@ struct ath_dfs_stats { u32 pri_phy_errors; u32 ext_phy_errors; u32 dc_phy_errors; + /* pattern detection stats */ + u32 pulses_processed; + 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 #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 new file mode 100644 index 000000000000..ea2a6cf7ef23 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c @@ -0,0 +1,300 @@ +/* + * 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" + +/* + * 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) + +#define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \ +{ \ + ID, WMIN, 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; + + cd = kmalloc(sizeof(*cd), GFP_KERNEL); + 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_KERNEL); + 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: + pr_err("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; + bool ts_wraparound; + struct channel_detector *cd; + + if (dpd->region == NL80211_DFS_UNSET) { + /* + * pulses received for a non-supported or un-initialized + * domain are treated as detected radars + */ + return true; + } + + cd = channel_detector_get(dpd, event->freq); + if (cd == NULL) + return false; + + ts_wraparound = (event->ts < dpd->last_pulse_ts); + dpd->last_pulse_ts = event->ts; + if (ts_wraparound) { + /* + * reset detector on time stamp wraparound + * with monotonic time stamps, this should never happen + */ + pr_warn("DFS: time stamp wraparound detected, resetting\n"); + dpd_reset(dpd); + } + /* do type individual pattern matching */ + for (i = 0; i < dpd->num_radar_types; i++) { + if (cd->detectors[i]->add_pulse(cd->detectors[i], event) != 0) { + 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_domain = dpd_set_domain, + .add_pulse = dpd_add_pulse, + .region = NL80211_DFS_UNSET, +}; + +struct dfs_pattern_detector * +dfs_pattern_detector_init(enum nl80211_dfs_regions region) +{ + struct dfs_pattern_detector *dpd; + dpd = kmalloc(sizeof(*dpd), GFP_KERNEL); + if (dpd == NULL) { + pr_err("allocation of dfs_pattern_detector failed\n"); + return NULL; + } + *dpd = default_dpd; + INIT_LIST_HEAD(&dpd->channel_detectors); + + if (dpd->set_domain(dpd, region)) + return dpd; + + pr_err("Could not set DFS domain to %d. ", region); + 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 new file mode 100644 index 000000000000..fd0328a30995 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h @@ -0,0 +1,104 @@ +/* + * 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_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_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; + + 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(enum nl80211_dfs_regions region); +#else +static inline struct dfs_pattern_detector * +dfs_pattern_detector_init(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 new file mode 100644 index 000000000000..91b8dceeadb1 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.c @@ -0,0 +1,452 @@ +/* + * 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 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 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_KERNEL); + 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_KERNEL); + 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 bool 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 false; + + if ((ts - de->last_ts) < rs->max_pri_tolerance) + /* if delta to last pulse is too short, don't use this pulse */ + return false; + 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)) { + pr_err("failed to create pulse sequences\n"); + pri_detector_reset(de, ts); + return false; + } + + ps = pseq_handler_check_detection(de); + + if (ps != NULL) { + pr_info("DFS: radar found: pri=%d, count=%d, count_false=%d\n", + ps->pri, ps->count, ps->count_falses); + pri_detector_reset(de, ts); + return true; + } + pulse_queue_enqueue(de, ts); + return false; +} + +struct pri_detector * +pri_detector_init(const struct radar_detector_specs *rs) +{ + struct pri_detector *de; + de = kzalloc(sizeof(*de), GFP_KERNEL); + 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 new file mode 100644 index 000000000000..81cde9f28e44 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/dfs_pri_detector.h @@ -0,0 +1,52 @@ +/* + * 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_detector - PRI detector element for a dedicated radar type + * @exit(): destructor + * @add_pulse(): add pulse event, returns true 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); + bool (*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/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index c43523233319..0512397a293c 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -16,14 +16,6 @@ #include "hw.h" -static inline u16 ath9k_hw_fbin2freq(u8 fbin, bool is2GHz) -{ - if (fbin == AR5416_BCHAN_UNUSED) - return fbin; - - return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); -} - void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val) { REG_WRITE(ah, reg, val); @@ -290,6 +282,34 @@ u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, return twiceMaxEdgePower; } +u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit, + u8 antenna_reduction) +{ + u16 reduction = antenna_reduction; + + /* + * Reduce scaled Power by number of chains active + * to get the per chain tx power level. + */ + switch (ar5416_get_ntxchains(ah->txchainmask)) { + case 1: + break; + case 2: + reduction += POWER_CORRECTION_FOR_TWO_CHAIN; + break; + case 3: + reduction += POWER_CORRECTION_FOR_THREE_CHAIN; + break; + } + + if (power_limit > reduction) + power_limit -= reduction; + else + power_limit = 0; + + return power_limit; +} + void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -299,10 +319,10 @@ void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah) case 1: break; case 2: - regulatory->max_power_level += INCREASE_MAXPOW_BY_TWO_CHAIN; + regulatory->max_power_level += POWER_CORRECTION_FOR_TWO_CHAIN; break; case 3: - regulatory->max_power_level += INCREASE_MAXPOW_BY_THREE_CHAIN; + regulatory->max_power_level += POWER_CORRECTION_FOR_THREE_CHAIN; break; default: ath_dbg(common, EEPROM, "Invalid chainmask configuration\n"); diff --git a/drivers/net/wireless/ath/ath9k/eeprom.h b/drivers/net/wireless/ath/ath9k/eeprom.h index 5ff7ab965120..33acb920ed3f 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.h +++ b/drivers/net/wireless/ath/ath9k/eeprom.h @@ -79,8 +79,8 @@ #define SUB_NUM_CTL_MODES_AT_5G_40 2 #define SUB_NUM_CTL_MODES_AT_2G_40 3 -#define INCREASE_MAXPOW_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ -#define INCREASE_MAXPOW_BY_THREE_CHAIN 10 /* 10*log10(3)*2 */ +#define POWER_CORRECTION_FOR_TWO_CHAIN 6 /* 10*log10(2)*2 */ +#define POWER_CORRECTION_FOR_THREE_CHAIN 10 /* 10*log10(3)*2 */ /* * For AR9285 and later chipsets, the following bits are not being programmed @@ -686,6 +686,8 @@ void ath9k_hw_get_target_powers(struct ath_hw *ah, u16 numRates, bool isHt40Target); u16 ath9k_hw_get_max_edge_power(u16 freq, struct cal_ctl_edges *pRdEdgesPower, bool is2GHz, int num_band_edges); +u16 ath9k_hw_get_scaled_power(struct ath_hw *ah, u16 power_limit, + u8 antenna_reduction); void ath9k_hw_update_regulatory_maxpower(struct ath_hw *ah); int ath9k_hw_eeprom_init(struct ath_hw *ah); @@ -697,6 +699,14 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, u16 *pPdGainBoundaries, u8 *pPDADCValues, u16 numXpdGains); +static inline u16 ath9k_hw_fbin2freq(u8 fbin, bool is2GHz) +{ + if (fbin == AR5416_BCHAN_UNUSED) + return fbin; + + return (u16) ((is2GHz) ? (2300 + fbin) : (4800 + 5 * fbin)); +} + #define ar5416_get_ntxchains(_txchainmask) \ (((_txchainmask >> 2) & 1) + \ ((_txchainmask >> 1) & 1) + (_txchainmask & 1)) diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index b34e8b2990b1..aa614767adff 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -564,9 +564,6 @@ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah, (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == \ ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL)) -#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 -#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 10 - u16 twiceMaxEdgePower; int i; struct cal_ctl_data_ar9287 *rep; @@ -591,29 +588,8 @@ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah, tx_chainmask = ah->txchainmask; ath9k_hw_get_channel_centers(ah, chan, ¢ers); - scaledPower = powerLimit - antenna_reduction; - - /* - * Reduce scaled Power by number of chains active - * to get the per chain tx power level. - */ - switch (ar5416_get_ntxchains(tx_chainmask)) { - case 1: - break; - case 2: - if (scaledPower > REDUCE_SCALED_POWER_BY_TWO_CHAIN) - scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN; - else - scaledPower = 0; - break; - case 3: - if (scaledPower > REDUCE_SCALED_POWER_BY_THREE_CHAIN) - scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN; - else - scaledPower = 0; - break; - } - scaledPower = max((u16)0, scaledPower); + scaledPower = ath9k_hw_get_scaled_power(ah, powerLimit, + antenna_reduction); /* * Get TX power from EEPROM. @@ -786,8 +762,6 @@ static void ath9k_hw_set_ar9287_power_per_rate_table(struct ath_hw *ah, #undef CMP_CTL #undef CMP_NO_CTL -#undef REDUCE_SCALED_POWER_BY_TWO_CHAIN -#undef REDUCE_SCALED_POWER_BY_THREE_CHAIN } static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 619b95d764ff..b5fba8b18b8b 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -991,9 +991,6 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, u16 antenna_reduction, u16 powerLimit) { -#define REDUCE_SCALED_POWER_BY_TWO_CHAIN 6 /* 10*log10(2)*2 */ -#define REDUCE_SCALED_POWER_BY_THREE_CHAIN 9 /* 10*log10(3)*2 */ - struct ar5416_eeprom_def *pEepData = &ah->eeprom.def; u16 twiceMaxEdgePower; int i; @@ -1027,24 +1024,8 @@ static void ath9k_hw_set_def_power_per_rate_table(struct ath_hw *ah, ath9k_hw_get_channel_centers(ah, chan, ¢ers); - scaledPower = powerLimit - antenna_reduction; - - switch (ar5416_get_ntxchains(tx_chainmask)) { - case 1: - break; - case 2: - if (scaledPower > REDUCE_SCALED_POWER_BY_TWO_CHAIN) - scaledPower -= REDUCE_SCALED_POWER_BY_TWO_CHAIN; - else - scaledPower = 0; - break; - case 3: - if (scaledPower > REDUCE_SCALED_POWER_BY_THREE_CHAIN) - scaledPower -= REDUCE_SCALED_POWER_BY_THREE_CHAIN; - else - scaledPower = 0; - break; - } + scaledPower = ath9k_hw_get_scaled_power(ah, powerLimit, + antenna_reduction); if (IS_CHAN_2GHZ(chan)) { numCtlModes = ARRAY_SIZE(ctlModesFor11g) - @@ -1263,20 +1244,7 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, regulatory->max_power_level = ratesArray[i]; } - switch(ar5416_get_ntxchains(ah->txchainmask)) { - case 1: - break; - case 2: - regulatory->max_power_level += INCREASE_MAXPOW_BY_TWO_CHAIN; - break; - case 3: - regulatory->max_power_level += INCREASE_MAXPOW_BY_THREE_CHAIN; - break; - default: - ath_dbg(ath9k_hw_common(ah), EEPROM, - "Invalid chainmask configuration\n"); - break; - } + ath9k_hw_update_regulatory_maxpower(ah); if (test) return; diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index fbe23de1297f..281a9af0f1b6 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -41,6 +41,9 @@ void ath_init_leds(struct ath_softc *sc) { int ret; + if (AR_SREV_9100(sc->sc_ah)) + return; + if (sc->sc_ah->led_pin < 0) { if (AR_SREV_9287(sc->sc_ah)) sc->sc_ah->led_pin = ATH_LED_PIN_9287; @@ -362,7 +365,7 @@ void ath9k_stop_btcoex(struct ath_softc *sc) ath9k_hw_btcoex_disable(ah); if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) ath9k_btcoex_timer_pause(sc); - if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_MCI) + if (AR_SREV_9462(ah)) ath_mci_flush_profile(&sc->btcoex.mci); } } @@ -373,7 +376,7 @@ void ath9k_deinit_btcoex(struct ath_softc *sc) ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_3WIRE) ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer); - if (ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_MCI) + if (AR_SREV_9462(sc->sc_ah)) ath_mci_cleanup(sc); } @@ -399,17 +402,16 @@ int ath9k_init_btcoex(struct ath_softc *sc) txq = sc->tx.txq_map[WME_AC_BE]; ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum); sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - break; - case ATH_BTCOEX_CFG_MCI: - sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; - INIT_LIST_HEAD(&sc->btcoex.mci.info); + if (AR_SREV_9462(ah)) { + sc->btcoex.duty_cycle = ATH_BTCOEX_DEF_DUTY_CYCLE; + INIT_LIST_HEAD(&sc->btcoex.mci.info); - r = ath_mci_setup(sc); - if (r) - return r; + r = ath_mci_setup(sc); + if (r) + return r; - ath9k_hw_btcoex_init_mci(ah); + ath9k_hw_btcoex_init_mci(ah); + } break; default: diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 424aabb2c730..aa327adcc3d8 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -53,6 +53,8 @@ static struct usb_device_id ath9k_hif_usb_ids[] = { .driver_info = AR9280_USB }, /* SMC Networks */ { USB_DEVICE(0x0411, 0x017f), .driver_info = AR9280_USB }, /* Sony UWA-BR100 */ + { USB_DEVICE(0x04da, 0x3904), + .driver_info = AR9280_USB }, { USB_DEVICE(0x0cf3, 0x20ff), .driver_info = STORAGE_DEVICE }, @@ -1356,6 +1358,7 @@ static struct usb_driver ath9k_hif_usb_driver = { #endif .id_table = ath9k_hif_usb_ids, .soft_unbind = 1, + .disable_hub_initiated_lpm = 1, }; int ath9k_hif_usb_init(void) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index de5ee15ee639..25213d521bc2 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "htc.h" MODULE_AUTHOR("Atheros Communications"); @@ -711,7 +713,8 @@ static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; + hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; hw->queues = 4; hw->channel_change_time = 5000; @@ -966,9 +969,7 @@ int ath9k_htc_resume(struct htc_target *htc_handle) static int __init ath9k_htc_init(void) { if (ath9k_hif_usb_init() < 0) { - printk(KERN_ERR - "ath9k_htc: No USB devices found," - " driver not installed.\n"); + pr_err("No USB devices found, driver not installed\n"); return -ENODEV; } @@ -979,6 +980,6 @@ module_init(ath9k_htc_init); static void __exit ath9k_htc_exit(void) { ath9k_hif_usb_exit(); - printk(KERN_INFO "ath9k_htc: Driver unloaded\n"); + pr_info("Driver unloaded\n"); } module_exit(ath9k_htc_exit); diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index c25226a32ddc..4a9570dfba72 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include "htc.h" static int htc_issue_send(struct htc_target *target, struct sk_buff* skb, @@ -461,7 +463,7 @@ int ath9k_htc_hw_init(struct htc_target *target, char *product, u32 drv_info) { if (ath9k_htc_probe_device(target, dev, devid, product, drv_info)) { - printk(KERN_ERR "Failed to initialize the device\n"); + pr_err("Failed to initialize the device\n"); return -ENODEV; } diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index fa84e37bf091..7db1890448f2 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -24,6 +24,8 @@ #include "rc.h" #include "ar9003_mac.h" #include "ar9003_mci.h" +#include "debug.h" +#include "ath9k.h" static bool ath9k_hw_set_reset_reg(struct ath_hw *ah, u32 type); @@ -83,6 +85,53 @@ static void ath9k_hw_ani_cache_ini_regs(struct ath_hw *ah) /* Helper Functions */ /********************/ +#ifdef CONFIG_ATH9K_DEBUGFS + +void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause) +{ + struct ath_softc *sc = common->priv; + if (sync_cause) + sc->debug.stats.istats.sync_cause_all++; + if (sync_cause & AR_INTR_SYNC_RTC_IRQ) + sc->debug.stats.istats.sync_rtc_irq++; + if (sync_cause & AR_INTR_SYNC_MAC_IRQ) + sc->debug.stats.istats.sync_mac_irq++; + if (sync_cause & AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS) + sc->debug.stats.istats.eeprom_illegal_access++; + if (sync_cause & AR_INTR_SYNC_APB_TIMEOUT) + sc->debug.stats.istats.apb_timeout++; + if (sync_cause & AR_INTR_SYNC_PCI_MODE_CONFLICT) + sc->debug.stats.istats.pci_mode_conflict++; + if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) + sc->debug.stats.istats.host1_fatal++; + if (sync_cause & AR_INTR_SYNC_HOST1_PERR) + sc->debug.stats.istats.host1_perr++; + if (sync_cause & AR_INTR_SYNC_TRCV_FIFO_PERR) + sc->debug.stats.istats.trcv_fifo_perr++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_EP) + sc->debug.stats.istats.radm_cpl_ep++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_DLLP_ABORT) + sc->debug.stats.istats.radm_cpl_dllp_abort++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_TLP_ABORT) + sc->debug.stats.istats.radm_cpl_tlp_abort++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_ECRC_ERR) + sc->debug.stats.istats.radm_cpl_ecrc_err++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) + sc->debug.stats.istats.radm_cpl_timeout++; + if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) + sc->debug.stats.istats.local_timeout++; + if (sync_cause & AR_INTR_SYNC_PM_ACCESS) + sc->debug.stats.istats.pm_access++; + if (sync_cause & AR_INTR_SYNC_MAC_AWAKE) + sc->debug.stats.istats.mac_awake++; + if (sync_cause & AR_INTR_SYNC_MAC_ASLEEP) + sc->debug.stats.istats.mac_asleep++; + if (sync_cause & AR_INTR_SYNC_MAC_SLEEP_ACCESS) + sc->debug.stats.istats.mac_sleep_access++; +} +#endif + + static void ath9k_hw_set_clockrate(struct ath_hw *ah) { struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; @@ -142,6 +191,22 @@ bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout) } EXPORT_SYMBOL(ath9k_hw_wait); +void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan, + int hw_delay) +{ + if (IS_CHAN_B(chan)) + hw_delay = (4 * hw_delay) / 22; + else + hw_delay /= 10; + + if (IS_CHAN_HALF_RATE(chan)) + hw_delay *= 2; + else if (IS_CHAN_QUARTER_RATE(chan)) + hw_delay *= 4; + + udelay(hw_delay + BASE_ACTIVATE_DELAY); +} + void ath9k_hw_write_array(struct ath_hw *ah, struct ar5416IniArray *array, int column, unsigned int *writecnt) { @@ -388,8 +453,8 @@ static void ath9k_hw_init_config(struct ath_hw *ah) { int i; - ah->config.dma_beacon_response_time = 2; - ah->config.sw_beacon_response_time = 10; + ah->config.dma_beacon_response_time = 1; + ah->config.sw_beacon_response_time = 6; ah->config.additional_swba_backoff = 0; ah->config.ack_6mb = 0x0; ah->config.cwm_ignore_extcca = 0; @@ -445,7 +510,6 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) AR_STA_ID1_MCAST_KSRCH; if (AR_SREV_9100(ah)) ah->sta_id1_defaults |= AR_STA_ID1_AR9100_BA_FIX; - ah->enable_32kHz_clock = DONT_USE_32KHZ; ah->slottime = ATH9K_SLOT_TIME_9; ah->globaltxtimeout = (u32) -1; ah->power_mode = ATH9K_PM_UNDEFINED; @@ -972,7 +1036,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); struct ieee80211_conf *conf = &common->hw->conf; const struct ath9k_channel *chan = ah->curchan; - int acktimeout, ctstimeout; + int acktimeout, ctstimeout, ack_offset = 0; int slottime; int sifstime; int rx_lat = 0, tx_lat = 0, eifs = 0; @@ -993,6 +1057,11 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) rx_lat = 37; tx_lat = 54; + if (IS_CHAN_5GHZ(chan)) + sifstime = 16; + else + sifstime = 10; + if (IS_CHAN_HALF_RATE(chan)) { eifs = 175; rx_lat *= 2; @@ -1000,8 +1069,9 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) if (IS_CHAN_A_FAST_CLOCK(ah, chan)) tx_lat += 11; + sifstime *= 2; + ack_offset = 16; slottime = 13; - sifstime = 32; } else if (IS_CHAN_QUARTER_RATE(chan)) { eifs = 340; rx_lat = (rx_lat * 4) - 1; @@ -1009,8 +1079,9 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) if (IS_CHAN_A_FAST_CLOCK(ah, chan)) tx_lat += 22; + sifstime *= 4; + ack_offset = 32; slottime = 21; - sifstime = 64; } else { if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah)) { eifs = AR_D_GBL_IFS_EIFS_ASYNC_FIFO; @@ -1024,14 +1095,10 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) tx_lat = MS(reg, AR_USEC_TX_LAT); slottime = ah->slottime; - if (IS_CHAN_5GHZ(chan)) - sifstime = 16; - else - sifstime = 10; } /* As defined by IEEE 802.11-2007 17.3.8.6 */ - acktimeout = slottime + sifstime + 3 * ah->coverage_class; + acktimeout = slottime + sifstime + 3 * ah->coverage_class + ack_offset; ctstimeout = acktimeout; /* @@ -1041,7 +1108,8 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah) * BA frames in some implementations, but it has been found to fix ACK * timeout issues in other cases as well. */ - if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ) { + if (conf->channel && conf->channel->band == IEEE80211_BAND_2GHZ && + !IS_CHAN_HALF_RATE(chan) && !IS_CHAN_QUARTER_RATE(chan)) { acktimeout += 64 - sifstime - ah->slottime; ctstimeout += 48 - sifstime - ah->slottime; } @@ -1400,6 +1468,9 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah, return false; ah->chip_fullsleep = false; + + if (AR_SREV_9330(ah)) + ar9003_hw_internal_regulator_apply(ah); ath9k_hw_init_pll(ah, chan); ath9k_hw_set_rfmode(ah, chan); @@ -1491,11 +1562,84 @@ static void ath9k_hw_apply_gpio_override(struct ath_hw *ah) } } +static bool ath9k_hw_check_dcs(u32 dma_dbg, u32 num_dcu_states, + int *hang_state, int *hang_pos) +{ + static u32 dcu_chain_state[] = {5, 6, 9}; /* DCU chain stuck states */ + u32 chain_state, dcs_pos, i; + + for (dcs_pos = 0; dcs_pos < num_dcu_states; dcs_pos++) { + chain_state = (dma_dbg >> (5 * dcs_pos)) & 0x1f; + for (i = 0; i < 3; i++) { + if (chain_state == dcu_chain_state[i]) { + *hang_state = chain_state; + *hang_pos = dcs_pos; + return true; + } + } + } + return false; +} + +#define DCU_COMPLETE_STATE 1 +#define DCU_COMPLETE_STATE_MASK 0x3 +#define NUM_STATUS_READS 50 +static bool ath9k_hw_detect_mac_hang(struct ath_hw *ah) +{ + u32 chain_state, comp_state, dcs_reg = AR_DMADBG_4; + u32 i, hang_pos, hang_state, num_state = 6; + + comp_state = REG_READ(ah, AR_DMADBG_6); + + if ((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) { + ath_dbg(ath9k_hw_common(ah), RESET, + "MAC Hang signature not found at DCU complete\n"); + return false; + } + + chain_state = REG_READ(ah, dcs_reg); + if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos)) + goto hang_check_iter; + + dcs_reg = AR_DMADBG_5; + num_state = 4; + chain_state = REG_READ(ah, dcs_reg); + if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos)) + goto hang_check_iter; + + ath_dbg(ath9k_hw_common(ah), RESET, + "MAC Hang signature 1 not found\n"); + return false; + +hang_check_iter: + ath_dbg(ath9k_hw_common(ah), RESET, + "DCU registers: chain %08x complete %08x Hang: state %d pos %d\n", + chain_state, comp_state, hang_state, hang_pos); + + for (i = 0; i < NUM_STATUS_READS; i++) { + chain_state = REG_READ(ah, dcs_reg); + chain_state = (chain_state >> (5 * hang_pos)) & 0x1f; + comp_state = REG_READ(ah, AR_DMADBG_6); + + if (((comp_state & DCU_COMPLETE_STATE_MASK) != + DCU_COMPLETE_STATE) || + (chain_state != hang_state)) + return false; + } + + ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature 1 found\n"); + + return true; +} + bool ath9k_hw_check_alive(struct ath_hw *ah) { int count = 50; u32 reg; + if (AR_SREV_9300(ah)) + return !ath9k_hw_detect_mac_hang(ah); + if (AR_SREV_9285_12_OR_LATER(ah)) return true; @@ -1546,6 +1690,10 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) if (chan->channel == ah->curchan->channel) goto fail; + if ((ah->curchan->channelFlags | chan->channelFlags) & + (CHANNEL_HALF | CHANNEL_QUARTER)) + goto fail; + if ((chan->channelFlags & CHANNEL_ALL) != (ah->curchan->channelFlags & CHANNEL_ALL)) goto fail; @@ -1557,10 +1705,10 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan) * For AR9462, make sure that calibration data for * re-using are present. */ - if (AR_SREV_9462(ah) && (!ah->caldata || - !ah->caldata->done_txiqcal_once || - !ah->caldata->done_txclcal_once || - !ah->caldata->rtt_hist.num_readings)) + if (AR_SREV_9462(ah) && (ah->caldata && + (!ah->caldata->done_txiqcal_once || + !ah->caldata->done_txclcal_once || + !ah->caldata->rtt_done))) goto fail; ath_dbg(common, RESET, "FastChannelChange for %d -> %d\n", @@ -1796,7 +1944,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (caldata) { caldata->done_txiqcal_once = false; caldata->done_txclcal_once = false; - caldata->rtt_hist.num_readings = 0; } if (!ath9k_hw_init_cal(ah, chan)) return -EIO; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index e88f182ff45c..b620c557c2a6 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -348,12 +348,6 @@ enum ath9k_int { CHANNEL_HT40MINUS) #define MAX_RTT_TABLE_ENTRY 6 -#define RTT_HIST_MAX 3 -struct ath9k_rtt_hist { - u32 table[AR9300_MAX_CHAINS][RTT_HIST_MAX][MAX_RTT_TABLE_ENTRY]; - u8 num_readings; -}; - #define MAX_IQCAL_MEASUREMENT 8 #define MAX_CL_TAB_ENTRY 16 @@ -363,6 +357,7 @@ struct ath9k_hw_cal_data { int32_t CalValid; int8_t iCoff; int8_t qCoff; + bool rtt_done; bool paprd_done; bool nfcal_pending; bool nfcal_interference; @@ -373,8 +368,8 @@ struct ath9k_hw_cal_data { u32 num_measures[AR9300_MAX_CHAINS]; int tx_corr_coeff[MAX_IQCAL_MEASUREMENT][AR9300_MAX_CHAINS]; u32 tx_clcal[AR9300_MAX_CHAINS][MAX_CL_TAB_ENTRY]; + u32 rtt_table[AR9300_MAX_CHAINS][MAX_RTT_TABLE_ENTRY]; struct ath9k_nfcal_hist nfCalHist[NUM_NF_READINGS]; - struct ath9k_rtt_hist rtt_hist; }; struct ath9k_channel { @@ -708,7 +703,6 @@ struct ath_hw { struct ar5416Stats stats; struct ath9k_tx_queue_info txq[ATH9K_NUM_TX_QUEUES]; - int16_t curchan_rad_index; enum ath9k_int imask; u32 imrs2_reg; u32 txok_interrupt_mask; @@ -762,11 +756,6 @@ struct ath_hw { u32 sta_id1_defaults; u32 misc_mode; - enum { - AUTO_32KHZ, - USE_32KHZ, - DONT_USE_32KHZ, - } enable_32kHz_clock; /* Private to hardware code */ struct ath_hw_private_ops private_ops; @@ -783,7 +772,6 @@ struct ath_hw { u32 *analogBank7Data; u32 *bank6Temp; - u8 txpower_limit; int coverage_class; u32 slottime; u32 globaltxtimeout; @@ -848,7 +836,6 @@ struct ath_hw { struct ath_gen_timer_table hw_gen_timers; struct ar9003_txs *ts_ring; - void *ts_start; u32 ts_paddr_start; u32 ts_paddr_end; u16 ts_tail; @@ -915,7 +902,6 @@ static inline u8 get_streams(int mask) } /* Initialization, Detach, Reset */ -const char *ath9k_hw_probe(u16 vendorid, u16 devid); void ath9k_hw_deinit(struct ath_hw *ah); int ath9k_hw_init(struct ath_hw *ah); int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, @@ -932,6 +918,8 @@ void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val); void ath9k_hw_setantenna(struct ath_hw *ah, u32 antenna); /* General Operation */ +void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan, + int hw_delay); bool ath9k_hw_wait(struct ath_hw *ah, u32 reg, u32 mask, u32 val, u32 timeout); void ath9k_hw_write_array(struct ath_hw *ah, struct ar5416IniArray *array, int column, unsigned int *writecnt); @@ -965,6 +953,13 @@ bool ath9k_hw_check_alive(struct ath_hw *ah); bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode); +#ifdef CONFIG_ATH9K_DEBUGFS +void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause); +#else +static inline void ath9k_debug_sync_cause(struct ath_common *common, + u32 sync_cause) {} +#endif + /* Generic hw timer primitives */ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, void (*trigger)(void *), @@ -1012,7 +1007,6 @@ int ar9003_paprd_create_curve(struct ath_hw *ah, int ar9003_paprd_setup_gain_table(struct ath_hw *ah, int chain); int ar9003_paprd_init_table(struct ath_hw *ah); bool ar9003_paprd_is_done(struct ath_hw *ah); -void ar9003_hw_set_paprd_txdesc(struct ath_hw *ah, void *ds, u8 chains); /* Hardware family op attach helpers */ void ar5008_hw_attach_phy_ops(struct ath_hw *ah); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index cb006458fc4b..dee9e092449a 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/dma-mapping.h> #include <linux/slab.h> #include <linux/ath9k_platform.h> @@ -519,6 +521,8 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, atomic_set(&ah->intr_ref_cnt, -1); sc->sc_ah = ah; + sc->dfs_detector = dfs_pattern_detector_init(NL80211_DFS_UNSET); + if (!pdata) { ah->ah_flags |= AH_USE_EEPROM; sc->sc_ah->led_pin = -1; @@ -642,6 +646,24 @@ void ath9k_reload_chainmask_settings(struct ath_softc *sc) setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); } +static const struct ieee80211_iface_limit if_limits[] = { + { .max = 2048, .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_WDS) }, + { .max = 8, .types = +#ifdef CONFIG_MAC80211_MESH + BIT(NL80211_IFTYPE_MESH_POINT) | +#endif + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) }, +}; + +static const struct ieee80211_iface_combination if_comb = { + .limits = if_limits, + .n_limits = ARRAY_SIZE(if_limits), + .max_interfaces = 2048, + .num_different_channels = 1, +}; void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) { @@ -671,11 +693,15 @@ void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_MESH_POINT); + hw->wiphy->iface_combinations = &if_comb; + hw->wiphy->n_iface_combinations = 1; + if (AR_SREV_5416(sc->sc_ah)) hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; hw->wiphy->flags |= WIPHY_FLAG_IBSS_RSN; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; + hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; hw->queues = 4; hw->max_rates = 4; @@ -779,6 +805,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, goto error_world; } + setup_timer(&sc->rx_poll_timer, ath_rx_poll, (unsigned long)sc); sc->last_rssi = ATH_RSSI_DUMMY_MARKER; ath_init_leds(sc); @@ -821,6 +848,8 @@ static void ath9k_deinit_softc(struct ath_softc *sc) ath_tx_cleanupq(sc, &sc->tx.txq[i]); ath9k_hw_deinit(sc->sc_ah); + if (sc->dfs_detector != NULL) + sc->dfs_detector->exit(sc->dfs_detector); kfree(sc->sc_ah); sc->sc_ah = NULL; @@ -866,17 +895,14 @@ static int __init ath9k_init(void) /* Register rate control algorithm */ error = ath_rate_control_register(); if (error != 0) { - printk(KERN_ERR - "ath9k: Unable to register rate control " - "algorithm: %d\n", - error); + pr_err("Unable to register rate control algorithm: %d\n", + error); goto err_out; } error = ath_pci_init(); if (error < 0) { - printk(KERN_ERR - "ath9k: No PCI devices found, driver not installed.\n"); + pr_err("No PCI devices found, driver not installed\n"); error = -ENODEV; goto err_rate_unregister; } @@ -905,6 +931,6 @@ static void __exit ath9k_exit(void) ath_ahb_exit(); ath_pci_exit(); ath_rate_control_unregister(); - printk(KERN_INFO "%s: Driver unloaded\n", dev_info); + pr_info("%s: Driver unloaded\n", dev_info); } module_exit(ath9k_exit); diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index f7bd2532269c..04ef775ccee1 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -133,8 +133,16 @@ EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel); void ath9k_hw_abort_tx_dma(struct ath_hw *ah) { + int maxdelay = 1000; int i, q; + if (ah->curchan) { + if (IS_CHAN_HALF_RATE(ah->curchan)) + maxdelay *= 2; + else if (IS_CHAN_QUARTER_RATE(ah->curchan)) + maxdelay *= 4; + } + REG_WRITE(ah, AR_Q_TXD, AR_Q_TXD_M); REG_SET_BIT(ah, AR_PCU_MISC, AR_PCU_FORCE_QUIET_COLL | AR_PCU_CLEAR_VMF); @@ -142,7 +150,7 @@ void ath9k_hw_abort_tx_dma(struct ath_hw *ah) REG_SET_BIT(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_IGNORE_BACKOFF); for (q = 0; q < AR_NUM_QCU; q++) { - for (i = 0; i < 1000; i++) { + for (i = 0; i < maxdelay; i++) { if (i) udelay(5); diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 798ea57252b4..4de4473776ac 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -113,21 +113,25 @@ void ath9k_ps_restore(struct ath_softc *sc) struct ath_common *common = ath9k_hw_common(sc->sc_ah); enum ath9k_power_mode mode; unsigned long flags; + bool reset; spin_lock_irqsave(&sc->sc_pm_lock, flags); if (--sc->ps_usecount != 0) goto unlock; - if (sc->ps_idle && (sc->ps_flags & PS_WAIT_FOR_TX_ACK)) + if (sc->ps_idle) { + ath9k_hw_setrxabort(sc->sc_ah, 1); + ath9k_hw_stopdmarecv(sc->sc_ah, &reset); mode = ATH9K_PM_FULL_SLEEP; - else if (sc->ps_enabled && - !(sc->ps_flags & (PS_WAIT_FOR_BEACON | - PS_WAIT_FOR_CAB | - PS_WAIT_FOR_PSPOLL_DATA | - PS_WAIT_FOR_TX_ACK))) + } else if (sc->ps_enabled && + !(sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK))) { mode = ATH9K_PM_NETWORK_SLEEP; - else + } else { goto unlock; + } spin_lock(&common->cc_lock); ath_hw_cycle_counters_update(common); @@ -235,21 +239,23 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - bool ret; + bool ret = true; ieee80211_stop_queues(sc->hw); sc->hw_busy_count = 0; del_timer_sync(&common->ani.timer); + del_timer_sync(&sc->rx_poll_timer); ath9k_debug_samp_bb_mac(sc); ath9k_hw_disable_interrupts(ah); - ret = ath_drain_all_txq(sc, retry_tx); - if (!ath_stoprecv(sc)) ret = false; + if (!ath_drain_all_txq(sc, retry_tx)) + ret = false; + if (!flush) { if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ath_rx_tasklet(sc, 1, true); @@ -282,6 +288,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start) ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0); ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2); + ath_start_rx_poll(sc, 3); if (!common->disable_ani) ath_start_ani(common); } @@ -690,17 +697,6 @@ void ath9k_tasklet(unsigned long data) goto out; } - /* - * Only run the baseband hang check if beacons stop working in AP or - * IBSS mode, because it has a high false positive rate. For station - * mode it should not be necessary, since the upper layers will detect - * this through a beacon miss automatically and the following channel - * change will trigger a hardware reset anyway - */ - if (ath9k_hw_numtxpending(ah, sc->beacon.beaconq) != 0 && - !ath9k_hw_check_alive(ah)) - ieee80211_queue_work(sc->hw, &sc->hw_check_work); - if ((status & ATH9K_INT_TSFOOR) && sc->ps_enabled) { /* * TSF sync does not look correct; remain awake to sync with @@ -912,10 +908,19 @@ void ath_hw_check(struct work_struct *work) struct ath_common *common = ath9k_hw_common(sc->sc_ah); unsigned long flags; int busy; + u8 is_alive, nbeacon = 1; ath9k_ps_wakeup(sc); - if (ath9k_hw_check_alive(sc->sc_ah)) + is_alive = ath9k_hw_check_alive(sc->sc_ah); + + if (is_alive && !AR_SREV_9300(sc->sc_ah)) goto out; + else if (!is_alive && AR_SREV_9300(sc->sc_ah)) { + ath_dbg(common, RESET, + "DCU stuck is detected. Schedule chip reset\n"); + RESET_STAT_INC(sc, RESET_TYPE_MAC_HANG); + goto sched_reset; + } spin_lock_irqsave(&common->cc_lock, flags); busy = ath_update_survey_stats(sc); @@ -926,12 +931,18 @@ void ath_hw_check(struct work_struct *work) if (busy >= 99) { if (++sc->hw_busy_count >= 3) { RESET_STAT_INC(sc, RESET_TYPE_BB_HANG); - ieee80211_queue_work(sc->hw, &sc->hw_reset_work); + goto sched_reset; } - - } else if (busy >= 0) + } else if (busy >= 0) { sc->hw_busy_count = 0; + nbeacon = 3; + } + ath_start_rx_poll(sc, nbeacon); + goto out; + +sched_reset: + ieee80211_queue_work(sc->hw, &sc->hw_reset_work); out: ath9k_ps_restore(sc); } @@ -1094,14 +1105,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) } } - /* - * Cannot tx while the hardware is in full sleep, it first needs a full - * chip reset to recover from that - */ - if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_FULL_SLEEP)) - goto exit; - - if (unlikely(sc->sc_ah->power_mode != ATH9K_PM_AWAKE)) { + if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_NETWORK_SLEEP)) { /* * We are using PS-Poll and mac80211 can request TX while in * power save mode. Need to wake up hardware for the TX to be @@ -1120,12 +1124,21 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) } /* * The actual restore operation will happen only after - * the sc_flags bit is cleared. We are just dropping + * the ps_flags bit is cleared. We are just dropping * the ps_usecount here. */ ath9k_ps_restore(sc); } + /* + * Cannot tx while the hardware is in full sleep, it first needs a full + * chip reset to recover from that + */ + if (unlikely(sc->sc_ah->power_mode == ATH9K_PM_FULL_SLEEP)) { + ath_err(common, "TX while HW is in FULL_SLEEP mode\n"); + goto exit; + } + memset(&txctl, 0, sizeof(struct ath_tx_control)); txctl.txq = sc->tx.txq_map[skb_get_queue_mapping(skb)]; @@ -1133,6 +1146,7 @@ static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (ath_tx_start(hw, skb, &txctl) != 0) { ath_dbg(common, XMIT, "TX failed\n"); + TX_STAT_INC(txctl.txq->axq_qnum, txfailed); goto exit; } @@ -1151,6 +1165,7 @@ static void ath9k_stop(struct ieee80211_hw *hw) mutex_lock(&sc->mutex); ath_cancel_work(sc); + del_timer_sync(&sc->rx_poll_timer); if (sc->sc_flags & SC_OP_INVALID) { ath_dbg(common, ANY, "Device not present\n"); @@ -1237,7 +1252,6 @@ static void ath9k_reclaim_beacon(struct ath_softc *sc, ath9k_set_beaconing_status(sc, false); ath_beacon_return(sc, avp); ath9k_set_beaconing_status(sc, true); - sc->sc_flags &= ~SC_OP_BEACONS; } static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) @@ -1368,21 +1382,31 @@ static void ath9k_do_vif_add_setup(struct ieee80211_hw *hw, ath9k_calculate_summary_state(hw, vif); if (ath9k_uses_beacons(vif->type)) { - int error; - /* This may fail because upper levels do not have beacons - * properly configured yet. That's OK, we assume it - * will be properly configured and then we will be notified - * in the info_changed method and set up beacons properly - * there. - */ + /* Reserve a beacon slot for the vif */ ath9k_set_beaconing_status(sc, false); - error = ath_beacon_alloc(sc, vif); - if (!error) - ath_beacon_config(sc, vif); + ath_beacon_alloc(sc, vif); ath9k_set_beaconing_status(sc, true); } } +void ath_start_rx_poll(struct ath_softc *sc, u8 nbeacon) +{ + if (!AR_SREV_9300(sc->sc_ah)) + return; + + if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF)) + return; + + mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies + (nbeacon * sc->cur_beacon_conf.beacon_interval)); +} + +void ath_rx_poll(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *)data; + + ieee80211_queue_work(sc->hw, &sc->hw_check_work); +} static int ath9k_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) @@ -1511,6 +1535,7 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw, static void ath9k_enable_ps(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); sc->ps_enabled = true; if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { @@ -1520,11 +1545,13 @@ static void ath9k_enable_ps(struct ath_softc *sc) } ath9k_hw_setrxabort(ah, 1); } + ath_dbg(common, PS, "PowerSave enabled\n"); } static void ath9k_disable_ps(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); sc->ps_enabled = false; ath9k_hw_setpower(ah, ATH9K_PM_AWAKE); @@ -1539,7 +1566,7 @@ static void ath9k_disable_ps(struct ath_softc *sc) ath9k_hw_set_interrupts(ah); } } - + ath_dbg(common, PS, "PowerSave disabled\n"); } static int ath9k_config(struct ieee80211_hw *hw, u32 changed) @@ -1911,6 +1938,8 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) sc->last_rssi = ATH_RSSI_DUMMY_MARKER; sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER; + ath_start_rx_poll(sc, 3); + if (!common->disable_ani) { sc->sc_flags |= SC_OP_ANI_RUN; ath_start_ani(common); @@ -1950,6 +1979,7 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif) /* Stop ANI */ sc->sc_flags &= ~SC_OP_ANI_RUN; del_timer_sync(&common->ani.timer); + del_timer_sync(&sc->rx_poll_timer); memset(&sc->caldata, 0, sizeof(sc->caldata)); } } @@ -1964,7 +1994,6 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; int slottime; - int error; ath9k_ps_wakeup(sc); mutex_lock(&sc->mutex); @@ -1993,16 +2022,29 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } else { sc->sc_flags &= ~SC_OP_ANI_RUN; del_timer_sync(&common->ani.timer); + del_timer_sync(&sc->rx_poll_timer); } } - /* Enable transmission of beacons (AP, IBSS, MESH) */ - if ((changed & BSS_CHANGED_BEACON) || - ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon)) { + /* + * In case of AP mode, the HW TSF has to be reset + * when the beacon interval changes. + */ + if ((changed & BSS_CHANGED_BEACON_INT) && + (vif->type == NL80211_IFTYPE_AP)) + sc->sc_flags |= SC_OP_TSF_RESET; + + /* Configure beaconing (AP, IBSS, MESH) */ + if (ath9k_uses_beacons(vif->type) && + ((changed & BSS_CHANGED_BEACON) || + (changed & BSS_CHANGED_BEACON_ENABLED) || + (changed & BSS_CHANGED_BEACON_INT))) { ath9k_set_beaconing_status(sc, false); - error = ath_beacon_alloc(sc, vif); - if (!error) - ath_beacon_config(sc, vif); + if (bss_conf->enable_beacon) + ath_beacon_alloc(sc, vif); + else + avp->is_bslot_active = false; + ath_beacon_config(sc, vif); ath9k_set_beaconing_status(sc, true); } @@ -2025,30 +2067,6 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } } - /* Disable transmission of beacons */ - if ((changed & BSS_CHANGED_BEACON_ENABLED) && - !bss_conf->enable_beacon) { - ath9k_set_beaconing_status(sc, false); - avp->is_bslot_active = false; - ath9k_set_beaconing_status(sc, true); - } - - if (changed & BSS_CHANGED_BEACON_INT) { - /* - * In case of AP mode, the HW TSF has to be reset - * when the beacon interval changes. - */ - if (vif->type == NL80211_IFTYPE_AP) { - sc->sc_flags |= SC_OP_TSF_RESET; - ath9k_set_beaconing_status(sc, false); - error = ath_beacon_alloc(sc, vif); - if (!error) - ath_beacon_config(sc, vif); - ath9k_set_beaconing_status(sc, true); - } else - ath_beacon_config(sc, vif); - } - mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); } diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 77dc327def8d..a856b51255f4 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/nl80211.h> #include <linux/pci.h> #include <linux/pci-aspm.h> @@ -171,14 +173,13 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { - printk(KERN_ERR "ath9k: 32-bit DMA not available\n"); + pr_err("32-bit DMA not available\n"); goto err_dma; } ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); if (ret) { - printk(KERN_ERR "ath9k: 32-bit DMA consistent " - "DMA enable failed\n"); + pr_err("32-bit DMA consistent DMA enable failed\n"); goto err_dma; } @@ -224,7 +225,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mem = pci_iomap(pdev, 0, 0); if (!mem) { - printk(KERN_ERR "PCI memory map error\n") ; + pr_err("PCI memory map error\n") ; ret = -EIO; goto err_iomap; } diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 08bb45532701..92a6c0a87f89 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -1436,7 +1436,7 @@ static void ath_rate_init(void *priv, struct ieee80211_supported_band *sband, static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, - u32 changed, enum nl80211_channel_type oper_chan_type) + u32 changed) { struct ath_softc *sc = priv; struct ath_rate_priv *ath_rc_priv = priv_sta; @@ -1447,12 +1447,11 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, /* FIXME: Handle AP mode later when we support CWM */ - if (changed & IEEE80211_RC_HT_CHANGED) { + if (changed & IEEE80211_RC_BW_CHANGED) { if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION) return; - if (oper_chan_type == NL80211_CHAN_HT40MINUS || - oper_chan_type == NL80211_CHAN_HT40PLUS) + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) oper_cw40 = true; if (oper_cw40) diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 1c4583c7ff7c..e1fcc68124dc 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -812,6 +812,7 @@ static bool ath9k_rx_accept(struct ath_common *common, is_valid_tkip = rx_stats->rs_keyix != ATH9K_RXKEYIX_INVALID && test_bit(rx_stats->rs_keyix, common->tkip_keymap); strip_mic = is_valid_tkip && ieee80211_is_data(fc) && + ieee80211_has_protected(fc) && !(rx_stats->rs_status & (ATH9K_RXERR_DECRYPT | ATH9K_RXERR_CRC | ATH9K_RXERR_MIC | ATH9K_RXERR_KEYMISS)); @@ -824,15 +825,20 @@ static bool ath9k_rx_accept(struct ath_common *common, if (rx_stats->rs_keyix == ATH9K_RXKEYIX_INVALID) rx_stats->rs_status &= ~ATH9K_RXERR_KEYMISS; - if (!rx_stats->rs_datalen) + if (!rx_stats->rs_datalen) { + RX_STAT_INC(rx_len_err); return false; + } + /* * rs_status follows rs_datalen so if rs_datalen is too large * we can take a hint that hardware corrupted it, so ignore * those frames. */ - if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len)) + if (rx_stats->rs_datalen > (common->rx_bufsize - rx_status_len)) { + RX_STAT_INC(rx_len_err); return false; + } /* Only use error bits from the last fragment */ if (rx_stats->rs_more) @@ -902,6 +908,7 @@ static int ath9k_process_rate(struct ath_common *common, struct ieee80211_supported_band *sband; enum ieee80211_band band; unsigned int i = 0; + struct ath_softc __maybe_unused *sc = common->priv; band = hw->conf.channel->band; sband = hw->wiphy->bands[band]; @@ -936,7 +943,7 @@ static int ath9k_process_rate(struct ath_common *common, ath_dbg(common, ANY, "unsupported hw bitrate detected 0x%02x using 1 Mbit\n", rx_stats->rs_rate); - + RX_STAT_INC(rx_rate_err); return -EINVAL; } @@ -1823,10 +1830,14 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) hdr = (struct ieee80211_hdr *) (hdr_skb->data + rx_status_len); rxs = IEEE80211_SKB_RXCB(hdr_skb); - if (ieee80211_is_beacon(hdr->frame_control) && - !is_zero_ether_addr(common->curbssid) && - !compare_ether_addr(hdr->addr3, common->curbssid)) - rs.is_mybeacon = true; + if (ieee80211_is_beacon(hdr->frame_control)) { + RX_STAT_INC(rx_beacons); + if (!is_zero_ether_addr(common->curbssid) && + ether_addr_equal(hdr->addr3, common->curbssid)) + rs.is_mybeacon = true; + else + rs.is_mybeacon = false; + } else rs.is_mybeacon = false; @@ -1836,8 +1847,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) * If we're asked to flush receive queue, directly * chain it back at the queue without processing it. */ - if (sc->sc_flags & SC_OP_RXFLUSH) + if (sc->sc_flags & SC_OP_RXFLUSH) { + RX_STAT_INC(rx_drop_rxflush); goto requeue_drop_frag; + } memset(rxs, 0, sizeof(struct ieee80211_rx_status)); @@ -1855,6 +1868,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) if (retval) goto requeue_drop_frag; + if (rs.is_mybeacon) { + sc->hw_busy_count = 0; + ath_start_rx_poll(sc, 3); + } /* Ensure we always have an skb to requeue once we are done * processing the current buffer's skb */ requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC); @@ -1863,8 +1880,10 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) * tell hardware it can give us a new frame using the old * skb and put it at the tail of the sc->rx.rxbuf list for * processing. */ - if (!requeue_skb) + if (!requeue_skb) { + RX_STAT_INC(rx_oom_err); goto requeue_drop_frag; + } /* Unmap the frame */ dma_unmap_single(sc->dev, bf->bf_buf_addr, @@ -1895,6 +1914,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) } if (rs.rs_more) { + RX_STAT_INC(rx_frags); /* * rs_more indicates chained descriptors which can be * used to link buffers together for a sort of @@ -1904,6 +1924,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) /* too many fragments - cannot handle frame */ dev_kfree_skb_any(sc->rx.frag); dev_kfree_skb_any(skb); + RX_STAT_INC(rx_too_many_frags_err); skb = NULL; } sc->rx.frag = skb; @@ -1915,6 +1936,7 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp) if (pskb_expand_head(hdr_skb, 0, space, GFP_ATOMIC) < 0) { dev_kfree_skb(skb); + RX_STAT_INC(rx_oom_err); goto requeue_drop_frag; } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 23eaa1b26ebe..d59dd01d6cde 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -64,7 +64,8 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid, static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, - struct sk_buff *skb); + struct sk_buff *skb, + bool dequeue); enum { MCS_HT20, @@ -811,7 +812,7 @@ static enum ATH_AGGR_STATUS ath_tx_form_aggr(struct ath_softc *sc, fi = get_frame_info(skb); bf = fi->bf; if (!fi->bf) - bf = ath_tx_setup_buffer(sc, txq, tid, skb); + bf = ath_tx_setup_buffer(sc, txq, tid, skb, true); if (!bf) continue; @@ -1726,7 +1727,7 @@ static void ath_tx_send_ampdu(struct ath_softc *sc, struct ath_atx_tid *tid, return; } - bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb); + bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb, false); if (!bf) return; @@ -1753,7 +1754,7 @@ static void ath_tx_send_normal(struct ath_softc *sc, struct ath_txq *txq, bf = fi->bf; if (!bf) - bf = ath_tx_setup_buffer(sc, txq, tid, skb); + bf = ath_tx_setup_buffer(sc, txq, tid, skb, false); if (!bf) return; @@ -1814,7 +1815,8 @@ u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate) static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, struct ath_txq *txq, struct ath_atx_tid *tid, - struct sk_buff *skb) + struct sk_buff *skb, + bool dequeue) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_frame_info *fi = get_frame_info(skb); @@ -1863,6 +1865,8 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_softc *sc, return bf; error: + if (dequeue) + __skb_unlink(skb, &tid->buf_q); dev_kfree_skb_any(skb); return NULL; } @@ -1893,7 +1897,7 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct sk_buff *skb, */ ath_tx_send_ampdu(sc, tid, skb, txctl); } else { - bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb); + bf = ath_tx_setup_buffer(sc, txctl->txq, tid, skb, false); if (!bf) return; diff --git a/drivers/net/wireless/ath/carl9170/cmd.h b/drivers/net/wireless/ath/carl9170/cmd.h index 885c42778b8b..65919c902f55 100644 --- a/drivers/net/wireless/ath/carl9170/cmd.h +++ b/drivers/net/wireless/ath/carl9170/cmd.h @@ -114,7 +114,7 @@ __regwrite_out : \ #define carl9170_regwrite_result() \ __err; \ -} while (0); +} while (0) #define carl9170_async_regwrite_get_buf() \ @@ -126,7 +126,7 @@ do { \ __err = -ENOMEM; \ goto __async_regwrite_out; \ } \ -} while (0); +} while (0) #define carl9170_async_regwrite_begin(carl) \ do { \ @@ -169,6 +169,6 @@ __async_regwrite_out: \ #define carl9170_async_regwrite_result() \ __err; \ -} while (0); +} while (0) #endif /* __CMD_H */ diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index cffde8d9a521..5c73c03872f3 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -355,6 +355,8 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len) ar->hw->wiphy->interface_modes |= if_comb_types; + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + #undef SUPPORTED return carl9170_fw_tx_sequence(ar); } diff --git a/drivers/net/wireless/ath/carl9170/rx.c b/drivers/net/wireless/ath/carl9170/rx.c index dc99030ea8b6..84b22eec7abd 100644 --- a/drivers/net/wireless/ath/carl9170/rx.c +++ b/drivers/net/wireless/ath/carl9170/rx.c @@ -538,7 +538,7 @@ static void carl9170_ps_beacon(struct ar9170 *ar, void *data, unsigned int len) return; /* and only beacons from the associated BSSID, please */ - if (compare_ether_addr(hdr->addr3, ar->common.curbssid) || + if (!ether_addr_equal(hdr->addr3, ar->common.curbssid) || !ar->common.curaid) return; diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index 89821e4835c7..888152ce3eca 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -1159,6 +1159,7 @@ static struct usb_driver carl9170_driver = { .resume = carl9170_usb_resume, .reset_resume = carl9170_usb_resume, #endif /* CONFIG_PM */ + .disable_hub_initiated_lpm = 1, }; module_usb_driver(carl9170_driver); diff --git a/drivers/net/wireless/ath/main.c b/drivers/net/wireless/ath/main.c index ea2c737138d3..8e99540cd90e 100644 --- a/drivers/net/wireless/ath/main.c +++ b/drivers/net/wireless/ath/main.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> @@ -49,7 +51,7 @@ struct sk_buff *ath_rxbuf_alloc(struct ath_common *common, if (off != 0) skb_reserve(skb, common->cachelsz - off); } else { - printk(KERN_ERR "skbuff alloc of size %u failed\n", len); + pr_err("skbuff alloc of size %u failed\n", len); return NULL; } diff --git a/drivers/net/wireless/ath/regd.c b/drivers/net/wireless/ath/regd.c index 10dea37431b3..d81698015bf7 100644 --- a/drivers/net/wireless/ath/regd.c +++ b/drivers/net/wireless/ath/regd.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/export.h> #include <net/cfg80211.h> @@ -562,7 +564,7 @@ static int __ath_regd_init(struct ath_regulatory *reg) printk(KERN_DEBUG "ath: EEPROM regdomain: 0x%0x\n", reg->current_rd); if (!ath_regd_is_eeprom_valid(reg)) { - printk(KERN_ERR "ath: Invalid EEPROM contents\n"); + pr_err("Invalid EEPROM contents\n"); return -EINVAL; } |