diff options
author | David S. Miller <davem@davemloft.net> | 2010-03-29 22:50:10 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-03-29 22:50:10 +0200 |
commit | 7905e357ebe67a26d9dc8caa1a0b8346431b5f0d (patch) | |
tree | 134442df2f062caa6cebda1b352948b8209efcec | |
parent | netxen: fix interrupt for NX2031 (diff) | |
parent | iwlwifi: fix build error for CONFIG_IWLAGN=n (diff) | |
download | linux-7905e357ebe67a26d9dc8caa1a0b8346431b5f0d.tar.xz linux-7905e357ebe67a26d9dc8caa1a0b8346431b5f0d.zip |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
82 files changed, 9032 insertions, 1503 deletions
diff --git a/drivers/net/ps3_gelic_wireless.c b/drivers/net/ps3_gelic_wireless.c index 505f9a194369..a9153a3481e1 100644 --- a/drivers/net/ps3_gelic_wireless.c +++ b/drivers/net/ps3_gelic_wireless.c @@ -2279,26 +2279,25 @@ void gelic_wl_interrupt(struct net_device *netdev, u64 status) /* * driver helpers */ -#define IW_IOCTL(n) [(n) - SIOCSIWCOMMIT] static const iw_handler gelic_wl_wext_handler[] = { - IW_IOCTL(SIOCGIWNAME) = gelic_wl_get_name, - IW_IOCTL(SIOCGIWRANGE) = gelic_wl_get_range, - IW_IOCTL(SIOCSIWSCAN) = gelic_wl_set_scan, - IW_IOCTL(SIOCGIWSCAN) = gelic_wl_get_scan, - IW_IOCTL(SIOCSIWAUTH) = gelic_wl_set_auth, - IW_IOCTL(SIOCGIWAUTH) = gelic_wl_get_auth, - IW_IOCTL(SIOCSIWESSID) = gelic_wl_set_essid, - IW_IOCTL(SIOCGIWESSID) = gelic_wl_get_essid, - IW_IOCTL(SIOCSIWENCODE) = gelic_wl_set_encode, - IW_IOCTL(SIOCGIWENCODE) = gelic_wl_get_encode, - IW_IOCTL(SIOCSIWAP) = gelic_wl_set_ap, - IW_IOCTL(SIOCGIWAP) = gelic_wl_get_ap, - IW_IOCTL(SIOCSIWENCODEEXT) = gelic_wl_set_encodeext, - IW_IOCTL(SIOCGIWENCODEEXT) = gelic_wl_get_encodeext, - IW_IOCTL(SIOCSIWMODE) = gelic_wl_set_mode, - IW_IOCTL(SIOCGIWMODE) = gelic_wl_get_mode, - IW_IOCTL(SIOCGIWNICKN) = gelic_wl_get_nick, + IW_HANDLER(SIOCGIWNAME, gelic_wl_get_name), + IW_HANDLER(SIOCGIWRANGE, gelic_wl_get_range), + IW_HANDLER(SIOCSIWSCAN, gelic_wl_set_scan), + IW_HANDLER(SIOCGIWSCAN, gelic_wl_get_scan), + IW_HANDLER(SIOCSIWAUTH, gelic_wl_set_auth), + IW_HANDLER(SIOCGIWAUTH, gelic_wl_get_auth), + IW_HANDLER(SIOCSIWESSID, gelic_wl_set_essid), + IW_HANDLER(SIOCGIWESSID, gelic_wl_get_essid), + IW_HANDLER(SIOCSIWENCODE, gelic_wl_set_encode), + IW_HANDLER(SIOCGIWENCODE, gelic_wl_get_encode), + IW_HANDLER(SIOCSIWAP, gelic_wl_set_ap), + IW_HANDLER(SIOCGIWAP, gelic_wl_get_ap), + IW_HANDLER(SIOCSIWENCODEEXT, gelic_wl_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, gelic_wl_get_encodeext), + IW_HANDLER(SIOCSIWMODE, gelic_wl_set_mode), + IW_HANDLER(SIOCGIWMODE, gelic_wl_get_mode), + IW_HANDLER(SIOCGIWNICKN, gelic_wl_get_nick), }; static const struct iw_handler_def gelic_wl_wext_handler_def = { diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 4e7a7fd695c8..0a75be027afa 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -3,7 +3,7 @@ menuconfig ATH_COMMON depends on CFG80211 ---help--- This will enable the support for the Atheros wireless drivers. - ath5k, ath9k and ar9170 drivers share some common code, this option + ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option enables the common ath.ko module which shares common helpers. For more information and documentation on this module you can visit: diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 5774cea23a3b..35f23bdc442f 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -32,3 +32,24 @@ config ATH9K_DEBUGFS Also required for changing debug message flags at run time. +config ATH9K_HTC + tristate "Atheros HTC based wireless cards support" + depends on USB && MAC80211 + select ATH9K_HW + select MAC80211_LEDS + select LEDS_CLASS + select NEW_LEDS + select ATH9K_COMMON + ---help--- + Support for Atheros HTC based cards. + Chipsets supported: AR9271 + + For more information: http://wireless.kernel.org/en/users/Drivers/ath9k_htc + + The built module will be ath9k_htc. + +config ATH9K_HTC_DEBUGFS + bool "Atheros ath9k_htc debugging" + depends on ATH9K_HTC && DEBUG_FS + ---help--- + Say Y, if you need access to ath9k_htc's statistics. diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 6b50d5eb9ec3..97133beda269 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -28,3 +28,13 @@ obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o ath9k_common-y:= common.o + +ath9k_htc-y += htc_hst.o \ + hif_usb.o \ + wmi.o \ + htc_drv_txrx.o \ + htc_drv_main.o \ + htc_drv_beacon.o \ + htc_drv_init.o + +obj-$(CONFIG_ATH9K_HTC) += ath9k_htc.o diff --git a/drivers/net/wireless/ath/ath9k/calib.c b/drivers/net/wireless/ath/ath9k/calib.c index 238a5744d8e9..d5026e4f484b 100644 --- a/drivers/net/wireless/ath/ath9k/calib.c +++ b/drivers/net/wireless/ath/ath9k/calib.c @@ -101,9 +101,13 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, nf = 0 - ((nf ^ 0x1ff) + 1); ath_print(common, ATH_DBG_CALIBRATE, "NF calibrated [ctl] [chain 0] is %d\n", nf); + + if (AR_SREV_9271(ah) && (nf >= -114)) + nf = -116; + nfarray[0] = nf; - if (!AR_SREV_9285(ah)) { + if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) { if (AR_SREV_9280_10_OR_LATER(ah)) nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR9280_PHY_CH1_MINCCA_PWR); @@ -139,9 +143,13 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah, nf = 0 - ((nf ^ 0x1ff) + 1); ath_print(common, ATH_DBG_CALIBRATE, "NF calibrated [ext] [chain 0] is %d\n", nf); + + if (AR_SREV_9271(ah) && (nf >= -114)) + nf = -116; + nfarray[3] = nf; - if (!AR_SREV_9285(ah)) { + if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) { if (AR_SREV_9280_10_OR_LATER(ah)) nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), AR9280_PHY_CH1_EXT_MINCCA_PWR); @@ -621,7 +629,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan) u8 chainmask, rx_chain_status; rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK); - if (AR_SREV_9285(ah)) + if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) chainmask = 0x9; else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) { if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4)) @@ -715,7 +723,7 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah) if (AR_SREV_9280(ah)) noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE; - else if (AR_SREV_9285(ah)) + else if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE; else if (AR_SREV_9287(ah)) noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE; @@ -1051,9 +1059,12 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan, /* Do NF cal only at longer intervals */ if (longcal) { /* Do periodic PAOffset Cal */ - if (AR_SREV_9271(ah)) - ath9k_hw_9271_pa_cal(ah, false); - else if (AR_SREV_9285_11_OR_LATER(ah)) { + if (AR_SREV_9271(ah)) { + if (!ah->pacal_info.skipcount) + ath9k_hw_9271_pa_cal(ah, false); + else + ah->pacal_info.skipcount--; + } else if (AR_SREV_9285_11_OR_LATER(ah)) { if (!ah->pacal_info.skipcount) ath9k_hw_9285_pa_cal(ah, false); else diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index 4d775ae141db..7902d287f671 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -286,6 +286,427 @@ int ath9k_cmn_padpos(__le16 frame_control) } EXPORT_SYMBOL(ath9k_cmn_padpos); +int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb) +{ + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + + if (tx_info->control.hw_key) { + if (tx_info->control.hw_key->alg == ALG_WEP) + return ATH9K_KEY_TYPE_WEP; + else if (tx_info->control.hw_key->alg == ALG_TKIP) + return ATH9K_KEY_TYPE_TKIP; + else if (tx_info->control.hw_key->alg == ALG_CCMP) + return ATH9K_KEY_TYPE_AES; + } + + return ATH9K_KEY_TYPE_CLEAR; +} +EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype); + +/* + * Calculate the RX filter to be set in the HW. + */ +u32 ath9k_cmn_calcrxfilter(struct ieee80211_hw *hw, struct ath_hw *ah, + unsigned int rxfilter) +{ +#define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR) + + u32 rfilt; + + rfilt = (ath9k_hw_getrxfilter(ah) & RX_FILTER_PRESERVE) + | ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST + | ATH9K_RX_FILTER_MCAST; + + /* If not a STA, enable processing of Probe Requests */ + if (ah->opmode != NL80211_IFTYPE_STATION) + rfilt |= ATH9K_RX_FILTER_PROBEREQ; + + /* + * Set promiscuous mode when FIF_PROMISC_IN_BSS is enabled for station + * mode interface or when in monitor mode. AP mode does not need this + * since it receives all in-BSS frames anyway. + */ + if (((ah->opmode != NL80211_IFTYPE_AP) && + (rxfilter & FIF_PROMISC_IN_BSS)) || + (ah->opmode == NL80211_IFTYPE_MONITOR)) + rfilt |= ATH9K_RX_FILTER_PROM; + + if (rxfilter & FIF_CONTROL) + rfilt |= ATH9K_RX_FILTER_CONTROL; + + if ((ah->opmode == NL80211_IFTYPE_STATION) && + !(rxfilter & FIF_BCN_PRBRESP_PROMISC)) + rfilt |= ATH9K_RX_FILTER_MYBEACON; + else + rfilt |= ATH9K_RX_FILTER_BEACON; + + if ((AR_SREV_9280_10_OR_LATER(ah) || + AR_SREV_9285_10_OR_LATER(ah)) && + (ah->opmode == NL80211_IFTYPE_AP) && + (rxfilter & FIF_PSPOLL)) + rfilt |= ATH9K_RX_FILTER_PSPOLL; + + if (conf_is_ht(&hw->conf)) + rfilt |= ATH9K_RX_FILTER_COMP_BAR; + + return rfilt; + +#undef RX_FILTER_PRESERVE +} +EXPORT_SYMBOL(ath9k_cmn_calcrxfilter); + +/* + * Recv initialization for opmode change. + */ +void ath9k_cmn_opmode_init(struct ieee80211_hw *hw, struct ath_hw *ah, + unsigned int rxfilter) +{ + struct ath_common *common = ath9k_hw_common(ah); + + u32 rfilt, mfilt[2]; + + /* configure rx filter */ + rfilt = ath9k_cmn_calcrxfilter(hw, ah, rxfilter); + ath9k_hw_setrxfilter(ah, rfilt); + + /* configure bssid mask */ + if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + ath_hw_setbssidmask(common); + + /* configure operational mode */ + ath9k_hw_setopmode(ah); + + /* Handle any link-level address change. */ + ath9k_hw_setmac(ah, common->macaddr); + + /* calculate and install multicast filter */ + mfilt[0] = mfilt[1] = ~0; + ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]); +} +EXPORT_SYMBOL(ath9k_cmn_opmode_init); + +static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + u32 chanmode = 0; + + switch (chan->band) { + case IEEE80211_BAND_2GHZ: + switch (channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + chanmode = CHANNEL_G_HT20; + break; + case NL80211_CHAN_HT40PLUS: + chanmode = CHANNEL_G_HT40PLUS; + break; + case NL80211_CHAN_HT40MINUS: + chanmode = CHANNEL_G_HT40MINUS; + break; + } + break; + case IEEE80211_BAND_5GHZ: + switch (channel_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + chanmode = CHANNEL_A_HT20; + break; + case NL80211_CHAN_HT40PLUS: + chanmode = CHANNEL_A_HT40PLUS; + break; + case NL80211_CHAN_HT40MINUS: + chanmode = CHANNEL_A_HT40MINUS; + break; + } + break; + default: + break; + } + + return chanmode; +} + +/* + * Update internal channel flags. + */ +void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw, + struct ath9k_channel *ichan) +{ + struct ieee80211_channel *chan = hw->conf.channel; + struct ieee80211_conf *conf = &hw->conf; + + ichan->channel = chan->center_freq; + ichan->chan = chan; + + if (chan->band == IEEE80211_BAND_2GHZ) { + ichan->chanmode = CHANNEL_G; + ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G; + } else { + ichan->chanmode = CHANNEL_A; + ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM; + } + + if (conf_is_ht(conf)) + ichan->chanmode = ath9k_get_extchanmode(chan, + conf->channel_type); +} +EXPORT_SYMBOL(ath9k_cmn_update_ichannel); + +/* + * Get the internal channel reference. + */ +struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, + struct ath_hw *ah) +{ + struct ieee80211_channel *curchan = hw->conf.channel; + struct ath9k_channel *channel; + u8 chan_idx; + + chan_idx = curchan->hw_value; + channel = &ah->channels[chan_idx]; + ath9k_cmn_update_ichannel(hw, channel); + + return channel; +} +EXPORT_SYMBOL(ath9k_cmn_get_curchannel); + +static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key, + struct ath9k_keyval *hk, const u8 *addr, + bool authenticator) +{ + struct ath_hw *ah = common->ah; + const u8 *key_rxmic; + const u8 *key_txmic; + + key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY; + key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY; + + if (addr == NULL) { + /* + * Group key installation - only two key cache entries are used + * regardless of splitmic capability since group key is only + * used either for TX or RX. + */ + if (authenticator) { + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic)); + } else { + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic)); + } + return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr); + } + if (!common->splitmic) { + /* TX and RX keys share the same key cache entry. */ + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic)); + return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr); + } + + /* Separate key cache entries for TX and RX */ + + /* TX key goes at first index, RX key at +32. */ + memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic)); + if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) { + /* TX MIC entry failed. No need to proceed further */ + ath_print(common, ATH_DBG_FATAL, + "Setting TX MIC Key Failed\n"); + return 0; + } + + memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic)); + /* XXX delete tx key on failure? */ + return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr); +} + +static int ath_reserve_key_cache_slot_tkip(struct ath_common *common) +{ + int i; + + for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { + if (test_bit(i, common->keymap) || + test_bit(i + 64, common->keymap)) + continue; /* At least one part of TKIP key allocated */ + if (common->splitmic && + (test_bit(i + 32, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + continue; /* At least one part of TKIP key allocated */ + + /* Found a free slot for a TKIP key */ + return i; + } + return -1; +} + +static int ath_reserve_key_cache_slot(struct ath_common *common) +{ + int i; + + /* First, try to find slots that would not be available for TKIP. */ + if (common->splitmic) { + for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) { + if (!test_bit(i, common->keymap) && + (test_bit(i + 32, common->keymap) || + test_bit(i + 64, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + return i; + if (!test_bit(i + 32, common->keymap) && + (test_bit(i, common->keymap) || + test_bit(i + 64, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + return i + 32; + if (!test_bit(i + 64, common->keymap) && + (test_bit(i , common->keymap) || + test_bit(i + 32, common->keymap) || + test_bit(i + 64 + 32, common->keymap))) + return i + 64; + if (!test_bit(i + 64 + 32, common->keymap) && + (test_bit(i, common->keymap) || + test_bit(i + 32, common->keymap) || + test_bit(i + 64, common->keymap))) + return i + 64 + 32; + } + } else { + for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) { + if (!test_bit(i, common->keymap) && + test_bit(i + 64, common->keymap)) + return i; + if (test_bit(i, common->keymap) && + !test_bit(i + 64, common->keymap)) + return i + 64; + } + } + + /* No partially used TKIP slots, pick any available slot */ + for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) { + /* Do not allow slots that could be needed for TKIP group keys + * to be used. This limitation could be removed if we know that + * TKIP will not be used. */ + if (i >= 64 && i < 64 + IEEE80211_WEP_NKID) + continue; + if (common->splitmic) { + if (i >= 32 && i < 32 + IEEE80211_WEP_NKID) + continue; + if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID) + continue; + } + + if (!test_bit(i, common->keymap)) + return i; /* Found a free slot for a key */ + } + + /* No free slot found */ + return -1; +} + +/* + * Configure encryption in the HW. + */ +int ath9k_cmn_key_config(struct ath_common *common, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath_hw *ah = common->ah; + struct ath9k_keyval hk; + const u8 *mac = NULL; + int ret = 0; + int idx; + + memset(&hk, 0, sizeof(hk)); + + switch (key->alg) { + case ALG_WEP: + hk.kv_type = ATH9K_CIPHER_WEP; + break; + case ALG_TKIP: + hk.kv_type = ATH9K_CIPHER_TKIP; + break; + case ALG_CCMP: + hk.kv_type = ATH9K_CIPHER_AES_CCM; + break; + default: + return -EOPNOTSUPP; + } + + hk.kv_len = key->keylen; + memcpy(hk.kv_val, key->key, key->keylen); + + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + /* For now, use the default keys for broadcast keys. This may + * need to change with virtual interfaces. */ + idx = key->keyidx; + } else if (key->keyidx) { + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + + if (vif->type != NL80211_IFTYPE_AP) { + /* Only keyidx 0 should be used with unicast key, but + * allow this for client mode for now. */ + idx = key->keyidx; + } else + return -EIO; + } else { + if (WARN_ON(!sta)) + return -EOPNOTSUPP; + mac = sta->addr; + + if (key->alg == ALG_TKIP) + idx = ath_reserve_key_cache_slot_tkip(common); + else + idx = ath_reserve_key_cache_slot(common); + if (idx < 0) + return -ENOSPC; /* no free key cache entries */ + } + + if (key->alg == ALG_TKIP) + ret = ath_setkey_tkip(common, idx, key->key, &hk, mac, + vif->type == NL80211_IFTYPE_AP); + else + ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac); + + if (!ret) + return -EIO; + + set_bit(idx, common->keymap); + if (key->alg == ALG_TKIP) { + set_bit(idx + 64, common->keymap); + if (common->splitmic) { + set_bit(idx + 32, common->keymap); + set_bit(idx + 64 + 32, common->keymap); + } + } + + return idx; +} +EXPORT_SYMBOL(ath9k_cmn_key_config); + +/* + * Delete Key. + */ +void ath9k_cmn_key_delete(struct ath_common *common, + struct ieee80211_key_conf *key) +{ + struct ath_hw *ah = common->ah; + + ath9k_hw_keyreset(ah, key->hw_key_idx); + if (key->hw_key_idx < IEEE80211_WEP_NKID) + return; + + clear_bit(key->hw_key_idx, common->keymap); + if (key->alg != ALG_TKIP) + return; + + clear_bit(key->hw_key_idx + 64, common->keymap); + if (common->splitmic) { + ath9k_hw_keyreset(ah, key->hw_key_idx + 32); + clear_bit(key->hw_key_idx + 32, common->keymap); + clear_bit(key->hw_key_idx + 64 + 32, common->keymap); + } +} +EXPORT_SYMBOL(ath9k_cmn_key_delete); + static int __init ath9k_cmn_init(void) { return 0; diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index 042999c2fe9c..bbcc57f6eba3 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -23,6 +23,8 @@ /* Common header for Atheros 802.11n base driver cores */ +#define IEEE80211_WEP_NKID 4 + #define WME_NUM_TID 16 #define WME_BA_BMP_SIZE 64 #define WME_MAX_BA WME_BA_BMP_SIZE @@ -125,3 +127,18 @@ void ath9k_cmn_rx_skb_postprocess(struct ath_common *common, bool decrypt_error); int ath9k_cmn_padpos(__le16 frame_control); +int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb); +u32 ath9k_cmn_calcrxfilter(struct ieee80211_hw *hw, struct ath_hw *ah, + unsigned int rxfilter); +void ath9k_cmn_opmode_init(struct ieee80211_hw *hw, struct ath_hw *ah, + unsigned int rxfilter); +void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw, + struct ath9k_channel *ichan); +struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, + struct ath_hw *ah); +int ath9k_cmn_key_config(struct ath_common *common, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +void ath9k_cmn_key_delete(struct ath_common *common, + struct ieee80211_key_conf *key); diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c new file mode 100644 index 000000000000..fc4f6e8c9ef3 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -0,0 +1,993 @@ +/* + * Copyright (c) 2010 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 "htc.h" + +#define ATH9K_FW_USB_DEV(devid, fw) \ + { USB_DEVICE(0x0cf3, devid), .driver_info = (unsigned long) fw } + +static struct usb_device_id ath9k_hif_usb_ids[] = { + ATH9K_FW_USB_DEV(0x9271, "ar9271.fw"), + { }, +}; + +MODULE_DEVICE_TABLE(usb, ath9k_hif_usb_ids); + +static int __hif_usb_tx(struct hif_device_usb *hif_dev); + +static void hif_usb_regout_cb(struct urb *urb) +{ + struct cmd_buf *cmd = (struct cmd_buf *)urb->context; + struct hif_device_usb *hif_dev = cmd->hif_dev; + + if (!hif_dev) { + usb_free_urb(urb); + if (cmd) { + if (cmd->skb) + dev_kfree_skb_any(cmd->skb); + kfree(cmd); + } + return; + } + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + break; + case -ENODEV: + case -ESHUTDOWN: + return; + default: + break; + } + + if (cmd) { + ath9k_htc_txcompletion_cb(cmd->hif_dev->htc_handle, + cmd->skb, 1); + kfree(cmd); + usb_free_urb(urb); + } +} + +static int hif_usb_send_regout(struct hif_device_usb *hif_dev, + struct sk_buff *skb) +{ + struct urb *urb; + struct cmd_buf *cmd; + int ret = 0; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) + return -ENOMEM; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) { + usb_free_urb(urb); + return -ENOMEM; + } + + cmd->skb = skb; + cmd->hif_dev = hif_dev; + + usb_fill_int_urb(urb, hif_dev->udev, + usb_sndintpipe(hif_dev->udev, USB_REG_OUT_PIPE), + skb->data, skb->len, + hif_usb_regout_cb, cmd, 1); + + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + usb_free_urb(urb); + kfree(cmd); + } + + return ret; +} + +static void hif_usb_tx_cb(struct urb *urb) +{ + struct tx_buf *tx_buf = (struct tx_buf *) urb->context; + struct hif_device_usb *hif_dev = tx_buf->hif_dev; + struct sk_buff *skb; + bool drop, flush; + + if (!hif_dev) + return; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + break; + case -ENODEV: + case -ESHUTDOWN: + return; + default: + break; + } + + if (tx_buf) { + spin_lock(&hif_dev->tx.tx_lock); + drop = !!(hif_dev->tx.flags & HIF_USB_TX_STOP); + flush = !!(hif_dev->tx.flags & HIF_USB_TX_FLUSH); + spin_unlock(&hif_dev->tx.tx_lock); + + while ((skb = __skb_dequeue(&tx_buf->skb_queue)) != NULL) { + if (!drop && !flush) { + ath9k_htc_txcompletion_cb(hif_dev->htc_handle, + skb, 1); + TX_STAT_INC(skb_completed); + } else { + dev_kfree_skb_any(skb); + } + } + + if (flush) + return; + + tx_buf->len = tx_buf->offset = 0; + __skb_queue_head_init(&tx_buf->skb_queue); + + spin_lock(&hif_dev->tx.tx_lock); + list_del(&tx_buf->list); + list_add_tail(&tx_buf->list, &hif_dev->tx.tx_buf); + hif_dev->tx.tx_buf_cnt++; + if (!drop) + __hif_usb_tx(hif_dev); /* Check for pending SKBs */ + TX_STAT_INC(buf_completed); + spin_unlock(&hif_dev->tx.tx_lock); + } +} + +/* TX lock has to be taken */ +static int __hif_usb_tx(struct hif_device_usb *hif_dev) +{ + struct tx_buf *tx_buf = NULL; + struct sk_buff *nskb = NULL; + int ret = 0, i; + u16 *hdr, tx_skb_cnt = 0; + u8 *buf; + + if (hif_dev->tx.tx_skb_cnt == 0) + return 0; + + /* Check if a free TX buffer is available */ + if (list_empty(&hif_dev->tx.tx_buf)) + return 0; + + tx_buf = list_first_entry(&hif_dev->tx.tx_buf, struct tx_buf, list); + list_del(&tx_buf->list); + list_add_tail(&tx_buf->list, &hif_dev->tx.tx_pending); + hif_dev->tx.tx_buf_cnt--; + + tx_skb_cnt = min_t(u16, hif_dev->tx.tx_skb_cnt, MAX_TX_AGGR_NUM); + + for (i = 0; i < tx_skb_cnt; i++) { + nskb = __skb_dequeue(&hif_dev->tx.tx_skb_queue); + + /* Should never be NULL */ + BUG_ON(!nskb); + + hif_dev->tx.tx_skb_cnt--; + + buf = tx_buf->buf; + buf += tx_buf->offset; + hdr = (u16 *)buf; + *hdr++ = nskb->len; + *hdr++ = ATH_USB_TX_STREAM_MODE_TAG; + buf += 4; + memcpy(buf, nskb->data, nskb->len); + tx_buf->len = nskb->len + 4; + + if (i < (tx_skb_cnt - 1)) + tx_buf->offset += (((tx_buf->len - 1) / 4) + 1) * 4; + + if (i == (tx_skb_cnt - 1)) + tx_buf->len += tx_buf->offset; + + __skb_queue_tail(&tx_buf->skb_queue, nskb); + TX_STAT_INC(skb_queued); + } + + usb_fill_bulk_urb(tx_buf->urb, hif_dev->udev, + usb_sndbulkpipe(hif_dev->udev, USB_WLAN_TX_PIPE), + tx_buf->buf, tx_buf->len, + hif_usb_tx_cb, tx_buf); + + ret = usb_submit_urb(tx_buf->urb, GFP_ATOMIC); + if (ret) { + tx_buf->len = tx_buf->offset = 0; + __skb_queue_purge(&tx_buf->skb_queue); + __skb_queue_head_init(&tx_buf->skb_queue); + list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf); + hif_dev->tx.tx_buf_cnt++; + } + + if (!ret) + TX_STAT_INC(buf_queued); + + return ret; +} + +static int hif_usb_send_tx(struct hif_device_usb *hif_dev, struct sk_buff *skb, + struct ath9k_htc_tx_ctl *tx_ctl) +{ + unsigned long flags; + + spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + + if (hif_dev->tx.flags & HIF_USB_TX_STOP) { + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); + return -ENODEV; + } + + /* Check if the max queue count has been reached */ + if (hif_dev->tx.tx_skb_cnt > MAX_TX_BUF_NUM) { + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); + return -ENOMEM; + } + + __skb_queue_tail(&hif_dev->tx.tx_skb_queue, skb); + hif_dev->tx.tx_skb_cnt++; + + /* Send normal frames immediately */ + if (!tx_ctl || (tx_ctl && (tx_ctl->type == ATH9K_HTC_NORMAL))) + __hif_usb_tx(hif_dev); + + /* Check if AMPDUs have to be sent immediately */ + if (tx_ctl && (tx_ctl->type == ATH9K_HTC_AMPDU) && + (hif_dev->tx.tx_buf_cnt == MAX_TX_URB_NUM) && + (hif_dev->tx.tx_skb_cnt < 2)) { + __hif_usb_tx(hif_dev); + } + + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); + + return 0; +} + +static void hif_usb_start(void *hif_handle, u8 pipe_id) +{ + struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; + unsigned long flags; + + hif_dev->flags |= HIF_USB_START; + + spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + hif_dev->tx.flags &= ~HIF_USB_TX_STOP; + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); +} + +static void hif_usb_stop(void *hif_handle, u8 pipe_id) +{ + struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; + unsigned long flags; + + spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + __skb_queue_purge(&hif_dev->tx.tx_skb_queue); + hif_dev->tx.tx_skb_cnt = 0; + hif_dev->tx.flags |= HIF_USB_TX_STOP; + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); +} + +static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb, + struct ath9k_htc_tx_ctl *tx_ctl) +{ + struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle; + int ret = 0; + + switch (pipe_id) { + case USB_WLAN_TX_PIPE: + ret = hif_usb_send_tx(hif_dev, skb, tx_ctl); + break; + case USB_REG_OUT_PIPE: + ret = hif_usb_send_regout(hif_dev, skb); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static struct ath9k_htc_hif hif_usb = { + .transport = ATH9K_HIF_USB, + .name = "ath9k_hif_usb", + + .control_ul_pipe = USB_REG_OUT_PIPE, + .control_dl_pipe = USB_REG_IN_PIPE, + + .start = hif_usb_start, + .stop = hif_usb_stop, + .send = hif_usb_send, +}; + +static void ath9k_hif_usb_rx_stream(struct hif_device_usb *hif_dev, + struct sk_buff *skb) +{ + struct sk_buff *nskb, *skb_pool[8]; + int index = 0, i = 0, chk_idx, len = skb->len; + int rx_remain_len = 0, rx_pkt_len = 0; + u16 pkt_len, pkt_tag, pool_index = 0; + u8 *ptr; + + rx_remain_len = hif_dev->rx_remain_len; + rx_pkt_len = hif_dev->rx_transfer_len; + + if (rx_remain_len != 0) { + struct sk_buff *remain_skb = hif_dev->remain_skb; + + if (remain_skb) { + ptr = (u8 *) remain_skb->data; + + index = rx_remain_len; + rx_remain_len -= hif_dev->rx_pad_len; + ptr += rx_pkt_len; + + memcpy(ptr, skb->data, rx_remain_len); + + rx_pkt_len += rx_remain_len; + hif_dev->rx_remain_len = 0; + skb_put(remain_skb, rx_pkt_len); + + skb_pool[pool_index++] = remain_skb; + + } else { + index = rx_remain_len; + } + } + + while (index < len) { + ptr = (u8 *) skb->data; + + pkt_len = ptr[index] + (ptr[index+1] << 8); + pkt_tag = ptr[index+2] + (ptr[index+3] << 8); + + if (pkt_tag == ATH_USB_RX_STREAM_MODE_TAG) { + u16 pad_len; + + pad_len = 4 - (pkt_len & 0x3); + if (pad_len == 4) + pad_len = 0; + + chk_idx = index; + index = index + 4 + pkt_len + pad_len; + + if (index > MAX_RX_BUF_SIZE) { + hif_dev->rx_remain_len = index - MAX_RX_BUF_SIZE; + hif_dev->rx_transfer_len = + MAX_RX_BUF_SIZE - chk_idx - 4; + hif_dev->rx_pad_len = pad_len; + + nskb = __dev_alloc_skb(pkt_len + 32, + GFP_ATOMIC); + if (!nskb) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: RX memory allocation" + " error\n"); + goto err; + } + skb_reserve(nskb, 32); + RX_STAT_INC(skb_allocated); + + memcpy(nskb->data, &(skb->data[chk_idx+4]), + hif_dev->rx_transfer_len); + + /* Record the buffer pointer */ + hif_dev->remain_skb = nskb; + } else { + nskb = __dev_alloc_skb(pkt_len + 32, GFP_ATOMIC); + if (!nskb) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: RX memory allocation" + " error\n"); + goto err; + } + skb_reserve(nskb, 32); + RX_STAT_INC(skb_allocated); + + memcpy(nskb->data, &(skb->data[chk_idx+4]), pkt_len); + skb_put(nskb, pkt_len); + skb_pool[pool_index++] = nskb; + } + } else { + RX_STAT_INC(skb_dropped); + dev_kfree_skb_any(skb); + return; + } + } + +err: + dev_kfree_skb_any(skb); + + for (i = 0; i < pool_index; i++) { + ath9k_htc_rx_msg(hif_dev->htc_handle, skb_pool[i], + skb_pool[i]->len, USB_WLAN_RX_PIPE); + RX_STAT_INC(skb_completed); + } +} + +static void ath9k_hif_usb_rx_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct sk_buff *nskb; + struct hif_device_usb *hif_dev = (struct hif_device_usb *) + usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); + int ret; + + if (!hif_dev) + goto free; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + goto free; + default: + goto resubmit; + } + + if (likely(urb->actual_length != 0)) { + skb_put(skb, urb->actual_length); + + nskb = __dev_alloc_skb(MAX_RX_BUF_SIZE, GFP_ATOMIC); + if (!nskb) + goto resubmit; + + usb_fill_bulk_urb(urb, hif_dev->udev, + usb_rcvbulkpipe(hif_dev->udev, + USB_WLAN_RX_PIPE), + nskb->data, MAX_RX_BUF_SIZE, + ath9k_hif_usb_rx_cb, nskb); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + dev_kfree_skb_any(nskb); + goto free; + } + + ath9k_hif_usb_rx_stream(hif_dev, skb); + return; + } + +resubmit: + skb_reset_tail_pointer(skb); + skb_trim(skb, 0); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + goto free; + + return; +free: + dev_kfree_skb_any(skb); +} + +static void ath9k_hif_usb_reg_in_cb(struct urb *urb) +{ + struct sk_buff *skb = (struct sk_buff *) urb->context; + struct sk_buff *nskb; + struct hif_device_usb *hif_dev = (struct hif_device_usb *) + usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); + int ret; + + if (!hif_dev) + goto free; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + goto free; + default: + goto resubmit; + } + + if (likely(urb->actual_length != 0)) { + skb_put(skb, urb->actual_length); + + nskb = __dev_alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_ATOMIC); + if (!nskb) + goto resubmit; + + usb_fill_int_urb(urb, hif_dev->udev, + usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE), + nskb->data, MAX_REG_IN_BUF_SIZE, + ath9k_hif_usb_reg_in_cb, nskb, 1); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) { + dev_kfree_skb_any(nskb); + goto free; + } + + ath9k_htc_rx_msg(hif_dev->htc_handle, skb, + skb->len, USB_REG_IN_PIPE); + + return; + } + +resubmit: + skb_reset_tail_pointer(skb); + skb_trim(skb, 0); + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + goto free; + + return; +free: + dev_kfree_skb_any(skb); +} + +static void ath9k_hif_usb_dealloc_tx_urbs(struct hif_device_usb *hif_dev) +{ + unsigned long flags; + struct tx_buf *tx_buf = NULL, *tx_buf_tmp = NULL; + + list_for_each_entry_safe(tx_buf, tx_buf_tmp, &hif_dev->tx.tx_buf, list) { + list_del(&tx_buf->list); + usb_free_urb(tx_buf->urb); + kfree(tx_buf->buf); + kfree(tx_buf); + } + + spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + hif_dev->tx.flags |= HIF_USB_TX_FLUSH; + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); + + list_for_each_entry_safe(tx_buf, tx_buf_tmp, + &hif_dev->tx.tx_pending, list) { + usb_kill_urb(tx_buf->urb); + list_del(&tx_buf->list); + usb_free_urb(tx_buf->urb); + kfree(tx_buf->buf); + kfree(tx_buf); + } + + spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); + hif_dev->tx.flags &= ~HIF_USB_TX_FLUSH; + spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); +} + +static int ath9k_hif_usb_alloc_tx_urbs(struct hif_device_usb *hif_dev) +{ + struct tx_buf *tx_buf; + int i; + + INIT_LIST_HEAD(&hif_dev->tx.tx_buf); + INIT_LIST_HEAD(&hif_dev->tx.tx_pending); + spin_lock_init(&hif_dev->tx.tx_lock); + __skb_queue_head_init(&hif_dev->tx.tx_skb_queue); + + for (i = 0; i < MAX_TX_URB_NUM; i++) { + tx_buf = kzalloc(sizeof(struct tx_buf), GFP_KERNEL); + if (!tx_buf) + goto err; + + tx_buf->buf = kzalloc(MAX_TX_BUF_SIZE, GFP_KERNEL); + if (!tx_buf->buf) + goto err; + + tx_buf->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!tx_buf->urb) + goto err; + + tx_buf->hif_dev = hif_dev; + __skb_queue_head_init(&tx_buf->skb_queue); + + list_add_tail(&tx_buf->list, &hif_dev->tx.tx_buf); + } + + hif_dev->tx.tx_buf_cnt = MAX_TX_URB_NUM; + + return 0; +err: + ath9k_hif_usb_dealloc_tx_urbs(hif_dev); + return -ENOMEM; +} + +static void ath9k_hif_usb_dealloc_rx_skbs(struct hif_device_usb *hif_dev) +{ + int i; + + for (i = 0; i < MAX_RX_URB_NUM; i++) { + if (hif_dev->wlan_rx_data_urb[i]) { + if (hif_dev->wlan_rx_data_urb[i]->transfer_buffer) + dev_kfree_skb_any((void *) + hif_dev->wlan_rx_data_urb[i]->context); + } + } +} + +static void ath9k_hif_usb_dealloc_rx_urbs(struct hif_device_usb *hif_dev) +{ + int i; + + for (i = 0; i < MAX_RX_URB_NUM; i++) { + if (hif_dev->wlan_rx_data_urb[i]) { + usb_kill_urb(hif_dev->wlan_rx_data_urb[i]); + usb_free_urb(hif_dev->wlan_rx_data_urb[i]); + hif_dev->wlan_rx_data_urb[i] = NULL; + } + } +} + +static int ath9k_hif_usb_prep_rx_urb(struct hif_device_usb *hif_dev, + struct urb *urb) +{ + struct sk_buff *skb; + + skb = __dev_alloc_skb(MAX_RX_BUF_SIZE, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + usb_fill_bulk_urb(urb, hif_dev->udev, + usb_rcvbulkpipe(hif_dev->udev, USB_WLAN_RX_PIPE), + skb->data, MAX_RX_BUF_SIZE, + ath9k_hif_usb_rx_cb, skb); + return 0; +} + +static int ath9k_hif_usb_alloc_rx_urbs(struct hif_device_usb *hif_dev) +{ + int i, ret; + + for (i = 0; i < MAX_RX_URB_NUM; i++) { + + /* Allocate URB */ + hif_dev->wlan_rx_data_urb[i] = usb_alloc_urb(0, GFP_KERNEL); + if (hif_dev->wlan_rx_data_urb[i] == NULL) { + ret = -ENOMEM; + goto err_rx_urb; + } + + /* Allocate buffer */ + ret = ath9k_hif_usb_prep_rx_urb(hif_dev, + hif_dev->wlan_rx_data_urb[i]); + if (ret) + goto err_rx_urb; + + /* Submit URB */ + ret = usb_submit_urb(hif_dev->wlan_rx_data_urb[i], GFP_KERNEL); + if (ret) + goto err_rx_urb; + + } + + return 0; + +err_rx_urb: + ath9k_hif_usb_dealloc_rx_skbs(hif_dev); + ath9k_hif_usb_dealloc_rx_urbs(hif_dev); + return ret; +} + +static void ath9k_hif_usb_dealloc_reg_in_urb(struct hif_device_usb *hif_dev) +{ + if (hif_dev->reg_in_urb) { + usb_kill_urb(hif_dev->reg_in_urb); + usb_free_urb(hif_dev->reg_in_urb); + hif_dev->reg_in_urb = NULL; + } +} + +static int ath9k_hif_usb_alloc_reg_in_urb(struct hif_device_usb *hif_dev) +{ + struct sk_buff *skb; + + hif_dev->reg_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (hif_dev->reg_in_urb == NULL) + return -ENOMEM; + + skb = __dev_alloc_skb(MAX_REG_IN_BUF_SIZE, GFP_KERNEL); + if (!skb) + goto err; + + usb_fill_int_urb(hif_dev->reg_in_urb, hif_dev->udev, + usb_rcvintpipe(hif_dev->udev, USB_REG_IN_PIPE), + skb->data, MAX_REG_IN_BUF_SIZE, + ath9k_hif_usb_reg_in_cb, skb, 1); + + if (usb_submit_urb(hif_dev->reg_in_urb, GFP_KERNEL) != 0) + goto err_skb; + + return 0; + +err_skb: + dev_kfree_skb_any(skb); +err: + ath9k_hif_usb_dealloc_reg_in_urb(hif_dev); + return -ENOMEM; +} + +static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev) +{ + /* TX */ + if (ath9k_hif_usb_alloc_tx_urbs(hif_dev) < 0) + goto err; + + /* RX */ + if (ath9k_hif_usb_alloc_rx_urbs(hif_dev) < 0) + goto err; + + /* Register Read/Write */ + if (ath9k_hif_usb_alloc_reg_in_urb(hif_dev) < 0) + goto err; + + return 0; +err: + return -ENOMEM; +} + +static int ath9k_hif_usb_download_fw(struct hif_device_usb *hif_dev) +{ + int transfer, err; + const void *data = hif_dev->firmware->data; + size_t len = hif_dev->firmware->size; + u32 addr = AR9271_FIRMWARE; + u8 *buf = kzalloc(4096, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + while (len) { + transfer = min_t(int, len, 4096); + memcpy(buf, data, transfer); + + err = usb_control_msg(hif_dev->udev, + usb_sndctrlpipe(hif_dev->udev, 0), + FIRMWARE_DOWNLOAD, 0x40 | USB_DIR_OUT, + addr >> 8, 0, buf, transfer, HZ); + if (err < 0) { + kfree(buf); + return err; + } + + len -= transfer; + data += transfer; + addr += transfer; + } + kfree(buf); + + /* + * Issue FW download complete command to firmware. + */ + err = usb_control_msg(hif_dev->udev, usb_sndctrlpipe(hif_dev->udev, 0), + FIRMWARE_DOWNLOAD_COMP, + 0x40 | USB_DIR_OUT, + AR9271_FIRMWARE_TEXT >> 8, 0, NULL, 0, HZ); + if (err) + return -EIO; + + dev_info(&hif_dev->udev->dev, "ath9k_htc: Transferred FW: %s, size: %ld\n", + "ar9271.fw", (unsigned long) hif_dev->firmware->size); + + return 0; +} + +static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev, + const char *fw_name) +{ + int ret; + + /* Request firmware */ + ret = request_firmware(&hif_dev->firmware, fw_name, &hif_dev->udev->dev); + if (ret) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: Firmware - %s not found\n", fw_name); + goto err_fw_req; + } + + /* Download firmware */ + ret = ath9k_hif_usb_download_fw(hif_dev); + if (ret) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: Firmware - %s download failed\n", fw_name); + goto err_fw_download; + } + + /* Alloc URBs */ + ret = ath9k_hif_usb_alloc_urbs(hif_dev); + if (ret) { + dev_err(&hif_dev->udev->dev, + "ath9k_htc: Unable to allocate URBs\n"); + goto err_urb; + } + + return 0; + +err_urb: + /* Nothing */ +err_fw_download: + release_firmware(hif_dev->firmware); +err_fw_req: + hif_dev->firmware = NULL; + return ret; +} + +static void ath9k_hif_usb_dealloc_urbs(struct hif_device_usb *hif_dev) +{ + ath9k_hif_usb_dealloc_reg_in_urb(hif_dev); + ath9k_hif_usb_dealloc_tx_urbs(hif_dev); + ath9k_hif_usb_dealloc_rx_urbs(hif_dev); +} + +static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev) +{ + ath9k_hif_usb_dealloc_urbs(hif_dev); + if (hif_dev->firmware) + release_firmware(hif_dev->firmware); +} + +static int ath9k_hif_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct hif_device_usb *hif_dev; + const char *fw_name = (const char *) id->driver_info; + int ret = 0; + + hif_dev = kzalloc(sizeof(struct hif_device_usb), GFP_KERNEL); + if (!hif_dev) { + ret = -ENOMEM; + goto err_alloc; + } + + usb_get_dev(udev); + hif_dev->udev = udev; + hif_dev->interface = interface; + hif_dev->device_id = id->idProduct; +#ifdef CONFIG_PM + udev->reset_resume = 1; +#endif + usb_set_intfdata(interface, hif_dev); + + ret = ath9k_hif_usb_dev_init(hif_dev, fw_name); + if (ret) { + ret = -EINVAL; + goto err_hif_init_usb; + } + + hif_dev->htc_handle = ath9k_htc_hw_alloc(hif_dev); + if (hif_dev->htc_handle == NULL) { + ret = -ENOMEM; + goto err_htc_hw_alloc; + } + + ret = ath9k_htc_hw_init(&hif_usb, hif_dev->htc_handle, hif_dev, + &hif_dev->udev->dev, hif_dev->device_id, + ATH9K_HIF_USB); + if (ret) { + ret = -EINVAL; + goto err_htc_hw_init; + } + + dev_info(&hif_dev->udev->dev, "ath9k_htc: USB layer initialized\n"); + + return 0; + +err_htc_hw_init: + ath9k_htc_hw_free(hif_dev->htc_handle); +err_htc_hw_alloc: + ath9k_hif_usb_dev_deinit(hif_dev); +err_hif_init_usb: + usb_set_intfdata(interface, NULL); + kfree(hif_dev); + usb_put_dev(udev); +err_alloc: + return ret; +} + +static void ath9k_hif_usb_disconnect(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct hif_device_usb *hif_dev = + (struct hif_device_usb *) usb_get_intfdata(interface); + + if (hif_dev) { + ath9k_htc_hw_deinit(hif_dev->htc_handle, true); + ath9k_htc_hw_free(hif_dev->htc_handle); + ath9k_hif_usb_dev_deinit(hif_dev); + usb_set_intfdata(interface, NULL); + } + + if (hif_dev->flags & HIF_USB_START) + usb_reset_device(udev); + + kfree(hif_dev); + dev_info(&udev->dev, "ath9k_htc: USB layer deinitialized\n"); + usb_put_dev(udev); +} + +#ifdef CONFIG_PM +static int ath9k_hif_usb_suspend(struct usb_interface *interface, + pm_message_t message) +{ + struct hif_device_usb *hif_dev = + (struct hif_device_usb *) usb_get_intfdata(interface); + + ath9k_hif_usb_dealloc_urbs(hif_dev); + + return 0; +} + +static int ath9k_hif_usb_resume(struct usb_interface *interface) +{ + struct hif_device_usb *hif_dev = + (struct hif_device_usb *) usb_get_intfdata(interface); + int ret; + + ret = ath9k_hif_usb_alloc_urbs(hif_dev); + if (ret) + return ret; + + if (hif_dev->firmware) { + ret = ath9k_hif_usb_download_fw(hif_dev); + if (ret) + goto fail_resume; + } else { + ath9k_hif_usb_dealloc_urbs(hif_dev); + return -EIO; + } + + mdelay(100); + + ret = ath9k_htc_resume(hif_dev->htc_handle); + + if (ret) + goto fail_resume; + + return 0; + +fail_resume: + ath9k_hif_usb_dealloc_urbs(hif_dev); + + return ret; +} +#endif + +static struct usb_driver ath9k_hif_usb_driver = { + .name = "ath9k_hif_usb", + .probe = ath9k_hif_usb_probe, + .disconnect = ath9k_hif_usb_disconnect, +#ifdef CONFIG_PM + .suspend = ath9k_hif_usb_suspend, + .resume = ath9k_hif_usb_resume, + .reset_resume = ath9k_hif_usb_resume, +#endif + .id_table = ath9k_hif_usb_ids, + .soft_unbind = 1, +}; + +int ath9k_hif_usb_init(void) +{ + return usb_register(&ath9k_hif_usb_driver); +} + +void ath9k_hif_usb_exit(void) +{ + usb_deregister(&ath9k_hif_usb_driver); +} diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.h b/drivers/net/wireless/ath/ath9k/hif_usb.h new file mode 100644 index 000000000000..7cc3762a6789 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/hif_usb.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2010 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_USB_H +#define HTC_USB_H + +#define AR9271_FIRMWARE 0x501000 +#define AR9271_FIRMWARE_TEXT 0x903000 + +#define FIRMWARE_DOWNLOAD 0x30 +#define FIRMWARE_DOWNLOAD_COMP 0x31 + +#define ATH_USB_RX_STREAM_MODE_TAG 0x4e00 +#define ATH_USB_TX_STREAM_MODE_TAG 0x697e + +/* FIXME: Verify these numbers (with Windows) */ +#define MAX_TX_URB_NUM 8 +#define MAX_TX_BUF_NUM 1024 +#define MAX_TX_BUF_SIZE 32768 +#define MAX_TX_AGGR_NUM 20 + +#define MAX_RX_URB_NUM 8 +#define MAX_RX_BUF_SIZE 16384 + +#define MAX_REG_OUT_URB_NUM 1 +#define MAX_REG_OUT_BUF_NUM 8 + +#define MAX_REG_IN_BUF_SIZE 64 + +/* USB Endpoint definition */ +#define USB_WLAN_TX_PIPE 1 +#define USB_WLAN_RX_PIPE 2 +#define USB_REG_IN_PIPE 3 +#define USB_REG_OUT_PIPE 4 + +#define HIF_USB_MAX_RXPIPES 2 +#define HIF_USB_MAX_TXPIPES 4 + +struct tx_buf { + u8 *buf; + u16 len; + u16 offset; + struct urb *urb; + struct sk_buff_head skb_queue; + struct hif_device_usb *hif_dev; + struct list_head list; +}; + +#define HIF_USB_TX_STOP BIT(0) +#define HIF_USB_TX_FLUSH BIT(1) + +struct hif_usb_tx { + u8 flags; + u8 tx_buf_cnt; + u16 tx_skb_cnt; + struct sk_buff_head tx_skb_queue; + struct list_head tx_buf; + struct list_head tx_pending; + spinlock_t tx_lock; +}; + +struct cmd_buf { + struct sk_buff *skb; + struct hif_device_usb *hif_dev; +}; + +#define HIF_USB_START BIT(0) + +struct hif_device_usb { + u16 device_id; + struct usb_device *udev; + struct usb_interface *interface; + const struct firmware *firmware; + struct htc_target *htc_handle; + u8 flags; + + struct hif_usb_tx tx; + + struct urb *wlan_rx_data_urb[MAX_RX_URB_NUM]; + struct urb *reg_in_urb; + + struct sk_buff *remain_skb; + int rx_remain_len; + int rx_pkt_len; + int rx_transfer_len; + int rx_pad_len; +}; + +int ath9k_hif_usb_init(void); +void ath9k_hif_usb_exit(void); + +#endif /* HTC_USB_H */ diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h new file mode 100644 index 000000000000..777064945fca --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2010 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_H +#define HTC_H + +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/firmware.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/leds.h> +#include <net/mac80211.h> + +#include "common.h" +#include "htc_hst.h" +#include "hif_usb.h" +#include "wmi.h" + +#define ATH_STA_SHORT_CALINTERVAL 1000 /* 1 second */ +#define ATH_ANI_POLLINTERVAL 100 /* 100 ms */ +#define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ +#define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ + +#define ATH_DEFAULT_BMISS_LIMIT 10 +#define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) +#define TSF_TO_TU(_h, _l) \ + ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) + +extern struct ieee80211_ops ath9k_htc_ops; +extern int htc_modparam_nohwcrypt; + +enum htc_phymode { + HTC_MODE_AUTO = 0, + HTC_MODE_11A = 1, + HTC_MODE_11B = 2, + HTC_MODE_11G = 3, + HTC_MODE_FH = 4, + HTC_MODE_TURBO_A = 5, + HTC_MODE_TURBO_G = 6, + HTC_MODE_11NA = 7, + HTC_MODE_11NG = 8 +}; + +enum htc_opmode { + HTC_M_STA = 1, + HTC_M_IBSS = 0, + HTC_M_AHDEMO = 3, + HTC_M_HOSTAP = 6, + HTC_M_MONITOR = 8, + HTC_M_WDS = 2 +}; + +#define ATH9K_HTC_HDRSPACE sizeof(struct htc_frame_hdr) +#define ATH9K_HTC_AMPDU 1 +#define ATH9K_HTC_NORMAL 2 + +#define ATH9K_HTC_TX_CTSONLY 0x1 +#define ATH9K_HTC_TX_RTSCTS 0x2 +#define ATH9K_HTC_TX_USE_MIN_RATE 0x100 + +struct tx_frame_hdr { + u8 data_type; + u8 node_idx; + u8 vif_idx; + u8 tidno; + u32 flags; /* ATH9K_HTC_TX_* */ + u8 key_type; + u8 keyix; + u8 reserved[26]; +} __packed; + +struct tx_mgmt_hdr { + u8 node_idx; + u8 vif_idx; + u8 tidno; + u8 flags; + u8 key_type; + u8 keyix; + u16 reserved; +} __packed; + +struct tx_beacon_header { + u8 len_changed; + u8 vif_index; + u16 rev; +} __packed; + +struct ath9k_htc_target_hw { + u32 flags; + u32 flags_ext; + u32 ampdu_limit; + u8 ampdu_subframes; + u8 tx_chainmask; + u8 tx_chainmask_legacy; + u8 rtscts_ratecode; + u8 protmode; +} __packed; + +struct ath9k_htc_cap_target { + u32 flags; + u32 flags_ext; + u32 ampdu_limit; + u8 ampdu_subframes; + u8 tx_chainmask; + u8 tx_chainmask_legacy; + u8 rtscts_ratecode; + u8 protmode; +} __packed; + +struct ath9k_htc_target_vif { + u8 index; + u8 des_bssid[ETH_ALEN]; + enum htc_opmode opmode; + u8 myaddr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u32 flags; + u32 flags_ext; + u16 ps_sta; + u16 rtsthreshold; + u8 ath_cap; + u8 node; + s8 mcast_rate; +} __packed; + +#define ATH_HTC_STA_AUTH 0x0001 +#define ATH_HTC_STA_QOS 0x0002 +#define ATH_HTC_STA_ERP 0x0004 +#define ATH_HTC_STA_HT 0x0008 + +/* FIXME: UAPSD variables */ +struct ath9k_htc_target_sta { + u16 associd; + u16 txpower; + u32 ucastkey; + u8 macaddr[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + u8 sta_index; + u8 vif_index; + u8 vif_sta; + u16 flags; /* ATH_HTC_STA_* */ + u16 htcap; + u8 valid; + u16 capinfo; + struct ath9k_htc_target_hw *hw; + struct ath9k_htc_target_vif *vif; + u16 txseqmgmt; + u8 is_vif_sta; + u16 maxampdu; + u16 iv16; + u32 iv32; +} __packed; + +struct ath9k_htc_target_aggr { + u8 sta_index; + u8 tidno; + u8 aggr_enable; + u8 padding; +} __packed; + +#define ATH_HTC_RATE_MAX 30 + +#define WLAN_RC_DS_FLAG 0x01 +#define WLAN_RC_40_FLAG 0x02 +#define WLAN_RC_SGI_FLAG 0x04 +#define WLAN_RC_HT_FLAG 0x08 + +struct ath9k_htc_rateset { + u8 rs_nrates; + u8 rs_rates[ATH_HTC_RATE_MAX]; +}; + +struct ath9k_htc_rate { + struct ath9k_htc_rateset legacy_rates; + struct ath9k_htc_rateset ht_rates; +} __packed; + +struct ath9k_htc_target_rate { + u8 sta_index; + u8 isnew; + u32 capflags; + struct ath9k_htc_rate rates; +}; + +struct ath9k_htc_target_stats { + u32 tx_shortretry; + u32 tx_longretry; + u32 tx_xretries; + u32 ht_txunaggr_xretry; + u32 ht_tx_xretries; +} __packed; + +struct ath9k_htc_vif { + u8 index; +}; + +#define ATH9K_HTC_MAX_STA 8 +#define ATH9K_HTC_MAX_TID 8 + +enum tid_aggr_state { + AGGR_STOP = 0, + AGGR_PROGRESS, + AGGR_START, + AGGR_OPERATIONAL +}; + +struct ath9k_htc_sta { + u8 index; + enum tid_aggr_state tid_state[ATH9K_HTC_MAX_TID]; +}; + +struct ath9k_htc_aggr_work { + u16 tid; + u8 sta_addr[ETH_ALEN]; + struct ieee80211_hw *hw; + struct ieee80211_vif *vif; + enum ieee80211_ampdu_mlme_action action; + struct mutex mutex; +}; + +#define ATH9K_HTC_RXBUF 256 +#define HTC_RX_FRAME_HEADER_SIZE 40 + +struct ath9k_htc_rxbuf { + bool in_process; + struct sk_buff *skb; + struct ath_htc_rx_status rxstatus; + struct list_head list; +}; + +struct ath9k_htc_rx { + int last_rssi; /* FIXME: per-STA */ + struct list_head rxbuf; + spinlock_t rxbuflock; +}; + +struct ath9k_htc_tx_ctl { + u8 type; /* ATH9K_HTC_* */ +}; + +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + +#define TX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.tx_stats.c++) +#define RX_STAT_INC(c) (hif_dev->htc_handle->drv_priv->debug.rx_stats.c++) + +struct ath_tx_stats { + u32 buf_queued; + u32 buf_completed; + u32 skb_queued; + u32 skb_completed; +}; + +struct ath_rx_stats { + u32 skb_allocated; + u32 skb_completed; + u32 skb_dropped; +}; + +struct ath9k_debug { + struct dentry *debugfs_phy; + struct dentry *debugfs_tgt_stats; + struct dentry *debugfs_xmit; + struct dentry *debugfs_recv; + struct ath_tx_stats tx_stats; + struct ath_rx_stats rx_stats; + u32 txrate; +}; + +#else + +#define TX_STAT_INC(c) do { } while (0) +#define RX_STAT_INC(c) do { } while (0) + +#endif /* CONFIG_ATH9K_HTC_DEBUGFS */ + +#define ATH_LED_PIN_DEF 1 +#define ATH_LED_PIN_9287 8 +#define ATH_LED_PIN_9271 15 +#define ATH_LED_ON_DURATION_IDLE 350 /* in msecs */ +#define ATH_LED_OFF_DURATION_IDLE 250 /* in msecs */ + +enum ath_led_type { + ATH_LED_RADIO, + ATH_LED_ASSOC, + ATH_LED_TX, + ATH_LED_RX +}; + +struct ath_led { + struct ath9k_htc_priv *priv; + struct led_classdev led_cdev; + enum ath_led_type led_type; + struct delayed_work brightness_work; + char name[32]; + bool registered; + int brightness; +}; + +#define OP_INVALID BIT(0) +#define OP_SCANNING BIT(1) +#define OP_FULL_RESET BIT(2) +#define OP_LED_ASSOCIATED BIT(3) +#define OP_LED_ON BIT(4) +#define OP_PREAMBLE_SHORT BIT(5) +#define OP_PROTECT_ENABLE BIT(6) +#define OP_TXAGGR BIT(7) +#define OP_ASSOCIATED BIT(8) +#define OP_ENABLE_BEACON BIT(9) +#define OP_LED_DEINIT BIT(10) + +struct ath9k_htc_priv { + struct device *dev; + struct ieee80211_hw *hw; + struct ath_hw *ah; + struct htc_target *htc; + struct wmi *wmi; + + enum htc_endpoint_id wmi_cmd_ep; + enum htc_endpoint_id beacon_ep; + enum htc_endpoint_id cab_ep; + enum htc_endpoint_id uapsd_ep; + enum htc_endpoint_id mgmt_ep; + enum htc_endpoint_id data_be_ep; + enum htc_endpoint_id data_bk_ep; + enum htc_endpoint_id data_vi_ep; + enum htc_endpoint_id data_vo_ep; + + u16 op_flags; + u16 curtxpow; + u16 txpowlimit; + u16 nvifs; + u16 nstations; + u16 seq_no; + u32 bmiss_cnt; + + struct sk_buff *beacon; + spinlock_t beacon_lock; + + struct ieee80211_vif *vif; + unsigned int rxfilter; + struct tasklet_struct wmi_tasklet; + struct tasklet_struct rx_tasklet; + struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; + struct ath9k_htc_rx rx; + struct tasklet_struct tx_tasklet; + struct sk_buff_head tx_queue; + struct ath9k_htc_aggr_work aggr_work; + struct delayed_work ath9k_aggr_work; + struct delayed_work ath9k_ani_work; + + struct ath_led radio_led; + struct ath_led assoc_led; + struct ath_led tx_led; + struct ath_led rx_led; + struct delayed_work ath9k_led_blink_work; + int led_on_duration; + int led_off_duration; + int led_on_cnt; + int led_off_cnt; + int hwq_map[ATH9K_WME_AC_VO+1]; + +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + struct ath9k_debug debug; +#endif + struct ath9k_htc_target_rate tgt_rate; + + struct mutex mutex; +}; + +static inline void ath_read_cachesize(struct ath_common *common, int *csz) +{ + common->bus_ops->read_cachesize(common, csz); +} + +void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf); +void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending); +void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif); + +void ath9k_htc_rxep(void *priv, struct sk_buff *skb, + enum htc_endpoint_id ep_id); +void ath9k_htc_txep(void *priv, struct sk_buff *skb, enum htc_endpoint_id ep_id, + bool txok); + +void ath9k_htc_station_work(struct work_struct *work); +void ath9k_htc_aggr_work(struct work_struct *work); +void ath9k_ani_work(struct work_struct *work);; + +int ath9k_tx_init(struct ath9k_htc_priv *priv); +void ath9k_tx_tasklet(unsigned long data); +int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb); +void ath9k_tx_cleanup(struct ath9k_htc_priv *priv); +bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv, + enum ath9k_tx_queue_subtype qtype); +int get_hw_qnum(u16 queue, int *hwq_map); +int ath_htc_txq_update(struct ath9k_htc_priv *priv, int qnum, + struct ath9k_tx_queue_info *qinfo); + +int ath9k_rx_init(struct ath9k_htc_priv *priv); +void ath9k_rx_cleanup(struct ath9k_htc_priv *priv); +void ath9k_host_rx_init(struct ath9k_htc_priv *priv); +void ath9k_rx_tasklet(unsigned long data); + +void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv); +void ath9k_init_leds(struct ath9k_htc_priv *priv); +void ath9k_deinit_leds(struct ath9k_htc_priv *priv); + +int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, + u16 devid); +void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug); +#ifdef CONFIG_PM +int ath9k_htc_resume(struct htc_target *htc_handle); +#endif +#ifdef CONFIG_ATH9K_HTC_DEBUGFS +int ath9k_htc_debug_create_root(void); +void ath9k_htc_debug_remove_root(void); +int ath9k_htc_init_debug(struct ath_hw *ah); +void ath9k_htc_exit_debug(struct ath_hw *ah); +#else +static inline int ath9k_htc_debug_create_root(void) { return 0; }; +static inline void ath9k_htc_debug_remove_root(void) {}; +static inline int ath9k_htc_init_debug(struct ath_hw *ah) { return 0; }; +static inline void ath9k_htc_exit_debug(struct ath_hw *ah) {}; +#endif /* CONFIG_ATH9K_HTC_DEBUGFS */ + +#endif /* HTC_H */ diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c new file mode 100644 index 000000000000..25f5b5377bac --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2010 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 "htc.h" + +#define FUDGE 2 + +static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_beacon_state bs; + enum ath9k_int imask = 0; + int dtimperiod, dtimcount, sleepduration; + int cfpperiod, cfpcount, bmiss_timeout; + u32 nexttbtt = 0, intval, tsftu, htc_imask = 0; + u64 tsf; + int num_beacons, offset, dtim_dec_count, cfp_dec_count; + int ret; + u8 cmd_rsp; + + memset(&bs, 0, sizeof(bs)); + + intval = bss_conf->beacon_int & ATH9K_BEACON_PERIOD; + bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_int); + + /* + * Setup dtim and cfp parameters according to + * last beacon we received (which may be none). + */ + dtimperiod = bss_conf->dtim_period; + if (dtimperiod <= 0) /* NB: 0 if not known */ + dtimperiod = 1; + dtimcount = 1; + if (dtimcount >= dtimperiod) /* NB: sanity check */ + dtimcount = 0; + cfpperiod = 1; /* NB: no PCF support yet */ + cfpcount = 0; + + sleepduration = intval; + if (sleepduration <= 0) + sleepduration = intval; + + /* + * Pull nexttbtt forward to reflect the current + * TSF and calculate dtim+cfp state for the result. + */ + tsf = ath9k_hw_gettsf64(priv->ah); + tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; + + num_beacons = tsftu / intval + 1; + offset = tsftu % intval; + nexttbtt = tsftu - offset; + if (offset) + nexttbtt += intval; + + /* DTIM Beacon every dtimperiod Beacon */ + dtim_dec_count = num_beacons % dtimperiod; + /* CFP every cfpperiod DTIM Beacon */ + cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; + if (dtim_dec_count) + cfp_dec_count++; + + dtimcount -= dtim_dec_count; + if (dtimcount < 0) + dtimcount += dtimperiod; + + cfpcount -= cfp_dec_count; + if (cfpcount < 0) + cfpcount += cfpperiod; + + bs.bs_intval = intval; + bs.bs_nexttbtt = nexttbtt; + bs.bs_dtimperiod = dtimperiod*intval; + bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; + bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; + bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; + bs.bs_cfpmaxduration = 0; + + /* + * Calculate the number of consecutive beacons to miss* before taking + * a BMISS interrupt. The configuration is specified in TU so we only + * need calculate based on the beacon interval. Note that we clamp the + * result to at most 15 beacons. + */ + if (sleepduration > intval) { + bs.bs_bmissthreshold = ATH_DEFAULT_BMISS_LIMIT / 2; + } else { + bs.bs_bmissthreshold = DIV_ROUND_UP(bmiss_timeout, intval); + if (bs.bs_bmissthreshold > 15) + bs.bs_bmissthreshold = 15; + else if (bs.bs_bmissthreshold <= 0) + bs.bs_bmissthreshold = 1; + } + + /* + * Calculate sleep duration. The configuration is given in ms. + * We ensure a multiple of the beacon period is used. Also, if the sleep + * duration is greater than the DTIM period then it makes senses + * to make it a multiple of that. + * + * XXX fixed at 100ms + */ + + bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); + if (bs.bs_sleepduration > bs.bs_dtimperiod) + bs.bs_sleepduration = bs.bs_dtimperiod; + + /* TSF out of range threshold fixed at 1 second */ + bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; + + ath_print(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); + ath_print(common, ATH_DBG_BEACON, + "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", + bs.bs_bmissthreshold, bs.bs_sleepduration, + bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); + + /* Set the computed STA beacon timers */ + + WMI_CMD(WMI_DISABLE_INTR_CMDID); + ath9k_hw_set_sta_beacon_timers(priv->ah, &bs); + imask |= ATH9K_INT_BMISS; + htc_imask = cpu_to_be32(imask); + WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); +} + +static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + enum ath9k_int imask = 0; + u32 nexttbtt, intval, htc_imask = 0; + int ret; + u8 cmd_rsp; + + intval = bss_conf->beacon_int & ATH9K_BEACON_PERIOD; + nexttbtt = intval; + intval |= ATH9K_BEACON_ENA; + if (priv->op_flags & OP_ENABLE_BEACON) + imask |= ATH9K_INT_SWBA; + + ath_print(common, ATH_DBG_BEACON, + "IBSS Beacon config, intval: %d, imask: 0x%x\n", + bss_conf->beacon_int, imask); + + WMI_CMD(WMI_DISABLE_INTR_CMDID); + ath9k_hw_beaconinit(priv->ah, nexttbtt, intval); + priv->bmiss_cnt = 0; + htc_imask = cpu_to_be32(imask); + WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); +} + +void ath9k_htc_beacon_update(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + + spin_lock_bh(&priv->beacon_lock); + + if (priv->beacon) + dev_kfree_skb_any(priv->beacon); + + priv->beacon = ieee80211_beacon_get(priv->hw, vif); + if (!priv->beacon) + ath_print(common, ATH_DBG_BEACON, + "Unable to allocate beacon\n"); + + spin_unlock_bh(&priv->beacon_lock); +} + +void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) +{ + struct ath9k_htc_vif *avp = (void *)priv->vif->drv_priv; + struct tx_beacon_header beacon_hdr; + struct ath9k_htc_tx_ctl tx_ctl; + struct ieee80211_tx_info *info; + u8 *tx_fhdr; + + memset(&beacon_hdr, 0, sizeof(struct tx_beacon_header)); + memset(&tx_ctl, 0, sizeof(struct ath9k_htc_tx_ctl)); + + /* FIXME: Handle BMISS */ + if (beacon_pending != 0) { + priv->bmiss_cnt++; + return; + } + + spin_lock_bh(&priv->beacon_lock); + + if (unlikely(priv->op_flags & OP_SCANNING)) { + spin_unlock_bh(&priv->beacon_lock); + return; + } + + if (unlikely(priv->beacon == NULL)) { + spin_unlock_bh(&priv->beacon_lock); + return; + } + + /* Free the old SKB first */ + dev_kfree_skb_any(priv->beacon); + + /* Get a new beacon */ + priv->beacon = ieee80211_beacon_get(priv->hw, priv->vif); + if (!priv->beacon) { + spin_unlock_bh(&priv->beacon_lock); + return; + } + + info = IEEE80211_SKB_CB(priv->beacon); + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) priv->beacon->data; + priv->seq_no += 0x10; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(priv->seq_no); + } + + tx_ctl.type = ATH9K_HTC_NORMAL; + beacon_hdr.vif_index = avp->index; + tx_fhdr = skb_push(priv->beacon, sizeof(beacon_hdr)); + memcpy(tx_fhdr, (u8 *) &beacon_hdr, sizeof(beacon_hdr)); + + htc_send(priv->htc, priv->beacon, priv->beacon_ep, &tx_ctl); + + spin_unlock_bh(&priv->beacon_lock); +} + +void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + ath9k_htc_beacon_config_sta(priv, bss_conf); + break; + case NL80211_IFTYPE_ADHOC: + ath9k_htc_beacon_config_adhoc(priv, bss_conf); + break; + default: + ath_print(common, ATH_DBG_CONFIG, + "Unsupported beaconing mode\n"); + return; + } +} diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c new file mode 100644 index 000000000000..10c87605d2c4 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -0,0 +1,713 @@ +/* + * Copyright (c) 2010 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 "htc.h" + +MODULE_AUTHOR("Atheros Communications"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_DESCRIPTION("Atheros driver 802.11n HTC based wireless devices"); + +static unsigned int ath9k_debug = ATH_DBG_DEFAULT; +module_param_named(debug, ath9k_debug, uint, 0); +MODULE_PARM_DESC(debug, "Debugging mask"); + +int htc_modparam_nohwcrypt; +module_param_named(nohwcrypt, htc_modparam_nohwcrypt, int, 0444); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); + +#define CHAN2G(_freq, _idx) { \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 20, \ +} + +static struct ieee80211_channel ath9k_2ghz_channels[] = { + CHAN2G(2412, 0), /* Channel 1 */ + CHAN2G(2417, 1), /* Channel 2 */ + CHAN2G(2422, 2), /* Channel 3 */ + CHAN2G(2427, 3), /* Channel 4 */ + CHAN2G(2432, 4), /* Channel 5 */ + CHAN2G(2437, 5), /* Channel 6 */ + CHAN2G(2442, 6), /* Channel 7 */ + CHAN2G(2447, 7), /* Channel 8 */ + CHAN2G(2452, 8), /* Channel 9 */ + CHAN2G(2457, 9), /* Channel 10 */ + CHAN2G(2462, 10), /* Channel 11 */ + CHAN2G(2467, 11), /* Channel 12 */ + CHAN2G(2472, 12), /* Channel 13 */ + CHAN2G(2484, 13), /* Channel 14 */ +}; + +/* Atheros hardware rate code addition for short premble */ +#define SHPCHECK(__hw_rate, __flags) \ + ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04) : 0) + +#define RATE(_bitrate, _hw_rate, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate), \ + .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ +} + +static struct ieee80211_rate ath9k_legacy_rates[] = { + RATE(10, 0x1b, 0), + RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp : 0x1e */ + RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp: 0x1d */ + RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), /* short: 0x1c */ + RATE(60, 0x0b, 0), + RATE(90, 0x0f, 0), + RATE(120, 0x0a, 0), + RATE(180, 0x0e, 0), + RATE(240, 0x09, 0), + RATE(360, 0x0d, 0), + RATE(480, 0x08, 0), + RATE(540, 0x0c, 0), +}; + +static int ath9k_htc_wait_for_target(struct ath9k_htc_priv *priv) +{ + int time_left; + + /* Firmware can take up to 50ms to get ready, to be safe use 1 second */ + time_left = wait_for_completion_timeout(&priv->htc->target_wait, HZ); + if (!time_left) { + dev_err(priv->dev, "ath9k_htc: Target is unresponsive\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static void ath9k_deinit_priv(struct ath9k_htc_priv *priv) +{ + ath9k_htc_exit_debug(priv->ah); + ath9k_hw_deinit(priv->ah); + tasklet_kill(&priv->wmi_tasklet); + tasklet_kill(&priv->rx_tasklet); + tasklet_kill(&priv->tx_tasklet); + kfree(priv->ah); + priv->ah = NULL; +} + +static void ath9k_deinit_device(struct ath9k_htc_priv *priv) +{ + struct ieee80211_hw *hw = priv->hw; + + wiphy_rfkill_stop_polling(hw->wiphy); + ath9k_deinit_leds(priv); + ieee80211_unregister_hw(hw); + ath9k_rx_cleanup(priv); + ath9k_tx_cleanup(priv); + ath9k_deinit_priv(priv); +} + +static inline int ath9k_htc_connect_svc(struct ath9k_htc_priv *priv, + u16 service_id, + void (*tx) (void *, + struct sk_buff *, + enum htc_endpoint_id, + bool txok), + enum htc_endpoint_id *ep_id) +{ + struct htc_service_connreq req; + + memset(&req, 0, sizeof(struct htc_service_connreq)); + + req.service_id = service_id; + req.ep_callbacks.priv = priv; + req.ep_callbacks.rx = ath9k_htc_rxep; + req.ep_callbacks.tx = tx; + + return htc_connect_service(priv->htc, &req, ep_id); +} + +static int ath9k_init_htc_services(struct ath9k_htc_priv *priv) +{ + int ret; + + /* WMI CMD*/ + ret = ath9k_wmi_connect(priv->htc, priv->wmi, &priv->wmi_cmd_ep); + if (ret) + goto err; + + /* Beacon */ + ret = ath9k_htc_connect_svc(priv, WMI_BEACON_SVC, NULL, + &priv->beacon_ep); + if (ret) + goto err; + + /* CAB */ + ret = ath9k_htc_connect_svc(priv, WMI_CAB_SVC, ath9k_htc_txep, + &priv->cab_ep); + if (ret) + goto err; + + + /* UAPSD */ + ret = ath9k_htc_connect_svc(priv, WMI_UAPSD_SVC, ath9k_htc_txep, + &priv->uapsd_ep); + if (ret) + goto err; + + /* MGMT */ + ret = ath9k_htc_connect_svc(priv, WMI_MGMT_SVC, ath9k_htc_txep, + &priv->mgmt_ep); + if (ret) + goto err; + + /* DATA BE */ + ret = ath9k_htc_connect_svc(priv, WMI_DATA_BE_SVC, ath9k_htc_txep, + &priv->data_be_ep); + if (ret) + goto err; + + /* DATA BK */ + ret = ath9k_htc_connect_svc(priv, WMI_DATA_BK_SVC, ath9k_htc_txep, + &priv->data_bk_ep); + if (ret) + goto err; + + /* DATA VI */ + ret = ath9k_htc_connect_svc(priv, WMI_DATA_VI_SVC, ath9k_htc_txep, + &priv->data_vi_ep); + if (ret) + goto err; + + /* DATA VO */ + ret = ath9k_htc_connect_svc(priv, WMI_DATA_VO_SVC, ath9k_htc_txep, + &priv->data_vo_ep); + if (ret) + goto err; + + ret = htc_init(priv->htc); + if (ret) + goto err; + + return 0; + +err: + dev_err(priv->dev, "ath9k_htc: Unable to initialize HTC services\n"); + return ret; +} + +static int ath9k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ath9k_htc_priv *priv = hw->priv; + + return ath_reg_notifier_apply(wiphy, request, + ath9k_hw_regulatory(priv->ah)); +} + +static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + __be32 val, reg = cpu_to_be32(reg_offset); + int r; + + r = ath9k_wmi_cmd(priv->wmi, WMI_REG_READ_CMDID, + (u8 *) ®, sizeof(reg), + (u8 *) &val, sizeof(val), + 100); + if (unlikely(r)) { + ath_print(common, ATH_DBG_WMI, + "REGISTER READ FAILED: (0x%04x, %d)\n", + reg_offset, r); + return -EIO; + } + + return be32_to_cpu(val); +} + +static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + __be32 buf[2] = { + cpu_to_be32(reg_offset), + cpu_to_be32(val), + }; + int r; + + r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID, + (u8 *) &buf, sizeof(buf), + (u8 *) &val, sizeof(val), + 100); + if (unlikely(r)) { + ath_print(common, ATH_DBG_WMI, + "REGISTER WRITE FAILED:(0x%04x, %d)\n", + reg_offset, r); + } +} + +static const struct ath_ops ath9k_common_ops = { + .read = ath9k_ioread32, + .write = ath9k_iowrite32, +}; + +static void ath_usb_read_cachesize(struct ath_common *common, int *csz) +{ + *csz = L1_CACHE_BYTES >> 2; +} + +static bool ath_usb_eeprom_read(struct ath_common *common, u32 off, u16 *data) +{ + struct ath_hw *ah = (struct ath_hw *) common->ah; + + (void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S)); + + if (!ath9k_hw_wait(ah, + AR_EEPROM_STATUS_DATA, + AR_EEPROM_STATUS_DATA_BUSY | + AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0, + AH_WAIT_TIMEOUT)) + return false; + + *data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA), + AR_EEPROM_STATUS_DATA_VAL); + + return true; +} + +static const struct ath_bus_ops ath9k_usb_bus_ops = { + .read_cachesize = ath_usb_read_cachesize, + .eeprom_read = ath_usb_eeprom_read, +}; + +static void setup_ht_cap(struct ath9k_htc_priv *priv, + struct ieee80211_sta_ht_cap *ht_info) +{ + ht_info->ht_supported = true; + ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40; + + ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; + + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + ht_info->mcs.rx_mask[0] = 0xff; + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; +} + +static int ath9k_init_queues(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + int i; + + for (i = 0; i < ARRAY_SIZE(priv->hwq_map); i++) + priv->hwq_map[i] = -1; + + if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_BE)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for BE traffic\n"); + goto err; + } + + if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_BK)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for BK traffic\n"); + goto err; + } + if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_VI)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for VI traffic\n"); + goto err; + } + if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_VO)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for VO traffic\n"); + goto err; + } + + return 0; + +err: + return -EINVAL; +} + +static void ath9k_init_crypto(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + int i = 0; + + /* Get the hardware key cache size. */ + common->keymax = priv->ah->caps.keycache_size; + if (common->keymax > ATH_KEYMAX) { + ath_print(common, ATH_DBG_ANY, + "Warning, using only %u entries in %u key cache\n", + ATH_KEYMAX, common->keymax); + common->keymax = ATH_KEYMAX; + } + + /* + * Reset the key cache since some parts do not + * reset the contents on initial power up. + */ + for (i = 0; i < common->keymax; i++) + ath9k_hw_keyreset(priv->ah, (u16) i); + + if (ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL)) { + /* + * Whether we should enable h/w TKIP MIC. + * XXX: if we don't support WME TKIP MIC, then we wouldn't + * report WMM capable, so it's always safe to turn on + * TKIP MIC in this case. + */ + ath9k_hw_setcapability(priv->ah, ATH9K_CAP_TKIP_MIC, 0, 1, NULL); + } + + /* + * Check whether the separate key cache entries + * are required to handle both tx+rx MIC keys. + * With split mic keys the number of stations is limited + * to 27 otherwise 59. + */ + if (ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL) + && ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_MIC, NULL) + && ath9k_hw_getcapability(priv->ah, ATH9K_CAP_TKIP_SPLIT, + 0, NULL)) + common->splitmic = 1; + + /* turn on mcast key search if possible */ + if (!ath9k_hw_getcapability(priv->ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) + (void)ath9k_hw_setcapability(priv->ah, ATH9K_CAP_MCAST_KEYSRCH, + 1, 1, NULL); +} + +static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv) +{ + if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) { + priv->sbands[IEEE80211_BAND_2GHZ].channels = + ath9k_2ghz_channels; + priv->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; + priv->sbands[IEEE80211_BAND_2GHZ].n_channels = + ARRAY_SIZE(ath9k_2ghz_channels); + priv->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; + priv->sbands[IEEE80211_BAND_2GHZ].n_bitrates = + ARRAY_SIZE(ath9k_legacy_rates); + } +} + +static void ath9k_init_misc(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + + common->tx_chainmask = priv->ah->caps.tx_chainmask; + common->rx_chainmask = priv->ah->caps.rx_chainmask; + + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + + priv->op_flags |= OP_TXAGGR; +} + +static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid) +{ + struct ath_hw *ah = NULL; + struct ath_common *common; + int ret = 0, csz = 0; + + priv->op_flags |= OP_INVALID; + + ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); + if (!ah) + return -ENOMEM; + + ah->hw_version.devid = devid; + ah->hw_version.subsysid = 0; /* FIXME */ + priv->ah = ah; + + common = ath9k_hw_common(ah); + common->ops = &ath9k_common_ops; + common->bus_ops = &ath9k_usb_bus_ops; + common->ah = ah; + common->hw = priv->hw; + common->priv = priv; + common->debug_mask = ath9k_debug; + + spin_lock_init(&priv->wmi->wmi_lock); + spin_lock_init(&priv->beacon_lock); + mutex_init(&priv->mutex); + mutex_init(&priv->aggr_work.mutex); + tasklet_init(&priv->wmi_tasklet, ath9k_wmi_tasklet, + (unsigned long)priv); + tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet, + (unsigned long)priv); + tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet, (unsigned long)priv); + INIT_DELAYED_WORK(&priv->ath9k_aggr_work, ath9k_htc_aggr_work); + INIT_DELAYED_WORK(&priv->ath9k_ani_work, ath9k_ani_work); + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + ath_read_cachesize(common, &csz); + common->cachelsz = csz << 2; /* convert to bytes */ + + ret = ath9k_hw_init(ah); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to initialize hardware; " + "initialization status: %d\n", ret); + goto err_hw; + } + + ret = ath9k_htc_init_debug(ah); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to create debugfs files\n"); + goto err_debug; + } + + ret = ath9k_init_queues(priv); + if (ret) + goto err_queues; + + ath9k_init_crypto(priv); + ath9k_init_channels_rates(priv); + ath9k_init_misc(priv); + + return 0; + +err_queues: + ath9k_htc_exit_debug(ah); +err_debug: + ath9k_hw_deinit(ah); +err_hw: + + kfree(ah); + priv->ah = NULL; + + return ret; +} + +static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv, + struct ieee80211_hw *hw) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_HAS_RATE_CONTROL; + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC); + + hw->queues = 4; + hw->channel_change_time = 5000; + hw->max_listen_interval = 10; + hw->vif_data_size = sizeof(struct ath9k_htc_vif); + hw->sta_data_size = sizeof(struct ath9k_htc_sta); + + /* tx_frame_hdr is larger than tx_mgmt_hdr anyway */ + hw->extra_tx_headroom = sizeof(struct tx_frame_hdr) + + sizeof(struct htc_frame_hdr) + 4; + + if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &priv->sbands[IEEE80211_BAND_2GHZ]; + + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) + setup_ht_cap(priv, + &priv->sbands[IEEE80211_BAND_2GHZ].ht_cap); + } + + SET_IEEE80211_PERM_ADDR(hw, common->macaddr); +} + +static int ath9k_init_device(struct ath9k_htc_priv *priv, u16 devid) +{ + struct ieee80211_hw *hw = priv->hw; + struct ath_common *common; + struct ath_hw *ah; + int error = 0; + struct ath_regulatory *reg; + + /* Bring up device */ + error = ath9k_init_priv(priv, devid); + if (error != 0) + goto err_init; + + ah = priv->ah; + common = ath9k_hw_common(ah); + ath9k_set_hw_capab(priv, hw); + + /* Initialize regulatory */ + error = ath_regd_init(&common->regulatory, priv->hw->wiphy, + ath9k_reg_notifier); + if (error) + goto err_regd; + + reg = &common->regulatory; + + /* Setup TX */ + error = ath9k_tx_init(priv); + if (error != 0) + goto err_tx; + + /* Setup RX */ + error = ath9k_rx_init(priv); + if (error != 0) + goto err_rx; + + /* Register with mac80211 */ + error = ieee80211_register_hw(hw); + if (error) + goto err_register; + + /* Handle world regulatory */ + if (!ath_is_world_regd(reg)) { + error = regulatory_hint(hw->wiphy, reg->alpha2); + if (error) + goto err_world; + } + + ath9k_init_leds(priv); + ath9k_start_rfkill_poll(priv); + + return 0; + +err_world: + ieee80211_unregister_hw(hw); +err_register: + ath9k_rx_cleanup(priv); +err_rx: + ath9k_tx_cleanup(priv); +err_tx: + /* Nothing */ +err_regd: + ath9k_deinit_priv(priv); +err_init: + return error; +} + +int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev, + u16 devid) +{ + struct ieee80211_hw *hw; + struct ath9k_htc_priv *priv; + int ret; + + hw = ieee80211_alloc_hw(sizeof(struct ath9k_htc_priv), &ath9k_htc_ops); + if (!hw) + return -ENOMEM; + + priv = hw->priv; + priv->hw = hw; + priv->htc = htc_handle; + priv->dev = dev; + htc_handle->drv_priv = priv; + SET_IEEE80211_DEV(hw, priv->dev); + + ret = ath9k_htc_wait_for_target(priv); + if (ret) + goto err_free; + + priv->wmi = ath9k_init_wmi(priv); + if (!priv->wmi) { + ret = -EINVAL; + goto err_free; + } + + ret = ath9k_init_htc_services(priv); + if (ret) + goto err_init; + + ret = ath9k_init_device(priv, devid); + if (ret) + goto err_init; + + return 0; + +err_init: + ath9k_deinit_wmi(priv); +err_free: + ieee80211_free_hw(hw); + return ret; +} + +void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug) +{ + if (htc_handle->drv_priv) { + ath9k_deinit_device(htc_handle->drv_priv); + ath9k_deinit_wmi(htc_handle->drv_priv); + ieee80211_free_hw(htc_handle->drv_priv->hw); + } +} + +#ifdef CONFIG_PM +int ath9k_htc_resume(struct htc_target *htc_handle) +{ + int ret; + + ret = ath9k_htc_wait_for_target(htc_handle->drv_priv); + if (ret) + return ret; + + ret = ath9k_init_htc_services(htc_handle->drv_priv); + return ret; +} +#endif + +static int __init ath9k_htc_init(void) +{ + int error; + + error = ath9k_htc_debug_create_root(); + if (error < 0) { + printk(KERN_ERR + "ath9k_htc: Unable to create debugfs root: %d\n", + error); + goto err_dbg; + } + + error = ath9k_hif_usb_init(); + if (error < 0) { + printk(KERN_ERR + "ath9k_htc: No USB devices found," + " driver not installed.\n"); + error = -ENODEV; + goto err_usb; + } + + return 0; + +err_usb: + ath9k_htc_debug_remove_root(); +err_dbg: + return error; +} +module_init(ath9k_htc_init); + +static void __exit ath9k_htc_exit(void) +{ + ath9k_hif_usb_exit(); + ath9k_htc_debug_remove_root(); + printk(KERN_INFO "ath9k_htc: Driver unloaded\n"); +} +module_exit(ath9k_htc_exit); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c new file mode 100644 index 000000000000..20a2c1341e20 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -0,0 +1,1626 @@ +/* + * Copyright (c) 2010 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 "htc.h" + +#ifdef CONFIG_ATH9K_HTC_DEBUGFS +static struct dentry *ath9k_debugfs_root; +#endif + +/*************/ +/* Utilities */ +/*************/ + +static void ath_update_txpow(struct ath9k_htc_priv *priv) +{ + struct ath_hw *ah = priv->ah; + u32 txpow; + + if (priv->curtxpow != priv->txpowlimit) { + ath9k_hw_set_txpowerlimit(ah, priv->txpowlimit); + /* read back in case value is clamped */ + ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow); + priv->curtxpow = txpow; + } +} + +/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */ +static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, + struct ath9k_channel *ichan) +{ + enum htc_phymode mode; + + mode = HTC_MODE_AUTO; + + switch (ichan->chanmode) { + case CHANNEL_G: + case CHANNEL_G_HT20: + case CHANNEL_G_HT40PLUS: + case CHANNEL_G_HT40MINUS: + mode = HTC_MODE_11NG; + break; + case CHANNEL_A: + case CHANNEL_A_HT20: + case CHANNEL_A_HT40PLUS: + case CHANNEL_A_HT40MINUS: + mode = HTC_MODE_11NA; + break; + default: + break; + } + + return mode; +} + +static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, + struct ieee80211_hw *hw, + struct ath9k_channel *hchan) +{ + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_conf *conf = &common->hw->conf; + bool fastcc = true; + struct ieee80211_channel *channel = hw->conf.channel; + enum htc_phymode mode; + u16 htc_mode; + u8 cmd_rsp; + int ret; + + if (priv->op_flags & OP_INVALID) + return -EIO; + + if (priv->op_flags & OP_FULL_RESET) + fastcc = false; + + /* Fiddle around with fastcc later on, for now just use full reset */ + fastcc = false; + + htc_stop(priv->htc); + WMI_CMD(WMI_DISABLE_INTR_CMDID); + WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); + WMI_CMD(WMI_STOP_RECV_CMDID); + + ath_print(common, ATH_DBG_CONFIG, + "(%u MHz) -> (%u MHz), HT: %d, HT40: %d\n", + priv->ah->curchan->channel, + channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf)); + + ret = ath9k_hw_reset(ah, hchan, fastcc); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to reset channel (%u Mhz) " + "reset status %d\n", channel->center_freq, ret); + goto err; + } + + ath_update_txpow(priv); + + WMI_CMD(WMI_START_RECV_CMDID); + if (ret) + goto err; + + ath9k_host_rx_init(priv); + + mode = ath9k_htc_get_curmode(priv, hchan); + htc_mode = cpu_to_be16(mode); + WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); + if (ret) + goto err; + + WMI_CMD(WMI_ENABLE_INTR_CMDID); + if (ret) + goto err; + + htc_start(priv->htc); + + priv->op_flags &= ~OP_FULL_RESET; +err: + return ret; +} + +static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_vif hvif; + int ret = 0; + u8 cmd_rsp; + + if (priv->nvifs > 0) + return -ENOBUFS; + + memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); + memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); + + hvif.opmode = cpu_to_be32(HTC_M_MONITOR); + priv->ah->opmode = NL80211_IFTYPE_MONITOR; + hvif.index = priv->nvifs; + + WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); + if (ret) + return ret; + + priv->nvifs++; + return 0; +} + +static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_vif hvif; + int ret = 0; + u8 cmd_rsp; + + memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); + memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); + hvif.index = 0; /* Should do for now */ + WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); + priv->nvifs--; + + return ret; +} + +static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_sta tsta; + struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; + struct ath9k_htc_sta *ista; + int ret; + u8 cmd_rsp; + + if (priv->nstations >= ATH9K_HTC_MAX_STA) + return -ENOBUFS; + + memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); + + if (sta) { + ista = (struct ath9k_htc_sta *) sta->drv_priv; + memcpy(&tsta.macaddr, sta->addr, ETH_ALEN); + memcpy(&tsta.bssid, common->curbssid, ETH_ALEN); + tsta.associd = common->curaid; + tsta.is_vif_sta = 0; + tsta.valid = true; + ista->index = priv->nstations; + } else { + memcpy(&tsta.macaddr, vif->addr, ETH_ALEN); + tsta.is_vif_sta = 1; + } + + tsta.sta_index = priv->nstations; + tsta.vif_index = avp->index; + tsta.maxampdu = 0xffff; + if (sta && sta->ht_cap.ht_supported) + tsta.flags = cpu_to_be16(ATH_HTC_STA_HT); + + WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); + if (ret) { + if (sta) + ath_print(common, ATH_DBG_FATAL, + "Unable to add station entry for: %pM\n", sta->addr); + return ret; + } + + if (sta) + ath_print(common, ATH_DBG_CONFIG, + "Added a station entry for: %pM (idx: %d)\n", + sta->addr, tsta.sta_index); + + priv->nstations++; + return 0; +} + +static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_sta *ista; + int ret; + u8 cmd_rsp, sta_idx; + + if (sta) { + ista = (struct ath9k_htc_sta *) sta->drv_priv; + sta_idx = ista->index; + } else { + sta_idx = 0; + } + + WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); + if (ret) { + if (sta) + ath_print(common, ATH_DBG_FATAL, + "Unable to remove station entry for: %pM\n", + sta->addr); + return ret; + } + + if (sta) + ath_print(common, ATH_DBG_CONFIG, + "Removed a station entry for: %pM (idx: %d)\n", + sta->addr, sta_idx); + + priv->nstations--; + return 0; +} + +static int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv) +{ + struct ath9k_htc_cap_target tcap; + int ret; + u8 cmd_rsp; + + memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target)); + + /* FIXME: Values are hardcoded */ + tcap.flags = 0x240c40; + tcap.flags_ext = 0x80601000; + tcap.ampdu_limit = 0xffff0000; + tcap.ampdu_subframes = 20; + tcap.tx_chainmask_legacy = 1; + tcap.protmode = 1; + tcap.tx_chainmask = 1; + + WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap); + + return ret; +} + +static int ath9k_htc_init_rate(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; + struct ieee80211_supported_band *sband; + struct ath9k_htc_target_rate trate; + u32 caps = 0; + u8 cmd_rsp; + int i, j, ret; + + memset(&trate, 0, sizeof(trate)); + + /* Only 2GHz is supported */ + sband = priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; + + for (i = 0, j = 0; i < sband->n_bitrates; i++) { + if (sta->supp_rates[sband->band] & BIT(i)) { + priv->tgt_rate.rates.legacy_rates.rs_rates[j] + = (sband->bitrates[i].bitrate * 2) / 10; + j++; + } + } + priv->tgt_rate.rates.legacy_rates.rs_nrates = j; + + if (sta->ht_cap.ht_supported) { + for (i = 0, j = 0; i < 77; i++) { + if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8))) + priv->tgt_rate.rates.ht_rates.rs_rates[j++] = i; + if (j == ATH_HTC_RATE_MAX) + break; + } + priv->tgt_rate.rates.ht_rates.rs_nrates = j; + + caps = WLAN_RC_HT_FLAG; + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) + caps |= WLAN_RC_40_FLAG; + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + caps |= WLAN_RC_SGI_FLAG; + + } + + priv->tgt_rate.sta_index = ista->index; + priv->tgt_rate.isnew = 1; + trate = priv->tgt_rate; + priv->tgt_rate.capflags = caps; + trate.capflags = cpu_to_be32(caps); + + WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, &trate); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to initialize Rate information on target\n"); + return ret; + } + + ath_print(common, ATH_DBG_CONFIG, + "Updated target STA: %pM (caps: 0x%x)\n", sta->addr, caps); + return 0; +} + +static bool check_rc_update(struct ieee80211_hw *hw, bool *cw40) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ieee80211_conf *conf = &hw->conf; + + if (!conf_is_ht(conf)) + return false; + + if (!(priv->op_flags & OP_ASSOCIATED) || + (priv->op_flags & OP_SCANNING)) + return false; + + if (conf_is_ht40(conf)) { + if (priv->ah->curchan->chanmode & + (CHANNEL_HT40PLUS | CHANNEL_HT40MINUS)) { + return false; + } else { + *cw40 = true; + return true; + } + } else { /* ht20 */ + if (priv->ah->curchan->chanmode & CHANNEL_HT20) + return false; + else + return true; + } +} + +static void ath9k_htc_rc_update(struct ath9k_htc_priv *priv, bool is_cw40) +{ + struct ath9k_htc_target_rate trate; + struct ath_common *common = ath9k_hw_common(priv->ah); + int ret; + u8 cmd_rsp; + + memset(&trate, 0, sizeof(trate)); + + trate = priv->tgt_rate; + + if (is_cw40) + priv->tgt_rate.capflags |= WLAN_RC_40_FLAG; + else + priv->tgt_rate.capflags &= ~WLAN_RC_40_FLAG; + + trate.capflags = cpu_to_be32(priv->tgt_rate.capflags); + + WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, &trate); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to update Rate information on target\n"); + return; + } + + ath_print(common, ATH_DBG_CONFIG, "Rate control updated with " + "caps:0x%x on target\n", priv->tgt_rate.capflags); +} + +static int ath9k_htc_aggr_oper(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif, + u8 *sta_addr, u8 tid, bool oper) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_aggr aggr; + struct ieee80211_sta *sta = NULL; + struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; + int ret = 0; + u8 cmd_rsp; + + if (tid > ATH9K_HTC_MAX_TID) + return -EINVAL; + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, sta_addr); + if (sta) { + ista = (struct ath9k_htc_sta *) sta->drv_priv; + } else { + rcu_read_unlock(); + return -EINVAL; + } + + if (!ista) { + rcu_read_unlock(); + return -EINVAL; + } + + memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr)); + + aggr.sta_index = ista->index; + rcu_read_unlock(); + aggr.tidno = tid; + aggr.aggr_enable = oper; + + if (oper) + ista->tid_state[tid] = AGGR_START; + else + ista->tid_state[tid] = AGGR_STOP; + + WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr); + if (ret) + ath_print(common, ATH_DBG_CONFIG, + "Unable to %s TX aggregation for (%pM, %d)\n", + (oper) ? "start" : "stop", sta->addr, tid); + else + ath_print(common, ATH_DBG_CONFIG, + "%s aggregation for (%pM, %d)\n", + (oper) ? "Starting" : "Stopping", sta->addr, tid); + + return ret; +} + +void ath9k_htc_aggr_work(struct work_struct *work) +{ + int ret = 0; + struct ath9k_htc_priv *priv = + container_of(work, struct ath9k_htc_priv, + ath9k_aggr_work.work); + struct ath9k_htc_aggr_work *wk = &priv->aggr_work; + + mutex_lock(&wk->mutex); + + switch (wk->action) { + case IEEE80211_AMPDU_TX_START: + ret = ath9k_htc_aggr_oper(priv, wk->vif, wk->sta_addr, + wk->tid, true); + if (!ret) + ieee80211_start_tx_ba_cb(wk->vif, wk->sta_addr, + wk->tid); + break; + case IEEE80211_AMPDU_TX_STOP: + ath9k_htc_aggr_oper(priv, wk->vif, wk->sta_addr, + wk->tid, false); + ieee80211_stop_tx_ba_cb(wk->vif, wk->sta_addr, wk->tid); + break; + default: + ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, + "Unknown AMPDU action\n"); + } + + mutex_unlock(&wk->mutex); +} + +/*********/ +/* DEBUG */ +/*********/ + +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + +static int ath9k_debugfs_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath9k_htc_priv *priv = + (struct ath9k_htc_priv *) file->private_data; + struct ath9k_htc_target_stats cmd_rsp; + char buf[512]; + unsigned int len = 0; + int ret = 0; + + memset(&cmd_rsp, 0, sizeof(cmd_rsp)); + + WMI_CMD(WMI_TGT_STATS_CMDID); + if (ret) + return -EINVAL; + + + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Short Retries", + be32_to_cpu(cmd_rsp.tx_shortretry)); + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Long Retries", + be32_to_cpu(cmd_rsp.tx_longretry)); + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Xretries", + be32_to_cpu(cmd_rsp.tx_xretries)); + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Unaggr. Xretries", + be32_to_cpu(cmd_rsp.ht_txunaggr_xretry)); + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Xretries (HT)", + be32_to_cpu(cmd_rsp.ht_tx_xretries)); + len += snprintf(buf + len, sizeof(buf) - len, + "%19s : %10u\n", "TX Rate", priv->debug.txrate); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_tgt_stats = { + .read = read_file_tgt_stats, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + +static ssize_t read_file_xmit(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath9k_htc_priv *priv = + (struct ath9k_htc_priv *) file->private_data; + char buf[512]; + unsigned int len = 0; + + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Buffers queued", + priv->debug.tx_stats.buf_queued); + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "Buffers completed", + priv->debug.tx_stats.buf_completed); + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs queued", + priv->debug.tx_stats.skb_queued); + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs completed", + priv->debug.tx_stats.skb_completed); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_xmit = { + .read = read_file_xmit, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + +static ssize_t read_file_recv(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath9k_htc_priv *priv = + (struct ath9k_htc_priv *) file->private_data; + char buf[512]; + unsigned int len = 0; + + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs allocated", + priv->debug.rx_stats.skb_allocated); + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs completed", + priv->debug.rx_stats.skb_completed); + len += snprintf(buf + len, sizeof(buf) - len, + "%20s : %10u\n", "SKBs Dropped", + priv->debug.rx_stats.skb_dropped); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_recv = { + .read = read_file_recv, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + +int ath9k_htc_init_debug(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + + if (!ath9k_debugfs_root) + return -ENOENT; + + priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy), + ath9k_debugfs_root); + if (!priv->debug.debugfs_phy) + goto err; + + priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR, + priv->debug.debugfs_phy, + priv, &fops_tgt_stats); + if (!priv->debug.debugfs_tgt_stats) + goto err; + + + priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR, + priv->debug.debugfs_phy, + priv, &fops_xmit); + if (!priv->debug.debugfs_xmit) + goto err; + + priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR, + priv->debug.debugfs_phy, + priv, &fops_recv); + if (!priv->debug.debugfs_recv) + goto err; + + return 0; + +err: + ath9k_htc_exit_debug(ah); + return -ENOMEM; +} + +void ath9k_htc_exit_debug(struct ath_hw *ah) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; + + debugfs_remove(priv->debug.debugfs_recv); + debugfs_remove(priv->debug.debugfs_xmit); + debugfs_remove(priv->debug.debugfs_tgt_stats); + debugfs_remove(priv->debug.debugfs_phy); +} + +int ath9k_htc_debug_create_root(void) +{ + ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!ath9k_debugfs_root) + return -ENOENT; + + return 0; +} + +void ath9k_htc_debug_remove_root(void) +{ + debugfs_remove(ath9k_debugfs_root); + ath9k_debugfs_root = NULL; +} + +#endif /* CONFIG_ATH9K_HTC_DEBUGFS */ + +/*******/ +/* ANI */ +/*******/ + +static void ath_start_ani(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + unsigned long timestamp = jiffies_to_msecs(jiffies); + + common->ani.longcal_timer = timestamp; + common->ani.shortcal_timer = timestamp; + common->ani.checkani_timer = timestamp; + + ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, + msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); +} + +void ath9k_ani_work(struct work_struct *work) +{ + struct ath9k_htc_priv *priv = + container_of(work, struct ath9k_htc_priv, + ath9k_ani_work.work); + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + bool longcal = false; + bool shortcal = false; + bool aniflag = false; + unsigned int timestamp = jiffies_to_msecs(jiffies); + u32 cal_interval, short_cal_interval; + + short_cal_interval = ATH_STA_SHORT_CALINTERVAL; + + /* Long calibration runs independently of short calibration. */ + if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { + longcal = true; + ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); + common->ani.longcal_timer = timestamp; + } + + /* Short calibration applies only while caldone is false */ + if (!common->ani.caldone) { + if ((timestamp - common->ani.shortcal_timer) >= + short_cal_interval) { + shortcal = true; + ath_print(common, ATH_DBG_ANI, + "shortcal @%lu\n", jiffies); + common->ani.shortcal_timer = timestamp; + common->ani.resetcal_timer = timestamp; + } + } else { + if ((timestamp - common->ani.resetcal_timer) >= + ATH_RESTART_CALINTERVAL) { + common->ani.caldone = ath9k_hw_reset_calvalid(ah); + if (common->ani.caldone) + common->ani.resetcal_timer = timestamp; + } + } + + /* Verify whether we must check ANI */ + if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { + aniflag = true; + common->ani.checkani_timer = timestamp; + } + + /* Skip all processing if there's nothing to do. */ + if (longcal || shortcal || aniflag) { + /* Call ANI routine if necessary */ + if (aniflag) + ath9k_hw_ani_monitor(ah, ah->curchan); + + /* Perform calibration if necessary */ + if (longcal || shortcal) { + common->ani.caldone = + ath9k_hw_calibrate(ah, ah->curchan, + common->rx_chainmask, + longcal); + + if (longcal) + common->ani.noise_floor = + ath9k_hw_getchan_noise(ah, ah->curchan); + + ath_print(common, ATH_DBG_ANI, + " calibrate chan %u/%x nf: %d\n", + ah->curchan->channel, + ah->curchan->channelFlags, + common->ani.noise_floor); + } + } + + /* + * Set timer interval based on previous results. + * The interval must be the shortest necessary to satisfy ANI, + * short calibration and long calibration. + */ + cal_interval = ATH_LONG_CALINTERVAL; + if (priv->ah->config.enable_ani) + cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); + if (!common->ani.caldone) + cal_interval = min(cal_interval, (u32)short_cal_interval); + + ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, + msecs_to_jiffies(cal_interval)); +} + +/*******/ +/* LED */ +/*******/ + +static void ath9k_led_blink_work(struct work_struct *work) +{ + struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, + ath9k_led_blink_work.work); + + if (!(priv->op_flags & OP_LED_ASSOCIATED)) + return; + + if ((priv->led_on_duration == ATH_LED_ON_DURATION_IDLE) || + (priv->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0); + else + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, + (priv->op_flags & OP_LED_ON) ? 1 : 0); + + ieee80211_queue_delayed_work(priv->hw, + &priv->ath9k_led_blink_work, + (priv->op_flags & OP_LED_ON) ? + msecs_to_jiffies(priv->led_off_duration) : + msecs_to_jiffies(priv->led_on_duration)); + + priv->led_on_duration = priv->led_on_cnt ? + max((ATH_LED_ON_DURATION_IDLE - priv->led_on_cnt), 25) : + ATH_LED_ON_DURATION_IDLE; + priv->led_off_duration = priv->led_off_cnt ? + max((ATH_LED_OFF_DURATION_IDLE - priv->led_off_cnt), 10) : + ATH_LED_OFF_DURATION_IDLE; + priv->led_on_cnt = priv->led_off_cnt = 0; + + if (priv->op_flags & OP_LED_ON) + priv->op_flags &= ~OP_LED_ON; + else + priv->op_flags |= OP_LED_ON; +} + +static void ath9k_led_brightness_work(struct work_struct *work) +{ + struct ath_led *led = container_of(work, struct ath_led, + brightness_work.work); + struct ath9k_htc_priv *priv = led->priv; + + switch (led->brightness) { + case LED_OFF: + if (led->led_type == ATH_LED_ASSOC || + led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, + (led->led_type == ATH_LED_RADIO)); + priv->op_flags &= ~OP_LED_ASSOCIATED; + if (led->led_type == ATH_LED_RADIO) + priv->op_flags &= ~OP_LED_ON; + } else { + priv->led_off_cnt++; + } + break; + case LED_FULL: + if (led->led_type == ATH_LED_ASSOC) { + priv->op_flags |= OP_LED_ASSOCIATED; + ieee80211_queue_delayed_work(priv->hw, + &priv->ath9k_led_blink_work, 0); + } else if (led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0); + priv->op_flags |= OP_LED_ON; + } else { + priv->led_on_cnt++; + } + break; + default: + break; + } +} + +static void ath9k_led_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); + struct ath9k_htc_priv *priv = led->priv; + + led->brightness = brightness; + if (!(priv->op_flags & OP_LED_DEINIT)) + ieee80211_queue_delayed_work(priv->hw, + &led->brightness_work, 0); +} + +static void ath9k_led_stop_brightness(struct ath9k_htc_priv *priv) +{ + cancel_delayed_work_sync(&priv->radio_led.brightness_work); + cancel_delayed_work_sync(&priv->assoc_led.brightness_work); + cancel_delayed_work_sync(&priv->tx_led.brightness_work); + cancel_delayed_work_sync(&priv->rx_led.brightness_work); +} + +static int ath9k_register_led(struct ath9k_htc_priv *priv, struct ath_led *led, + char *trigger) +{ + int ret; + + led->priv = priv; + led->led_cdev.name = led->name; + led->led_cdev.default_trigger = trigger; + led->led_cdev.brightness_set = ath9k_led_brightness; + + ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_cdev); + if (ret) + ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, + "Failed to register led:%s", led->name); + else + led->registered = 1; + + INIT_DELAYED_WORK(&led->brightness_work, ath9k_led_brightness_work); + + return ret; +} + +static void ath9k_unregister_led(struct ath_led *led) +{ + if (led->registered) { + led_classdev_unregister(&led->led_cdev); + led->registered = 0; + } +} + +void ath9k_deinit_leds(struct ath9k_htc_priv *priv) +{ + priv->op_flags |= OP_LED_DEINIT; + ath9k_unregister_led(&priv->assoc_led); + priv->op_flags &= ~OP_LED_ASSOCIATED; + ath9k_unregister_led(&priv->tx_led); + ath9k_unregister_led(&priv->rx_led); + ath9k_unregister_led(&priv->radio_led); + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); +} + +void ath9k_init_leds(struct ath9k_htc_priv *priv) +{ + char *trigger; + int ret; + + if (AR_SREV_9287(priv->ah)) + priv->ah->led_pin = ATH_LED_PIN_9287; + else if (AR_SREV_9271(priv->ah)) + priv->ah->led_pin = ATH_LED_PIN_9271; + else + priv->ah->led_pin = ATH_LED_PIN_DEF; + + /* Configure gpio 1 for output */ + ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + /* LED off, active low */ + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); + + INIT_DELAYED_WORK(&priv->ath9k_led_blink_work, ath9k_led_blink_work); + + trigger = ieee80211_get_radio_led_name(priv->hw); + snprintf(priv->radio_led.name, sizeof(priv->radio_led.name), + "ath9k-%s::radio", wiphy_name(priv->hw->wiphy)); + ret = ath9k_register_led(priv, &priv->radio_led, trigger); + priv->radio_led.led_type = ATH_LED_RADIO; + if (ret) + goto fail; + + trigger = ieee80211_get_assoc_led_name(priv->hw); + snprintf(priv->assoc_led.name, sizeof(priv->assoc_led.name), + "ath9k-%s::assoc", wiphy_name(priv->hw->wiphy)); + ret = ath9k_register_led(priv, &priv->assoc_led, trigger); + priv->assoc_led.led_type = ATH_LED_ASSOC; + if (ret) + goto fail; + + trigger = ieee80211_get_tx_led_name(priv->hw); + snprintf(priv->tx_led.name, sizeof(priv->tx_led.name), + "ath9k-%s::tx", wiphy_name(priv->hw->wiphy)); + ret = ath9k_register_led(priv, &priv->tx_led, trigger); + priv->tx_led.led_type = ATH_LED_TX; + if (ret) + goto fail; + + trigger = ieee80211_get_rx_led_name(priv->hw); + snprintf(priv->rx_led.name, sizeof(priv->rx_led.name), + "ath9k-%s::rx", wiphy_name(priv->hw->wiphy)); + ret = ath9k_register_led(priv, &priv->rx_led, trigger); + priv->rx_led.led_type = ATH_LED_RX; + if (ret) + goto fail; + + priv->op_flags &= ~OP_LED_DEINIT; + + return; + +fail: + cancel_delayed_work_sync(&priv->ath9k_led_blink_work); + ath9k_deinit_leds(priv); +} + +/*******************/ +/* Rfkill */ +/*******************/ + +static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv) +{ + return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) == + priv->ah->rfkill_polarity; +} + +static void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + bool blocked = !!ath_is_rfkill_set(priv); + + wiphy_rfkill_set_hw_state(hw->wiphy, blocked); +} + +void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv) +{ + if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) + wiphy_rfkill_start_polling(priv->hw->wiphy); +} + +/**********************/ +/* mac80211 Callbacks */ +/**********************/ + +static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + struct ath9k_htc_priv *priv = hw->priv; + int padpos, padsize; + + hdr = (struct ieee80211_hdr *) skb->data; + + /* Add the padding after the header if this is not already done */ + padpos = ath9k_cmn_padpos(hdr->frame_control); + padsize = padpos & 3; + if (padsize && skb->len > padpos) { + if (skb_headroom(skb) < padsize) + return -1; + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, padpos); + } + + if (ath9k_htc_tx_start(priv, skb) != 0) { + ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, "Tx failed"); + goto fail_tx; + } + + return 0; + +fail_tx: + dev_kfree_skb_any(skb); + return 0; +} + +static int ath9k_htc_start(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ieee80211_channel *curchan = hw->conf.channel; + struct ath9k_channel *init_channel; + int ret = 0; + enum htc_phymode mode; + u16 htc_mode; + u8 cmd_rsp; + + ath_print(common, ATH_DBG_CONFIG, + "Starting driver with initial channel: %d MHz\n", + curchan->center_freq); + + mutex_lock(&priv->mutex); + + /* setup initial channel */ + init_channel = ath9k_cmn_get_curchannel(hw, ah); + + /* Reset SERDES registers */ + ath9k_hw_configpcipowersave(ah, 0, 0); + + ath9k_hw_htc_resetinit(ah); + ret = ath9k_hw_reset(ah, init_channel, false); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to reset hardware; reset status %d " + "(freq %u MHz)\n", ret, curchan->center_freq); + goto mutex_unlock; + } + + ath_update_txpow(priv); + + mode = ath9k_htc_get_curmode(priv, init_channel); + htc_mode = cpu_to_be16(mode); + WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); + if (ret) + goto mutex_unlock; + + WMI_CMD(WMI_ATH_INIT_CMDID); + if (ret) + goto mutex_unlock; + + WMI_CMD(WMI_START_RECV_CMDID); + if (ret) + goto mutex_unlock; + + ath9k_host_rx_init(priv); + + priv->op_flags &= ~OP_INVALID; + htc_start(priv->htc); + +mutex_unlock: + mutex_unlock(&priv->mutex); + return ret; +} + +static void ath9k_htc_stop(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + int ret = 0; + u8 cmd_rsp; + + mutex_lock(&priv->mutex); + + if (priv->op_flags & OP_INVALID) { + ath_print(common, ATH_DBG_ANY, "Device not present\n"); + mutex_unlock(&priv->mutex); + return; + } + + htc_stop(priv->htc); + WMI_CMD(WMI_DISABLE_INTR_CMDID); + WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); + WMI_CMD(WMI_STOP_RECV_CMDID); + ath9k_hw_phy_disable(ah); + ath9k_hw_disable(ah); + ath9k_hw_configpcipowersave(ah, 1, 1); + ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); + + cancel_delayed_work_sync(&priv->ath9k_ani_work); + cancel_delayed_work_sync(&priv->ath9k_aggr_work); + cancel_delayed_work_sync(&priv->ath9k_led_blink_work); + ath9k_led_stop_brightness(priv); + skb_queue_purge(&priv->tx_queue); + + /* Remove monitor interface here */ + if (ah->opmode == NL80211_IFTYPE_MONITOR) { + if (ath9k_htc_remove_monitor_interface(priv)) + ath_print(common, ATH_DBG_FATAL, + "Unable to remove monitor interface\n"); + else + ath_print(common, ATH_DBG_CONFIG, + "Monitor interface removed\n"); + } + + priv->op_flags |= OP_INVALID; + mutex_unlock(&priv->mutex); + + ath_print(common, ATH_DBG_CONFIG, "Driver halt\n"); +} + +static int ath9k_htc_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath9k_htc_vif *avp = (void *)vif->drv_priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_target_vif hvif; + int ret = 0; + u8 cmd_rsp; + + mutex_lock(&priv->mutex); + + /* Only one interface for now */ + if (priv->nvifs > 0) { + ret = -ENOBUFS; + goto out; + } + + memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); + memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + hvif.opmode = cpu_to_be32(HTC_M_STA); + break; + case NL80211_IFTYPE_ADHOC: + hvif.opmode = cpu_to_be32(HTC_M_IBSS); + break; + default: + ath_print(common, ATH_DBG_FATAL, + "Interface type %d not yet supported\n", vif->type); + ret = -EOPNOTSUPP; + goto out; + } + + ath_print(common, ATH_DBG_CONFIG, + "Attach a VIF of type: %d\n", vif->type); + + priv->ah->opmode = vif->type; + + /* Index starts from zero on the target */ + avp->index = hvif.index = priv->nvifs; + hvif.rtsthreshold = cpu_to_be16(2304); + WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); + if (ret) + goto out; + + priv->nvifs++; + + /* + * We need a node in target to tx mgmt frames + * before association. + */ + ret = ath9k_htc_add_station(priv, vif, NULL); + if (ret) + goto out; + + ret = ath9k_htc_update_cap_target(priv); + if (ret) + ath_print(common, ATH_DBG_CONFIG, "Failed to update" + " capability in target \n"); + + priv->vif = vif; +out: + mutex_unlock(&priv->mutex); + return ret; +} + +static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_vif *avp = (void *)vif->drv_priv; + struct ath9k_htc_target_vif hvif; + int ret = 0; + u8 cmd_rsp; + + ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n"); + + mutex_lock(&priv->mutex); + + memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); + memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); + hvif.index = avp->index; + WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); + priv->nvifs--; + + ath9k_htc_remove_station(priv, vif, NULL); + + if (vif->type == NL80211_IFTYPE_ADHOC) { + spin_lock_bh(&priv->beacon_lock); + if (priv->beacon) + dev_kfree_skb_any(priv->beacon); + priv->beacon = NULL; + spin_unlock_bh(&priv->beacon_lock); + } + + priv->vif = NULL; + + mutex_unlock(&priv->mutex); +} + +static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ieee80211_conf *conf = &hw->conf; + + mutex_lock(&priv->mutex); + + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { + struct ieee80211_channel *curchan = hw->conf.channel; + int pos = curchan->hw_value; + bool is_cw40 = false; + + ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n", + curchan->center_freq); + + if (check_rc_update(hw, &is_cw40)) + ath9k_htc_rc_update(priv, is_cw40); + + ath9k_cmn_update_ichannel(hw, &priv->ah->channels[pos]); + + if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { + ath_print(common, ATH_DBG_FATAL, + "Unable to set channel\n"); + mutex_unlock(&priv->mutex); + return -EINVAL; + } + + } + + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + if (conf->flags & IEEE80211_CONF_MONITOR) { + if (ath9k_htc_add_monitor_interface(priv)) + ath_print(common, ATH_DBG_FATAL, + "Failed to set monitor mode\n"); + else + ath_print(common, ATH_DBG_CONFIG, + "HW opmode set to Monitor mode\n"); + } + } + + mutex_unlock(&priv->mutex); + + return 0; +} + +#define SUPPORTED_FILTERS \ + (FIF_PROMISC_IN_BSS | \ + FIF_ALLMULTI | \ + FIF_CONTROL | \ + FIF_PSPOLL | \ + FIF_OTHER_BSS | \ + FIF_BCN_PRBRESP_PROMISC | \ + FIF_FCSFAIL) + +static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct ath9k_htc_priv *priv = hw->priv; + u32 rfilt; + + mutex_lock(&priv->mutex); + + changed_flags &= SUPPORTED_FILTERS; + *total_flags &= SUPPORTED_FILTERS; + + priv->rxfilter = *total_flags; + rfilt = ath9k_cmn_calcrxfilter(hw, priv->ah, priv->rxfilter); + ath9k_hw_setrxfilter(priv->ah, rfilt); + + ath_print(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG, + "Set HW RX filter: 0x%x\n", rfilt); + + mutex_unlock(&priv->mutex); +} + +static void ath9k_htc_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct ath9k_htc_priv *priv = hw->priv; + int ret; + + switch (cmd) { + case STA_NOTIFY_ADD: + ret = ath9k_htc_add_station(priv, vif, sta); + if (!ret) + ath9k_htc_init_rate(priv, vif, sta); + break; + case STA_NOTIFY_REMOVE: + ath9k_htc_remove_station(priv, vif, sta); + break; + default: + break; + } +} + +static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_tx_queue_info qi; + int ret = 0, qnum; + + if (queue >= WME_NUM_AC) + return 0; + + mutex_lock(&priv->mutex); + + memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); + + qi.tqi_aifs = params->aifs; + qi.tqi_cwmin = params->cw_min; + qi.tqi_cwmax = params->cw_max; + qi.tqi_burstTime = params->txop; + + qnum = get_hw_qnum(queue, priv->hwq_map); + + ath_print(common, ATH_DBG_CONFIG, + "Configure tx [queue/hwq] [%d/%d], " + "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", + queue, qnum, params->aifs, params->cw_min, + params->cw_max, params->txop); + + ret = ath_htc_txq_update(priv, qnum, &qi); + if (ret) + ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n"); + + mutex_unlock(&priv->mutex); + + return ret; +} + +static int ath9k_htc_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_common *common = ath9k_hw_common(priv->ah); + int ret = 0; + + if (htc_modparam_nohwcrypt) + return -ENOSPC; + + mutex_lock(&priv->mutex); + ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n"); + + switch (cmd) { + case SET_KEY: + ret = ath9k_cmn_key_config(common, vif, sta, key); + if (ret >= 0) { + key->hw_key_idx = ret; + /* push IV and Michael MIC generation to stack */ + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + if (key->alg == ALG_TKIP) + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + if (priv->ah->sw_mgmt_crypto && key->alg == ALG_CCMP) + key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; + ret = 0; + } + break; + case DISABLE_KEY: + ath9k_cmn_key_delete(common, key); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&priv->mutex); + + return ret; +} + +static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changed) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + + mutex_lock(&priv->mutex); + + if (changed & BSS_CHANGED_ASSOC) { + common->curaid = bss_conf->assoc ? + bss_conf->aid : 0; + ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", + bss_conf->assoc); + + if (bss_conf->assoc) { + priv->op_flags |= OP_ASSOCIATED; + ath_start_ani(priv); + } else { + priv->op_flags &= ~OP_ASSOCIATED; + cancel_delayed_work_sync(&priv->ath9k_ani_work); + } + } + + if (changed & BSS_CHANGED_BSSID) { + /* Set BSSID */ + memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); + ath9k_hw_write_associd(ah); + + ath_print(common, ATH_DBG_CONFIG, + "BSSID: %pM aid: 0x%x\n", + common->curbssid, common->curaid); + } + + if ((changed & BSS_CHANGED_BEACON_INT) || + (changed & BSS_CHANGED_BEACON) || + ((changed & BSS_CHANGED_BEACON_ENABLED) && + bss_conf->enable_beacon)) { + priv->op_flags |= OP_ENABLE_BEACON; + ath9k_htc_beacon_config(priv, vif, bss_conf); + } + + if (changed & BSS_CHANGED_BEACON) + ath9k_htc_beacon_update(priv, vif); + + if ((changed & BSS_CHANGED_BEACON_ENABLED) && + !bss_conf->enable_beacon) { + priv->op_flags &= ~OP_ENABLE_BEACON; + ath9k_htc_beacon_config(priv, vif, bss_conf); + } + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", + bss_conf->use_short_preamble); + if (bss_conf->use_short_preamble) + priv->op_flags |= OP_PREAMBLE_SHORT; + else + priv->op_flags &= ~OP_PREAMBLE_SHORT; + } + + if (changed & BSS_CHANGED_ERP_CTS_PROT) { + ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n", + bss_conf->use_cts_prot); + if (bss_conf->use_cts_prot && + hw->conf.channel->band != IEEE80211_BAND_5GHZ) + priv->op_flags |= OP_PROTECT_ENABLE; + else + priv->op_flags &= ~OP_PROTECT_ENABLE; + } + + if (changed & BSS_CHANGED_ERP_SLOT) { + if (bss_conf->use_short_slot) + ah->slottime = 9; + else + ah->slottime = 20; + + ath9k_hw_init_global_settings(ah); + } + + mutex_unlock(&priv->mutex); +} + +static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + u64 tsf; + + mutex_lock(&priv->mutex); + tsf = ath9k_hw_gettsf64(priv->ah); + mutex_unlock(&priv->mutex); + + return tsf; +} + +static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf) +{ + struct ath9k_htc_priv *priv = hw->priv; + + mutex_lock(&priv->mutex); + ath9k_hw_settsf64(priv->ah, tsf); + mutex_unlock(&priv->mutex); +} + +static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + + mutex_lock(&priv->mutex); + ath9k_hw_reset_tsf(priv->ah); + mutex_unlock(&priv->mutex); +} + +static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, + u16 tid, u16 *ssn) +{ + struct ath9k_htc_priv *priv = hw->priv; + struct ath9k_htc_aggr_work *work = &priv->aggr_work; + struct ath9k_htc_sta *ista; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + break; + case IEEE80211_AMPDU_RX_STOP: + break; + case IEEE80211_AMPDU_TX_START: + case IEEE80211_AMPDU_TX_STOP: + if (!(priv->op_flags & OP_TXAGGR)) + return -ENOTSUPP; + memcpy(work->sta_addr, sta->addr, ETH_ALEN); + work->hw = hw; + work->vif = vif; + work->action = action; + work->tid = tid; + ieee80211_queue_delayed_work(hw, &priv->ath9k_aggr_work, 0); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ista = (struct ath9k_htc_sta *) sta->drv_priv; + ista->tid_state[tid] = AGGR_OPERATIONAL; + break; + default: + ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, + "Unknown AMPDU action\n"); + } + + return 0; +} + +static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + + mutex_lock(&priv->mutex); + spin_lock_bh(&priv->beacon_lock); + priv->op_flags |= OP_SCANNING; + spin_unlock_bh(&priv->beacon_lock); + cancel_delayed_work_sync(&priv->ath9k_ani_work); + mutex_unlock(&priv->mutex); +} + +static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) +{ + struct ath9k_htc_priv *priv = hw->priv; + + mutex_lock(&priv->mutex); + spin_lock_bh(&priv->beacon_lock); + priv->op_flags &= ~OP_SCANNING; + spin_unlock_bh(&priv->beacon_lock); + priv->op_flags |= OP_FULL_RESET; + ath_start_ani(priv); + mutex_unlock(&priv->mutex); +} + +static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + return 0; +} + +static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw, + u8 coverage_class) +{ + struct ath9k_htc_priv *priv = hw->priv; + + mutex_lock(&priv->mutex); + priv->ah->coverage_class = coverage_class; + ath9k_hw_init_global_settings(priv->ah); + mutex_unlock(&priv->mutex); +} + +struct ieee80211_ops ath9k_htc_ops = { + .tx = ath9k_htc_tx, + .start = ath9k_htc_start, + .stop = ath9k_htc_stop, + .add_interface = ath9k_htc_add_interface, + .remove_interface = ath9k_htc_remove_interface, + .config = ath9k_htc_config, + .configure_filter = ath9k_htc_configure_filter, + .sta_notify = ath9k_htc_sta_notify, + .conf_tx = ath9k_htc_conf_tx, + .bss_info_changed = ath9k_htc_bss_info_changed, + .set_key = ath9k_htc_set_key, + .get_tsf = ath9k_htc_get_tsf, + .set_tsf = ath9k_htc_set_tsf, + .reset_tsf = ath9k_htc_reset_tsf, + .ampdu_action = ath9k_htc_ampdu_action, + .sw_scan_start = ath9k_htc_sw_scan_start, + .sw_scan_complete = ath9k_htc_sw_scan_complete, + .set_rts_threshold = ath9k_htc_set_rts_threshold, + .rfkill_poll = ath9k_htc_rfkill_poll_state, + .set_coverage_class = ath9k_htc_set_coverage_class, +}; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c new file mode 100644 index 000000000000..ac66cf0b2d53 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -0,0 +1,604 @@ +/* + * Copyright (c) 2010 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 "htc.h" + +/******/ +/* TX */ +/******/ + +int get_hw_qnum(u16 queue, int *hwq_map) +{ + switch (queue) { + case 0: + return hwq_map[ATH9K_WME_AC_VO]; + case 1: + return hwq_map[ATH9K_WME_AC_VI]; + case 2: + return hwq_map[ATH9K_WME_AC_BE]; + case 3: + return hwq_map[ATH9K_WME_AC_BK]; + default: + return hwq_map[ATH9K_WME_AC_BE]; + } +} + +int ath_htc_txq_update(struct ath9k_htc_priv *priv, int qnum, + struct ath9k_tx_queue_info *qinfo) +{ + struct ath_hw *ah = priv->ah; + int error = 0; + struct ath9k_tx_queue_info qi; + + ath9k_hw_get_txq_props(ah, qnum, &qi); + + qi.tqi_aifs = qinfo->tqi_aifs; + qi.tqi_cwmin = qinfo->tqi_cwmin / 2; /* XXX */ + qi.tqi_cwmax = qinfo->tqi_cwmax; + qi.tqi_burstTime = qinfo->tqi_burstTime; + qi.tqi_readyTime = qinfo->tqi_readyTime; + + if (!ath9k_hw_set_txq_props(ah, qnum, &qi)) { + ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, + "Unable to update hardware queue %u!\n", qnum); + error = -EIO; + } else { + ath9k_hw_resettxqueue(ah, qnum); + } + + return error; +} + +int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta = tx_info->control.sta; + struct ath9k_htc_sta *ista; + struct ath9k_htc_vif *avp; + struct ath9k_htc_tx_ctl tx_ctl; + enum htc_endpoint_id epid; + u16 qnum, hw_qnum; + __le16 fc; + u8 *tx_fhdr; + u8 sta_idx; + + hdr = (struct ieee80211_hdr *) skb->data; + fc = hdr->frame_control; + + avp = (struct ath9k_htc_vif *) tx_info->control.vif->drv_priv; + if (sta) { + ista = (struct ath9k_htc_sta *) sta->drv_priv; + sta_idx = ista->index; + } else { + sta_idx = 0; + } + + memset(&tx_ctl, 0, sizeof(struct ath9k_htc_tx_ctl)); + + if (ieee80211_is_data(fc)) { + struct tx_frame_hdr tx_hdr; + u8 *qc; + + memset(&tx_hdr, 0, sizeof(struct tx_frame_hdr)); + + tx_hdr.node_idx = sta_idx; + tx_hdr.vif_idx = avp->index; + + if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { + tx_ctl.type = ATH9K_HTC_AMPDU; + tx_hdr.data_type = ATH9K_HTC_AMPDU; + } else { + tx_ctl.type = ATH9K_HTC_NORMAL; + tx_hdr.data_type = ATH9K_HTC_NORMAL; + } + + if (ieee80211_is_data(fc)) { + qc = ieee80211_get_qos_ctl(hdr); + tx_hdr.tidno = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + } + + /* Check for RTS protection */ + if (priv->hw->wiphy->rts_threshold != (u32) -1) + if (skb->len > priv->hw->wiphy->rts_threshold) + tx_hdr.flags |= ATH9K_HTC_TX_RTSCTS; + + /* CTS-to-self */ + if (!(tx_hdr.flags & ATH9K_HTC_TX_RTSCTS) && + (priv->op_flags & OP_PROTECT_ENABLE)) + tx_hdr.flags |= ATH9K_HTC_TX_CTSONLY; + + tx_hdr.key_type = ath9k_cmn_get_hw_crypto_keytype(skb); + if (tx_hdr.key_type == ATH9K_KEY_TYPE_CLEAR) + tx_hdr.keyix = (u8) ATH9K_TXKEYIX_INVALID; + else + tx_hdr.keyix = tx_info->control.hw_key->hw_key_idx; + + tx_fhdr = skb_push(skb, sizeof(tx_hdr)); + memcpy(tx_fhdr, (u8 *) &tx_hdr, sizeof(tx_hdr)); + + qnum = skb_get_queue_mapping(skb); + hw_qnum = get_hw_qnum(qnum, priv->hwq_map); + + switch (hw_qnum) { + case 0: + epid = priv->data_be_ep; + break; + case 2: + epid = priv->data_vi_ep; + break; + case 3: + epid = priv->data_vo_ep; + break; + case 1: + default: + epid = priv->data_bk_ep; + break; + } + } else { + struct tx_mgmt_hdr mgmt_hdr; + + memset(&mgmt_hdr, 0, sizeof(struct tx_mgmt_hdr)); + + tx_ctl.type = ATH9K_HTC_NORMAL; + + mgmt_hdr.node_idx = sta_idx; + mgmt_hdr.vif_idx = avp->index; + mgmt_hdr.tidno = 0; + mgmt_hdr.flags = 0; + + mgmt_hdr.key_type = ath9k_cmn_get_hw_crypto_keytype(skb); + if (mgmt_hdr.key_type == ATH9K_KEY_TYPE_CLEAR) + mgmt_hdr.keyix = (u8) ATH9K_TXKEYIX_INVALID; + else + mgmt_hdr.keyix = tx_info->control.hw_key->hw_key_idx; + + tx_fhdr = skb_push(skb, sizeof(mgmt_hdr)); + memcpy(tx_fhdr, (u8 *) &mgmt_hdr, sizeof(mgmt_hdr)); + epid = priv->mgmt_ep; + } + + return htc_send(priv->htc, skb, epid, &tx_ctl); +} + +void ath9k_tx_tasklet(unsigned long data) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + struct ieee80211_tx_info *tx_info; + struct sk_buff *skb = NULL; + __le16 fc; + + while ((skb = skb_dequeue(&priv->tx_queue)) != NULL) { + + hdr = (struct ieee80211_hdr *) skb->data; + fc = hdr->frame_control; + tx_info = IEEE80211_SKB_CB(skb); + sta = tx_info->control.sta; + + rcu_read_lock(); + + if (sta && conf_is_ht(&priv->hw->conf) && + (priv->op_flags & OP_TXAGGR) + && !(skb->protocol == cpu_to_be16(ETH_P_PAE))) { + if (ieee80211_is_data_qos(fc)) { + u8 *qc, tid; + struct ath9k_htc_sta *ista; + + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + ista = (struct ath9k_htc_sta *)sta->drv_priv; + + if ((tid < ATH9K_HTC_MAX_TID) && + ista->tid_state[tid] == AGGR_STOP) { + ieee80211_start_tx_ba_session(sta, tid); + ista->tid_state[tid] = AGGR_PROGRESS; + } + } + } + + rcu_read_unlock(); + + memset(&tx_info->status, 0, sizeof(tx_info->status)); + ieee80211_tx_status(priv->hw, skb); + } +} + +void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb, + enum htc_endpoint_id ep_id, bool txok) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) drv_priv; + struct ieee80211_tx_info *tx_info; + + if (!skb) + return; + + if (ep_id == priv->mgmt_ep) + skb_pull(skb, sizeof(struct tx_mgmt_hdr)); + else + /* TODO: Check for cab/uapsd/data */ + skb_pull(skb, sizeof(struct tx_frame_hdr)); + + tx_info = IEEE80211_SKB_CB(skb); + + if (txok) + tx_info->flags |= IEEE80211_TX_STAT_ACK; + + skb_queue_tail(&priv->tx_queue, skb); + tasklet_schedule(&priv->tx_tasklet); +} + +int ath9k_tx_init(struct ath9k_htc_priv *priv) +{ + skb_queue_head_init(&priv->tx_queue); + return 0; +} + +void ath9k_tx_cleanup(struct ath9k_htc_priv *priv) +{ + +} + +bool ath9k_htc_txq_setup(struct ath9k_htc_priv *priv, + enum ath9k_tx_queue_subtype subtype) +{ + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_tx_queue_info qi; + int qnum; + + memset(&qi, 0, sizeof(qi)); + + qi.tqi_subtype = subtype; + qi.tqi_aifs = ATH9K_TXQ_USEDEFAULT; + qi.tqi_cwmin = ATH9K_TXQ_USEDEFAULT; + qi.tqi_cwmax = ATH9K_TXQ_USEDEFAULT; + qi.tqi_physCompBuf = 0; + qi.tqi_qflags = TXQ_FLAG_TXEOLINT_ENABLE | TXQ_FLAG_TXDESCINT_ENABLE; + + qnum = ath9k_hw_setuptxqueue(priv->ah, ATH9K_TX_QUEUE_DATA, &qi); + if (qnum == -1) + return false; + + if (qnum >= ARRAY_SIZE(priv->hwq_map)) { + ath_print(common, ATH_DBG_FATAL, + "qnum %u out of range, max %u!\n", + qnum, (unsigned int)ARRAY_SIZE(priv->hwq_map)); + ath9k_hw_releasetxqueue(ah, qnum); + return false; + } + + priv->hwq_map[subtype] = qnum; + return true; +} + +/******/ +/* RX */ +/******/ + +void ath9k_host_rx_init(struct ath9k_htc_priv *priv) +{ + ath9k_hw_rxena(priv->ah); + ath9k_cmn_opmode_init(priv->hw, priv->ah, priv->rxfilter); + ath9k_hw_startpcureceive(priv->ah); + priv->rx.last_rssi = ATH_RSSI_DUMMY_MARKER; +} + +static void ath9k_process_rate(struct ieee80211_hw *hw, + struct ieee80211_rx_status *rxs, + u8 rx_rate, u8 rs_flags) +{ + struct ieee80211_supported_band *sband; + enum ieee80211_band band; + unsigned int i = 0; + + if (rx_rate & 0x80) { + /* HT rate */ + rxs->flag |= RX_FLAG_HT; + if (rs_flags & ATH9K_RX_2040) + rxs->flag |= RX_FLAG_40MHZ; + if (rs_flags & ATH9K_RX_GI) + rxs->flag |= RX_FLAG_SHORT_GI; + rxs->rate_idx = rx_rate & 0x7f; + return; + } + + band = hw->conf.channel->band; + sband = hw->wiphy->bands[band]; + + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].hw_value == rx_rate) { + rxs->rate_idx = i; + return; + } + if (sband->bitrates[i].hw_value_short == rx_rate) { + rxs->rate_idx = i; + rxs->flag |= RX_FLAG_SHORTPRE; + return; + } + } + +} + +static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, + struct ath9k_htc_rxbuf *rxbuf, + struct ieee80211_rx_status *rx_status) + +{ + struct ieee80211_hdr *hdr; + struct ieee80211_hw *hw = priv->hw; + struct sk_buff *skb = rxbuf->skb; + struct ath_common *common = ath9k_hw_common(priv->ah); + int hdrlen, padpos, padsize; + int last_rssi = ATH_RSSI_DUMMY_MARKER; + __le16 fc; + + hdr = (struct ieee80211_hdr *)skb->data; + fc = hdr->frame_control; + hdrlen = ieee80211_get_hdrlen_from_skb(skb); + + padpos = ath9k_cmn_padpos(fc); + + padsize = padpos & 3; + if (padsize && skb->len >= padpos+padsize) { + memmove(skb->data + padsize, skb->data, padpos); + skb_pull(skb, padsize); + } + + memset(rx_status, 0, sizeof(struct ieee80211_rx_status)); + + if (rxbuf->rxstatus.rs_status != 0) { + if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_CRC) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_PHY) + goto rx_next; + + if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT) { + /* FIXME */ + } else if (rxbuf->rxstatus.rs_status & ATH9K_RXERR_MIC) { + if (ieee80211_is_ctl(fc)) + /* + * Sometimes, we get invalid + * MIC failures on valid control frames. + * Remove these mic errors. + */ + rxbuf->rxstatus.rs_status &= ~ATH9K_RXERR_MIC; + else + rx_status->flag |= RX_FLAG_MMIC_ERROR; + } + + /* + * Reject error frames with the exception of + * decryption and MIC failures. For monitor mode, + * we also ignore the CRC error. + */ + if (priv->ah->opmode == NL80211_IFTYPE_MONITOR) { + if (rxbuf->rxstatus.rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC | + ATH9K_RXERR_CRC)) + goto rx_next; + } else { + if (rxbuf->rxstatus.rs_status & + ~(ATH9K_RXERR_DECRYPT | ATH9K_RXERR_MIC)) { + goto rx_next; + } + } + } + + if (!(rxbuf->rxstatus.rs_status & ATH9K_RXERR_DECRYPT)) { + u8 keyix; + keyix = rxbuf->rxstatus.rs_keyix; + if (keyix != ATH9K_RXKEYIX_INVALID) { + rx_status->flag |= RX_FLAG_DECRYPTED; + } else if (ieee80211_has_protected(fc) && + skb->len >= hdrlen + 4) { + keyix = skb->data[hdrlen + 3] >> 6; + if (test_bit(keyix, common->keymap)) + rx_status->flag |= RX_FLAG_DECRYPTED; + } + } + + ath9k_process_rate(hw, rx_status, rxbuf->rxstatus.rs_rate, + rxbuf->rxstatus.rs_flags); + + if (priv->op_flags & OP_ASSOCIATED) { + if (rxbuf->rxstatus.rs_rssi != ATH9K_RSSI_BAD && + !rxbuf->rxstatus.rs_moreaggr) + ATH_RSSI_LPF(priv->rx.last_rssi, + rxbuf->rxstatus.rs_rssi); + + last_rssi = priv->rx.last_rssi; + + if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) + rxbuf->rxstatus.rs_rssi = ATH_EP_RND(last_rssi, + ATH_RSSI_EP_MULTIPLIER); + + if (rxbuf->rxstatus.rs_rssi < 0) + rxbuf->rxstatus.rs_rssi = 0; + + if (ieee80211_is_beacon(fc)) + priv->ah->stats.avgbrssi = rxbuf->rxstatus.rs_rssi; + } + + rx_status->mactime = rxbuf->rxstatus.rs_tstamp; + rx_status->band = hw->conf.channel->band; + rx_status->freq = hw->conf.channel->center_freq; + rx_status->signal = rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR; + rx_status->antenna = rxbuf->rxstatus.rs_antenna; + rx_status->flag |= RX_FLAG_TSFT; + + return true; + +rx_next: + return false; +} + +/* + * FIXME: Handle FLUSH later on. + */ +void ath9k_rx_tasklet(unsigned long data) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; + struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL; + struct ieee80211_rx_status rx_status; + struct sk_buff *skb; + unsigned long flags; + + + do { + spin_lock_irqsave(&priv->rx.rxbuflock, flags); + list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) { + if (tmp_buf->in_process) { + rxbuf = tmp_buf; + break; + } + } + + if (rxbuf == NULL) { + spin_unlock_irqrestore(&priv->rx.rxbuflock, flags); + break; + } + + if (!rxbuf->skb) + goto requeue; + + if (!ath9k_rx_prepare(priv, rxbuf, &rx_status)) { + dev_kfree_skb_any(rxbuf->skb); + goto requeue; + } + + memcpy(IEEE80211_SKB_RXCB(rxbuf->skb), &rx_status, + sizeof(struct ieee80211_rx_status)); + skb = rxbuf->skb; + spin_unlock_irqrestore(&priv->rx.rxbuflock, flags); + + ieee80211_rx(priv->hw, skb); + + spin_lock_irqsave(&priv->rx.rxbuflock, flags); +requeue: + rxbuf->in_process = false; + rxbuf->skb = NULL; + list_move_tail(&rxbuf->list, &priv->rx.rxbuf); + rxbuf = NULL; + spin_unlock_irqrestore(&priv->rx.rxbuflock, flags); + } while (1); + +} + +void ath9k_htc_rxep(void *drv_priv, struct sk_buff *skb, + enum htc_endpoint_id ep_id) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)drv_priv; + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_rxbuf *rxbuf = NULL, *tmp_buf = NULL; + struct ath_htc_rx_status *rxstatus; + u32 len = 0; + + spin_lock(&priv->rx.rxbuflock); + list_for_each_entry(tmp_buf, &priv->rx.rxbuf, list) { + if (!tmp_buf->in_process) { + rxbuf = tmp_buf; + break; + } + } + spin_unlock(&priv->rx.rxbuflock); + + if (rxbuf == NULL) { + ath_print(common, ATH_DBG_ANY, + "No free RX buffer\n"); + goto err; + } + + len = skb->len; + if (len <= HTC_RX_FRAME_HEADER_SIZE) { + ath_print(common, ATH_DBG_FATAL, + "Corrupted RX frame, dropping\n"); + goto err; + } + + rxstatus = (struct ath_htc_rx_status *)skb->data; + + rxstatus->rs_tstamp = be64_to_cpu(rxstatus->rs_tstamp); + rxstatus->rs_datalen = be16_to_cpu(rxstatus->rs_datalen); + rxstatus->evm0 = be32_to_cpu(rxstatus->evm0); + rxstatus->evm1 = be32_to_cpu(rxstatus->evm1); + rxstatus->evm2 = be32_to_cpu(rxstatus->evm2); + + if (rxstatus->rs_datalen - (len - HTC_RX_FRAME_HEADER_SIZE) != 0) { + ath_print(common, ATH_DBG_FATAL, + "Corrupted RX data len, dropping " + "(epid: %d, dlen: %d, skblen: %d)\n", + ep_id, rxstatus->rs_datalen, len); + goto err; + } + + spin_lock(&priv->rx.rxbuflock); + memcpy(&rxbuf->rxstatus, rxstatus, HTC_RX_FRAME_HEADER_SIZE); + skb_pull(skb, HTC_RX_FRAME_HEADER_SIZE); + skb->len = rxstatus->rs_datalen; + rxbuf->skb = skb; + rxbuf->in_process = true; + spin_unlock(&priv->rx.rxbuflock); + + tasklet_schedule(&priv->rx_tasklet); + return; +err: + dev_kfree_skb_any(skb); + return; +} + +/* FIXME: Locking for cleanup/init */ + +void ath9k_rx_cleanup(struct ath9k_htc_priv *priv) +{ + struct ath9k_htc_rxbuf *rxbuf, *tbuf; + + list_for_each_entry_safe(rxbuf, tbuf, &priv->rx.rxbuf, list) { + list_del(&rxbuf->list); + if (rxbuf->skb) + dev_kfree_skb_any(rxbuf->skb); + kfree(rxbuf); + } +} + +int ath9k_rx_init(struct ath9k_htc_priv *priv) +{ + struct ath_hw *ah = priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + struct ath9k_htc_rxbuf *rxbuf; + int i = 0; + + INIT_LIST_HEAD(&priv->rx.rxbuf); + spin_lock_init(&priv->rx.rxbuflock); + + for (i = 0; i < ATH9K_HTC_RXBUF; i++) { + rxbuf = kzalloc(sizeof(struct ath9k_htc_rxbuf), GFP_KERNEL); + if (rxbuf == NULL) { + ath_print(common, ATH_DBG_FATAL, + "Unable to allocate RX buffers\n"); + goto err; + } + list_add_tail(&rxbuf->list, &priv->rx.rxbuf); + } + + return 0; + +err: + ath9k_rx_cleanup(priv); + return -ENOMEM; +} diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c new file mode 100644 index 000000000000..9a48999d0979 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -0,0 +1,463 @@ +/* + * Copyright (c) 2010 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 "htc.h" + +static int htc_issue_send(struct htc_target *target, struct sk_buff* skb, + u16 len, u8 flags, u8 epid, + struct ath9k_htc_tx_ctl *tx_ctl) +{ + struct htc_frame_hdr *hdr; + struct htc_endpoint *endpoint = &target->endpoint[epid]; + int status; + + hdr = (struct htc_frame_hdr *) + skb_push(skb, sizeof(struct htc_frame_hdr)); + hdr->endpoint_id = epid; + hdr->flags = flags; + hdr->payload_len = cpu_to_be16(len); + + status = target->hif->send(target->hif_dev, endpoint->ul_pipeid, skb, + tx_ctl); + return status; +} + +static struct htc_endpoint *get_next_avail_ep(struct htc_endpoint *endpoint) +{ + enum htc_endpoint_id avail_epid; + + for (avail_epid = ENDPOINT_MAX; avail_epid > ENDPOINT0; avail_epid--) + if (endpoint[avail_epid].service_id == 0) + return &endpoint[avail_epid]; + return NULL; +} + +static u8 service_to_ulpipe(u16 service_id) +{ + switch (service_id) { + case WMI_CONTROL_SVC: + return 4; + case WMI_BEACON_SVC: + case WMI_CAB_SVC: + case WMI_UAPSD_SVC: + case WMI_MGMT_SVC: + case WMI_DATA_VO_SVC: + case WMI_DATA_VI_SVC: + case WMI_DATA_BE_SVC: + case WMI_DATA_BK_SVC: + return 1; + default: + return 0; + } +} + +static u8 service_to_dlpipe(u16 service_id) +{ + switch (service_id) { + case WMI_CONTROL_SVC: + return 3; + case WMI_BEACON_SVC: + case WMI_CAB_SVC: + case WMI_UAPSD_SVC: + case WMI_MGMT_SVC: + case WMI_DATA_VO_SVC: + case WMI_DATA_VI_SVC: + case WMI_DATA_BE_SVC: + case WMI_DATA_BK_SVC: + return 2; + default: + return 0; + } +} + +static void htc_process_target_rdy(struct htc_target *target, + void *buf) +{ + struct htc_endpoint *endpoint; + struct htc_ready_msg *htc_ready_msg = (struct htc_ready_msg *) buf; + + target->credits = be16_to_cpu(htc_ready_msg->credits); + target->credit_size = be16_to_cpu(htc_ready_msg->credit_size); + + endpoint = &target->endpoint[ENDPOINT0]; + endpoint->service_id = HTC_CTRL_RSVD_SVC; + endpoint->max_msglen = HTC_MAX_CONTROL_MESSAGE_LENGTH; + complete(&target->target_wait); +} + +static void htc_process_conn_rsp(struct htc_target *target, + struct htc_frame_hdr *htc_hdr) +{ + struct htc_conn_svc_rspmsg *svc_rspmsg; + struct htc_endpoint *endpoint, *tmp_endpoint = NULL; + u16 service_id; + u16 max_msglen; + enum htc_endpoint_id epid, tepid; + + svc_rspmsg = (struct htc_conn_svc_rspmsg *) + ((void *) htc_hdr + sizeof(struct htc_frame_hdr)); + + if (svc_rspmsg->status == HTC_SERVICE_SUCCESS) { + epid = svc_rspmsg->endpoint_id; + service_id = be16_to_cpu(svc_rspmsg->service_id); + max_msglen = be16_to_cpu(svc_rspmsg->max_msg_len); + endpoint = &target->endpoint[epid]; + + for (tepid = ENDPOINT_MAX; tepid > ENDPOINT0; tepid--) { + tmp_endpoint = &target->endpoint[tepid]; + if (tmp_endpoint->service_id == service_id) { + tmp_endpoint->service_id = 0; + break; + } + } + + if (!tmp_endpoint) + return; + + endpoint->service_id = service_id; + endpoint->max_txqdepth = tmp_endpoint->max_txqdepth; + endpoint->ep_callbacks = tmp_endpoint->ep_callbacks; + endpoint->ul_pipeid = tmp_endpoint->ul_pipeid; + endpoint->dl_pipeid = tmp_endpoint->dl_pipeid; + endpoint->max_msglen = max_msglen; + target->conn_rsp_epid = epid; + complete(&target->cmd_wait); + } else { + target->conn_rsp_epid = ENDPOINT_UNUSED; + } +} + +static int htc_config_pipe_credits(struct htc_target *target) +{ + struct sk_buff *skb; + struct htc_config_pipe_msg *cp_msg; + int ret, time_left; + + skb = dev_alloc_skb(50 + sizeof(struct htc_frame_hdr)); + if (!skb) { + dev_err(target->dev, "failed to allocate send buffer\n"); + return -ENOMEM; + } + skb_reserve(skb, sizeof(struct htc_frame_hdr)); + + cp_msg = (struct htc_config_pipe_msg *) + skb_put(skb, sizeof(struct htc_config_pipe_msg)); + + cp_msg->message_id = cpu_to_be16(HTC_MSG_CONFIG_PIPE_ID); + cp_msg->pipe_id = USB_WLAN_TX_PIPE; + cp_msg->credits = 28; + + target->htc_flags |= HTC_OP_CONFIG_PIPE_CREDITS; + + ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0, NULL); + if (ret) + goto err; + + time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); + if (!time_left) { + dev_err(target->dev, "HTC credit config timeout\n"); + return -ETIMEDOUT; + } + + return 0; +err: + dev_kfree_skb(skb); + return -EINVAL; +} + +static int htc_setup_complete(struct htc_target *target) +{ + struct sk_buff *skb; + struct htc_comp_msg *comp_msg; + int ret = 0, time_left; + + skb = dev_alloc_skb(50 + sizeof(struct htc_frame_hdr)); + if (!skb) { + dev_err(target->dev, "failed to allocate send buffer\n"); + return -ENOMEM; + } + skb_reserve(skb, sizeof(struct htc_frame_hdr)); + + comp_msg = (struct htc_comp_msg *) + skb_put(skb, sizeof(struct htc_comp_msg)); + comp_msg->msg_id = cpu_to_be16(HTC_MSG_SETUP_COMPLETE_ID); + + target->htc_flags |= HTC_OP_START_WAIT; + + ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0, NULL); + if (ret) + goto err; + + time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); + if (!time_left) { + dev_err(target->dev, "HTC start timeout\n"); + return -ETIMEDOUT; + } + + return 0; + +err: + dev_kfree_skb(skb); + return -EINVAL; +} + +/* HTC APIs */ + +int htc_init(struct htc_target *target) +{ + int ret; + + ret = htc_config_pipe_credits(target); + if (ret) + return ret; + + return htc_setup_complete(target); +} + +int htc_connect_service(struct htc_target *target, + struct htc_service_connreq *service_connreq, + enum htc_endpoint_id *conn_rsp_epid) +{ + struct sk_buff *skb; + struct htc_endpoint *endpoint; + struct htc_conn_svc_msg *conn_msg; + int ret, time_left; + + /* Find an available endpoint */ + endpoint = get_next_avail_ep(target->endpoint); + if (!endpoint) { + dev_err(target->dev, "Endpoint is not available for" + "service %d\n", service_connreq->service_id); + return -EINVAL; + } + + endpoint->service_id = service_connreq->service_id; + endpoint->max_txqdepth = service_connreq->max_send_qdepth; + endpoint->ul_pipeid = service_to_ulpipe(service_connreq->service_id); + endpoint->dl_pipeid = service_to_dlpipe(service_connreq->service_id); + endpoint->ep_callbacks = service_connreq->ep_callbacks; + + skb = dev_alloc_skb(sizeof(struct htc_conn_svc_msg) + + sizeof(struct htc_frame_hdr)); + if (!skb) { + dev_err(target->dev, "Failed to allocate buf to send" + "service connect req\n"); + return -ENOMEM; + } + + skb_reserve(skb, sizeof(struct htc_frame_hdr)); + + conn_msg = (struct htc_conn_svc_msg *) + skb_put(skb, sizeof(struct htc_conn_svc_msg)); + conn_msg->service_id = cpu_to_be16(service_connreq->service_id); + conn_msg->msg_id = cpu_to_be16(HTC_MSG_CONNECT_SERVICE_ID); + conn_msg->con_flags = cpu_to_be16(service_connreq->con_flags); + conn_msg->dl_pipeid = endpoint->dl_pipeid; + conn_msg->ul_pipeid = endpoint->ul_pipeid; + + ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0, NULL); + if (ret) + goto err; + + time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); + if (!time_left) { + dev_err(target->dev, "Service connection timeout for: %d\n", + service_connreq->service_id); + return -ETIMEDOUT; + } + + *conn_rsp_epid = target->conn_rsp_epid; + return 0; +err: + dev_kfree_skb(skb); + return ret; +} + +int htc_send(struct htc_target *target, struct sk_buff *skb, + enum htc_endpoint_id epid, struct ath9k_htc_tx_ctl *tx_ctl) +{ + return htc_issue_send(target, skb, skb->len, 0, epid, tx_ctl); +} + +void htc_stop(struct htc_target *target) +{ + enum htc_endpoint_id epid; + struct htc_endpoint *endpoint; + + for (epid = ENDPOINT0; epid <= ENDPOINT_MAX; epid++) { + endpoint = &target->endpoint[epid]; + if (endpoint->service_id != 0) + target->hif->stop(target->hif_dev, endpoint->ul_pipeid); + } +} + +void htc_start(struct htc_target *target) +{ + enum htc_endpoint_id epid; + struct htc_endpoint *endpoint; + + for (epid = ENDPOINT0; epid <= ENDPOINT_MAX; epid++) { + endpoint = &target->endpoint[epid]; + if (endpoint->service_id != 0) + target->hif->start(target->hif_dev, + endpoint->ul_pipeid); + } +} + +void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, + struct sk_buff *skb, bool txok) +{ + struct htc_endpoint *endpoint; + struct htc_frame_hdr *htc_hdr; + + if (htc_handle->htc_flags & HTC_OP_CONFIG_PIPE_CREDITS) { + complete(&htc_handle->cmd_wait); + htc_handle->htc_flags &= ~HTC_OP_CONFIG_PIPE_CREDITS; + } + + if (htc_handle->htc_flags & HTC_OP_START_WAIT) { + complete(&htc_handle->cmd_wait); + htc_handle->htc_flags &= ~HTC_OP_START_WAIT; + } + + if (skb) { + htc_hdr = (struct htc_frame_hdr *) skb->data; + endpoint = &htc_handle->endpoint[htc_hdr->endpoint_id]; + skb_pull(skb, sizeof(struct htc_frame_hdr)); + + if (endpoint->ep_callbacks.tx) { + endpoint->ep_callbacks.tx(htc_handle->drv_priv, skb, + htc_hdr->endpoint_id, txok); + } + } +} + +/* + * HTC Messages are handled directly here and the obtained SKB + * is freed. + * + * Sevice messages (Data, WMI) passed to the corresponding + * endpoint RX handlers, which have to free the SKB. + */ +void ath9k_htc_rx_msg(struct htc_target *htc_handle, + struct sk_buff *skb, u32 len, u8 pipe_id) +{ + struct htc_frame_hdr *htc_hdr; + enum htc_endpoint_id epid; + struct htc_endpoint *endpoint; + u16 *msg_id; + + if (!htc_handle || !skb) + return; + + htc_hdr = (struct htc_frame_hdr *) skb->data; + epid = htc_hdr->endpoint_id; + + if (epid >= ENDPOINT_MAX) { + dev_kfree_skb_any(skb); + return; + } + + if (epid == ENDPOINT0) { + + /* Handle trailer */ + if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) { + if (be32_to_cpu(*(u32 *) skb->data) == 0x00C60000) + /* Move past the Watchdog pattern */ + htc_hdr = (struct htc_frame_hdr *) skb->data + 4; + } + + /* Get the message ID */ + msg_id = (u16 *) ((void *) htc_hdr + + sizeof(struct htc_frame_hdr)); + + /* Now process HTC messages */ + switch (be16_to_cpu(*msg_id)) { + case HTC_MSG_READY_ID: + htc_process_target_rdy(htc_handle, htc_hdr); + break; + case HTC_MSG_CONNECT_SERVICE_RESPONSE_ID: + htc_process_conn_rsp(htc_handle, htc_hdr); + break; + default: + break; + } + + dev_kfree_skb_any(skb); + + } else { + if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) + skb_trim(skb, len - htc_hdr->control[0]); + + skb_pull(skb, sizeof(struct htc_frame_hdr)); + + endpoint = &htc_handle->endpoint[epid]; + if (endpoint->ep_callbacks.rx) + endpoint->ep_callbacks.rx(endpoint->ep_callbacks.priv, + skb, epid); + } +} + +struct htc_target *ath9k_htc_hw_alloc(void *hif_handle) +{ + struct htc_target *target; + + target = kzalloc(sizeof(struct htc_target), GFP_KERNEL); + if (!target) + printk(KERN_ERR "Unable to allocate memory for" + "target device\n"); + + return target; +} + +void ath9k_htc_hw_free(struct htc_target *htc) +{ + kfree(htc); +} + +int ath9k_htc_hw_init(struct ath9k_htc_hif *hif, struct htc_target *target, + void *hif_handle, struct device *dev, u16 devid, + enum ath9k_hif_transports transport) +{ + struct htc_endpoint *endpoint; + int err = 0; + + init_completion(&target->target_wait); + init_completion(&target->cmd_wait); + + target->hif = hif; + target->hif_dev = hif_handle; + target->dev = dev; + + /* Assign control endpoint pipe IDs */ + endpoint = &target->endpoint[ENDPOINT0]; + endpoint->ul_pipeid = hif->control_ul_pipe; + endpoint->dl_pipeid = hif->control_dl_pipe; + + err = ath9k_htc_probe_device(target, dev, devid); + if (err) { + printk(KERN_ERR "Failed to initialize the device\n"); + return -ENODEV; + } + + return 0; +} + +void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug) +{ + if (target) + ath9k_htc_disconnect_device(target, hot_unplug); +} diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.h b/drivers/net/wireless/ath/ath9k/htc_hst.h new file mode 100644 index 000000000000..cd7048ffd239 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/htc_hst.h @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2010 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_HST_H +#define HTC_HST_H + +struct ath9k_htc_priv; +struct htc_target; +struct ath9k_htc_tx_ctl; + +enum ath9k_hif_transports { + ATH9K_HIF_USB, +}; + +struct ath9k_htc_hif { + struct list_head list; + const enum ath9k_hif_transports transport; + const char *name; + + u8 control_dl_pipe; + u8 control_ul_pipe; + + void (*start) (void *hif_handle, u8 pipe); + void (*stop) (void *hif_handle, u8 pipe); + int (*send) (void *hif_handle, u8 pipe, struct sk_buff *buf, + struct ath9k_htc_tx_ctl *tx_ctl); +}; + +enum htc_endpoint_id { + ENDPOINT_UNUSED = -1, + ENDPOINT0 = 0, + ENDPOINT1 = 1, + ENDPOINT2 = 2, + ENDPOINT3 = 3, + ENDPOINT4 = 4, + ENDPOINT5 = 5, + ENDPOINT6 = 6, + ENDPOINT7 = 7, + ENDPOINT8 = 8, + ENDPOINT_MAX = 22 +}; + +/* Htc frame hdr flags */ +#define HTC_FLAGS_RECV_TRAILER (1 << 1) + +struct htc_frame_hdr { + u8 endpoint_id; + u8 flags; + u16 payload_len; + u8 control[4]; +} __packed; + +struct htc_ready_msg { + u16 message_id; + u16 credits; + u16 credit_size; + u8 max_endpoints; + u8 pad; +} __packed; + +struct htc_config_pipe_msg { + u16 message_id; + u8 pipe_id; + u8 credits; +} __packed; + +struct htc_packet { + void *pktcontext; + u8 *buf; + u8 *buf_payload; + u32 buflen; + u32 payload_len; + + int endpoint; + int status; + + void *context; + u32 reserved; +}; + +struct htc_ep_callbacks { + void *priv; + void (*tx) (void *, struct sk_buff *, enum htc_endpoint_id, bool txok); + void (*rx) (void *, struct sk_buff *, enum htc_endpoint_id); +}; + +#define HTC_TX_QUEUE_SIZE 256 + +struct htc_txq { + struct sk_buff *buf[HTC_TX_QUEUE_SIZE]; + u32 txqdepth; + u16 txbuf_cnt; + u16 txq_head; + u16 txq_tail; +}; + +struct htc_endpoint { + u16 service_id; + + struct htc_ep_callbacks ep_callbacks; + struct htc_txq htc_txq; + u32 max_txqdepth; + int max_msglen; + + u8 ul_pipeid; + u8 dl_pipeid; +}; + +#define HTC_MAX_CONTROL_MESSAGE_LENGTH 255 +#define HTC_CONTROL_BUFFER_SIZE \ + (HTC_MAX_CONTROL_MESSAGE_LENGTH + sizeof(struct htc_frame_hdr)) + +#define NUM_CONTROL_BUFFERS 8 +#define HST_ENDPOINT_MAX 8 + +struct htc_control_buf { + struct htc_packet htc_pkt; + u8 buf[HTC_CONTROL_BUFFER_SIZE]; +}; + +#define HTC_OP_START_WAIT BIT(0) +#define HTC_OP_CONFIG_PIPE_CREDITS BIT(1) + +struct htc_target { + void *hif_dev; + struct ath9k_htc_priv *drv_priv; + struct device *dev; + struct ath9k_htc_hif *hif; + struct htc_endpoint endpoint[HST_ENDPOINT_MAX]; + struct completion target_wait; + struct completion cmd_wait; + struct list_head list; + enum htc_endpoint_id conn_rsp_epid; + u16 credits; + u16 credit_size; + u8 htc_flags; +}; + +enum htc_msg_id { + HTC_MSG_READY_ID = 1, + HTC_MSG_CONNECT_SERVICE_ID, + HTC_MSG_CONNECT_SERVICE_RESPONSE_ID, + HTC_MSG_SETUP_COMPLETE_ID, + HTC_MSG_CONFIG_PIPE_ID, + HTC_MSG_CONFIG_PIPE_RESPONSE_ID, +}; + +struct htc_service_connreq { + u16 service_id; + u16 con_flags; + u32 max_send_qdepth; + struct htc_ep_callbacks ep_callbacks; +}; + +/* Current service IDs */ + +enum htc_service_group_ids{ + RSVD_SERVICE_GROUP = 0, + WMI_SERVICE_GROUP = 1, + + HTC_SERVICE_GROUP_LAST = 255 +}; + +#define MAKE_SERVICE_ID(group, index) \ + (int)(((int)group << 8) | (int)(index)) + +/* NOTE: service ID of 0x0000 is reserved and should never be used */ +#define HTC_CTRL_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 1) +#define HTC_LOOPBACK_RSVD_SVC MAKE_SERVICE_ID(RSVD_SERVICE_GROUP, 2) + +#define WMI_CONTROL_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 0) +#define WMI_BEACON_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 1) +#define WMI_CAB_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 2) +#define WMI_UAPSD_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 3) +#define WMI_MGMT_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 4) +#define WMI_DATA_VO_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 5) +#define WMI_DATA_VI_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 6) +#define WMI_DATA_BE_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 7) +#define WMI_DATA_BK_SVC MAKE_SERVICE_ID(WMI_SERVICE_GROUP, 8) + +struct htc_conn_svc_msg { + u16 msg_id; + u16 service_id; + u16 con_flags; + u8 dl_pipeid; + u8 ul_pipeid; + u8 svc_meta_len; + u8 pad; +} __packed; + +/* connect response status codes */ +#define HTC_SERVICE_SUCCESS 0 +#define HTC_SERVICE_NOT_FOUND 1 +#define HTC_SERVICE_FAILED 2 +#define HTC_SERVICE_NO_RESOURCES 3 +#define HTC_SERVICE_NO_MORE_EP 4 + +struct htc_conn_svc_rspmsg { + u16 msg_id; + u16 service_id; + u8 status; + u8 endpoint_id; + u16 max_msg_len; + u8 svc_meta_len; + u8 pad; +} __packed; + +struct htc_comp_msg { + u16 msg_id; +} __packed; + +int htc_init(struct htc_target *target); +int htc_connect_service(struct htc_target *target, + struct htc_service_connreq *service_connreq, + enum htc_endpoint_id *conn_rsp_eid); +int htc_send(struct htc_target *target, struct sk_buff *skb, + enum htc_endpoint_id eid, struct ath9k_htc_tx_ctl *tx_ctl); +void htc_stop(struct htc_target *target); +void htc_start(struct htc_target *target); + +void ath9k_htc_rx_msg(struct htc_target *htc_handle, + struct sk_buff *skb, u32 len, u8 pipe_id); +void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, + struct sk_buff *skb, bool txok); + +struct htc_target *ath9k_htc_hw_alloc(void *hif_handle); +void ath9k_htc_hw_free(struct htc_target *htc); +int ath9k_htc_hw_init(struct ath9k_htc_hif *hif, struct htc_target *target, + void *hif_handle, struct device *dev, u16 devid, + enum ath9k_hif_transports transport); +void ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug); + +#endif /* HTC_HST_H */ diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 1fb14edfcb2a..77db932c3137 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -499,8 +499,10 @@ static int ath9k_hw_post_init(struct ath_hw *ah) { int ecode; - if (!ath9k_hw_chip_test(ah)) - return -ENODEV; + if (!AR_SREV_9271(ah)) { + if (!ath9k_hw_chip_test(ah)) + return -ENODEV; + } ecode = ath9k_hw_rf_claim(ah); if (ecode != 0) @@ -603,9 +605,23 @@ static void ath9k_hw_init_mode_regs(struct ath_hw *ah) ARRAY_SIZE(ar9271Modes_9271), 6); INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271, ARRAY_SIZE(ar9271Common_9271), 2); + INIT_INI_ARRAY(&ah->iniCommon_normal_cck_fir_coeff_9271, + ar9271Common_normal_cck_fir_coeff_9271, + ARRAY_SIZE(ar9271Common_normal_cck_fir_coeff_9271), 2); + INIT_INI_ARRAY(&ah->iniCommon_japan_2484_cck_fir_coeff_9271, + ar9271Common_japan_2484_cck_fir_coeff_9271, + ARRAY_SIZE(ar9271Common_japan_2484_cck_fir_coeff_9271), 2); INIT_INI_ARRAY(&ah->iniModes_9271_1_0_only, ar9271Modes_9271_1_0_only, ARRAY_SIZE(ar9271Modes_9271_1_0_only), 6); + INIT_INI_ARRAY(&ah->iniModes_9271_ANI_reg, ar9271Modes_9271_ANI_reg, + ARRAY_SIZE(ar9271Modes_9271_ANI_reg), 6); + INIT_INI_ARRAY(&ah->iniModes_high_power_tx_gain_9271, + ar9271Modes_high_power_tx_gain_9271, + ARRAY_SIZE(ar9271Modes_high_power_tx_gain_9271), 6); + INIT_INI_ARRAY(&ah->iniModes_normal_power_tx_gain_9271, + ar9271Modes_normal_power_tx_gain_9271, + ARRAY_SIZE(ar9271Modes_normal_power_tx_gain_9271), 6); return; } @@ -990,22 +1006,6 @@ static void ath9k_hw_init_qos(struct ath_hw *ah) REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); } -static void ath9k_hw_change_target_baud(struct ath_hw *ah, u32 freq, u32 baud) -{ - u32 lcr; - u32 baud_divider = freq * 1000 * 1000 / 16 / baud; - - lcr = REG_READ(ah , 0x5100c); - lcr |= 0x80; - - REG_WRITE(ah, 0x5100c, lcr); - REG_WRITE(ah, 0x51004, (baud_divider >> 8)); - REG_WRITE(ah, 0x51000, (baud_divider & 0xff)); - - lcr &= ~0x80; - REG_WRITE(ah, 0x5100c, lcr); -} - static void ath9k_hw_init_pll(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -1071,22 +1071,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, /* Switch the core clock for ar9271 to 117Mhz */ if (AR_SREV_9271(ah)) { - if ((pll == 0x142c) || (pll == 0x2850) ) { - udelay(500); - /* set CLKOBS to output AHB clock */ - REG_WRITE(ah, 0x7020, 0xe); - /* - * 0x304: 117Mhz, ahb_ratio: 1x1 - * 0x306: 40Mhz, ahb_ratio: 1x1 - */ - REG_WRITE(ah, 0x50040, 0x304); - /* - * makes adjustments for the baud dividor to keep the - * targetted baud rate based on the used core clock. - */ - ath9k_hw_change_target_baud(ah, AR9271_CORE_CLOCK, - AR9271_TARGET_BAUD_RATE); - } + udelay(500); + REG_WRITE(ah, 0x50040, 0x304); } udelay(RTC_PLL_SETTLE_DELAY); @@ -1241,7 +1227,7 @@ void ath9k_hw_deinit(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); - if (common->state <= ATH_HW_INITIALIZED) + if (common->state < ATH_HW_INITIALIZED) goto free_hw; if (!AR_SREV_9100(ah)) @@ -1252,8 +1238,6 @@ void ath9k_hw_deinit(struct ath_hw *ah) free_hw: if (!AR_SREV_9280_10_OR_LATER(ah)) ath9k_hw_rf_free_ext_banks(ah); - kfree(ah); - ah = NULL; } EXPORT_SYMBOL(ath9k_hw_deinit); @@ -1266,26 +1250,6 @@ static void ath9k_hw_override_ini(struct ath_hw *ah, { u32 val; - if (AR_SREV_9271(ah)) { - /* - * Enable spectral scan to solution for issues with stuck - * beacons on AR9271 1.0. The beacon stuck issue is not seeon on - * AR9271 1.1 - */ - if (AR_SREV_9271_10(ah)) { - val = REG_READ(ah, AR_PHY_SPECTRAL_SCAN) | - AR_PHY_SPECTRAL_SCAN_ENABLE; - REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val); - } - else if (AR_SREV_9271_11(ah)) - /* - * change AR_PHY_RF_CTL3 setting to fix MAC issue - * present on AR9271 1.1 - */ - REG_WRITE(ah, AR_PHY_RF_CTL3, 0x3a020001); - return; - } - /* * Set the RX_ABORT and RX_DIS and clear if off only after * RXE is set for MAC. This prevents frames with corrupted @@ -1294,8 +1258,10 @@ static void ath9k_hw_override_ini(struct ath_hw *ah, REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT)); if (AR_SREV_9280_10_OR_LATER(ah)) { - val = REG_READ(ah, AR_PCU_MISC_MODE2) & - (~AR_PCU_MISC_MODE2_HWWAR1); + val = REG_READ(ah, AR_PCU_MISC_MODE2); + + if (!AR_SREV_9271(ah)) + val &= ~AR_PCU_MISC_MODE2_HWWAR1; if (AR_SREV_9287_10_OR_LATER(ah)) val = val & (~AR_PCU_MISC_MODE2_HWWAR2); @@ -1439,7 +1405,10 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, return -EINVAL; } + /* Set correct baseband to analog shift setting to access analog chips */ REG_WRITE(ah, AR_PHY(0), 0x00000007); + + /* Write ADDAC shifts */ REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO); ah->eep_ops->set_addac(ah, chan); @@ -1451,9 +1420,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, sizeof(u32) * ah->iniAddac.ia_rows * ah->iniAddac.ia_columns; + /* For AR5416 2.0/2.1 */ memcpy(ah->addac5416_21, ah->iniAddac.ia_array, addacSize); + /* override CLKDRV value at [row, column] = [31, 1] */ (ah->addac5416_21)[31 * ah->iniAddac.ia_columns + 1] = 0; temp.ia_array = ah->addac5416_21; @@ -1485,6 +1456,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, AR_SREV_9287_10_OR_LATER(ah)) REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); + if (AR_SREV_9271_10(ah)) + REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only, + modesIndex, regWrites); + + /* Write common array parameters */ for (i = 0; i < ah->iniCommon.ia_rows; i++) { u32 reg = INI_RA(&ah->iniCommon, i, 0); u32 val = INI_RA(&ah->iniCommon, i, 1); @@ -1499,11 +1475,16 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, DO_DELAY(regWrites); } - ath9k_hw_write_regs(ah, freqIndex, regWrites); + if (AR_SREV_9271(ah)) { + if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) == 1) + REG_WRITE_ARRAY(&ah->iniModes_high_power_tx_gain_9271, + modesIndex, regWrites); + else + REG_WRITE_ARRAY(&ah->iniModes_normal_power_tx_gain_9271, + modesIndex, regWrites); + } - if (AR_SREV_9271_10(ah)) - REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only, - modesIndex, regWrites); + ath9k_hw_write_regs(ah, freqIndex, regWrites); if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) { REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex, @@ -1517,6 +1498,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, if (OLC_FOR_AR9280_20_LATER) ath9k_olc_init(ah); + /* Set TX power */ ah->eep_ops->set_txpower(ah, chan, ath9k_regd_get_ctl(regulatory, chan), channel->max_antenna_gain * 2, @@ -1524,6 +1506,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah, min((u32) MAX_RATE_POWER, (u32) regulatory->power_limit)); + /* Write analog registers */ if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, "ar5416SetRfRegs failed\n"); @@ -1966,6 +1949,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, ath9k_hw_mark_phy_inactive(ah); + /* Only required on the first reset */ if (AR_SREV_9271(ah) && ah->htc_reset_init) { REG_WRITE(ah, AR9271_RESET_POWER_DOWN_CONTROL, @@ -1978,6 +1962,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, return -EINVAL; } + /* Only required on the first reset */ if (AR_SREV_9271(ah) && ah->htc_reset_init) { ah->htc_reset_init = false; REG_WRITE(ah, @@ -2438,7 +2423,7 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip) if (!AR_SREV_9100(ah)) REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); - if(!AR_SREV_5416(ah)) + if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah)) REG_CLR_BIT(ah, (AR_RTC_RESET), AR_RTC_RESET_EN); } @@ -3216,7 +3201,9 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) else pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD; - if (AR_SREV_9285_10_OR_LATER(ah)) + if (AR_SREV_9271(ah)) + pCap->num_gpio_pins = AR9271_NUM_GPIO; + else if (AR_SREV_9285_10_OR_LATER(ah)) pCap->num_gpio_pins = AR9285_NUM_GPIO; else if (AR_SREV_9280_10_OR_LATER(ah)) pCap->num_gpio_pins = AR928X_NUM_GPIO; @@ -3452,7 +3439,9 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio) if (gpio >= ah->caps.num_gpio_pins) return 0xffffffff; - if (AR_SREV_9287_10_OR_LATER(ah)) + if (AR_SREV_9271(ah)) + return MS_REG_READ(AR9271, gpio) != 0; + else if (AR_SREV_9287_10_OR_LATER(ah)) return MS_REG_READ(AR9287, gpio) != 0; else if (AR_SREV_9285_10_OR_LATER(ah)) return MS_REG_READ(AR9285, gpio) != 0; @@ -3481,6 +3470,9 @@ EXPORT_SYMBOL(ath9k_hw_cfg_output); void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val) { + if (AR_SREV_9271(ah)) + val = ~val; + REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio), AR_GPIO_BIT(gpio)); } @@ -3865,6 +3857,16 @@ void ath_gen_timer_isr(struct ath_hw *ah) } EXPORT_SYMBOL(ath_gen_timer_isr); +/********/ +/* HTC */ +/********/ + +void ath9k_hw_htc_resetinit(struct ath_hw *ah) +{ + ah->htc_reset_init = true; +} +EXPORT_SYMBOL(ath9k_hw_htc_resetinit); + static struct { u32 version; const char * name; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 20d90268ce31..6b03e1688b22 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -599,6 +599,11 @@ struct ath_hw { struct ar5416IniArray iniModes_9271_1_0_only; struct ar5416IniArray iniCckfirNormal; struct ar5416IniArray iniCckfirJapan2484; + struct ar5416IniArray iniCommon_normal_cck_fir_coeff_9271; + struct ar5416IniArray iniCommon_japan_2484_cck_fir_coeff_9271; + struct ar5416IniArray iniModes_9271_ANI_reg; + struct ar5416IniArray iniModes_high_power_tx_gain_9271; + struct ar5416IniArray iniModes_normal_power_tx_gain_9271; u32 intr_gen_timer_trigger; u32 intr_gen_timer_thresh; @@ -702,6 +707,9 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah); void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len); +/* HTC */ +void ath9k_hw_htc_resetinit(struct ath_hw *ah); + #define ATH_PCIE_CAP_LINK_CTRL 0x70 #define ATH_PCIE_CAP_LINK_L0S 1 #define ATH_PCIE_CAP_LINK_L1 2 diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 623c2f884987..6063f5463708 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -758,6 +758,9 @@ static void ath9k_deinit_softc(struct ath_softc *sc) tasklet_kill(&sc->intr_tq); tasklet_kill(&sc->bcon_tasklet); + + kfree(sc->sc_ah); + sc->sc_ah = NULL; } void ath9k_deinit_device(struct ath_softc *sc) diff --git a/drivers/net/wireless/ath/ath9k/initvals.h b/drivers/net/wireless/ath/ath9k/initvals.h index 8a3bf3ab998d..177bdeb84ad7 100644 --- a/drivers/net/wireless/ath/ath9k/initvals.h +++ b/drivers/net/wireless/ath/ath9k/initvals.h @@ -6441,7 +6441,7 @@ static const u_int32_t ar9271Modes_9271[][6] = { { 0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 }, { 0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 }, { 0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 }, - { 0x00009a50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 }, + { 0x00009a50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000 }, { 0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 }, { 0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 }, { 0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 }, @@ -6455,8 +6455,8 @@ static const u_int32_t ar9271Modes_9271[][6] = { { 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 }, { 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, { 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, - { 0x00009a88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, - { 0x00009a8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000 }, + { 0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, { 0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, { 0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 }, { 0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 }, @@ -6569,7 +6569,7 @@ static const u_int32_t ar9271Modes_9271[][6] = { { 0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 }, { 0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 }, { 0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 }, - { 0x0000aa50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 }, + { 0x0000aa50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000 }, { 0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 }, { 0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 }, { 0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 }, @@ -6583,8 +6583,8 @@ static const u_int32_t ar9271Modes_9271[][6] = { { 0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 }, { 0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, { 0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, - { 0x0000aa88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, - { 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, + { 0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000 }, + { 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, { 0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, { 0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 }, { 0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 }, @@ -6683,25 +6683,6 @@ static const u_int32_t ar9271Modes_9271[][6] = { { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, { 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 }, - { 0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a218652, 0x0a218652, 0x0a22a652 }, - { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, - { 0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000 }, - { 0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000 }, - { 0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000 }, - { 0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610, 0x00000000 }, - { 0x0000a314, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0, 0x00000000 }, - { 0x0000a318, 0x00000000, 0x00000000, 0x00039758, 0x00039758, 0x00000000 }, - { 0x0000a31c, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759, 0x00000000 }, - { 0x0000a320, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a, 0x00000000 }, - { 0x0000a324, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c, 0x00000000 }, - { 0x0000a328, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e, 0x00000000 }, - { 0x0000a32c, 0x00000000, 0x00000000, 0x0004979f, 0x0004979f, 0x00000000 }, - { 0x0000a330, 0x00000000, 0x00000000, 0x0004d7df, 0x0004d7df, 0x00000000 }, - { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 }, - { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 }, - { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 }, - { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, - { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e }, }; @@ -6879,7 +6860,7 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x00008258, 0x00000000 }, { 0x0000825c, 0x400000ff }, { 0x00008260, 0x00080922 }, - { 0x00008264, 0x88a00010 }, + { 0x00008264, 0xa8a00010 }, { 0x00008270, 0x00000000 }, { 0x00008274, 0x40000000 }, { 0x00008278, 0x003e4180 }, @@ -6910,13 +6891,10 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x00007810, 0x71c0d388 }, { 0x00007814, 0x924934a8 }, { 0x0000781c, 0x00000000 }, - { 0x00007820, 0x00000c04 }, - { 0x00007824, 0x00d8abff }, { 0x00007828, 0x66964300 }, { 0x0000782c, 0x8db6d961 }, { 0x00007830, 0x8db6d96c }, { 0x00007834, 0x6140008b }, - { 0x00007838, 0x00000029 }, { 0x0000783c, 0x72ee0a72 }, { 0x00007840, 0xbbfffffc }, { 0x00007844, 0x000c0db6 }, @@ -6929,7 +6907,6 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x00007860, 0x21084210 }, { 0x00007864, 0xf7d7ffde }, { 0x00007868, 0xc2034080 }, - { 0x0000786c, 0x48609eb4 }, { 0x00007870, 0x10142c00 }, { 0x00009808, 0x00000000 }, { 0x0000980c, 0xafe68e30 }, @@ -6982,9 +6959,6 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x000099e8, 0x3c466478 }, { 0x000099ec, 0x0cc80caa }, { 0x000099f0, 0x00000000 }, - { 0x0000a1f4, 0x00000000 }, - { 0x0000a1f8, 0x71733d01 }, - { 0x0000a1fc, 0xd0ad5c12 }, { 0x0000a208, 0x803e68c8 }, { 0x0000a210, 0x4080a333 }, { 0x0000a214, 0x00206c10 }, @@ -7004,13 +6978,9 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x0000a260, 0xdfa90f01 }, { 0x0000a268, 0x00000000 }, { 0x0000a26c, 0x0ebae9e6 }, - { 0x0000a278, 0x3bdef7bd }, - { 0x0000a27c, 0x050e83bd }, { 0x0000a388, 0x0c000000 }, { 0x0000a38c, 0x20202020 }, { 0x0000a390, 0x20202020 }, - { 0x0000a394, 0x3bdef7bd }, - { 0x0000a398, 0x000003bd }, { 0x0000a39c, 0x00000001 }, { 0x0000a3a0, 0x00000000 }, { 0x0000a3a4, 0x00000000 }, @@ -7025,8 +6995,6 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x0000a3cc, 0x20202020 }, { 0x0000a3d0, 0x20202020 }, { 0x0000a3d4, 0x20202020 }, - { 0x0000a3dc, 0x3bdef7bd }, - { 0x0000a3e0, 0x000003bd }, { 0x0000a3e4, 0x00000000 }, { 0x0000a3e8, 0x18c43433 }, { 0x0000a3ec, 0x00f70081 }, @@ -7046,7 +7014,102 @@ static const u_int32_t ar9271Common_9271[][2] = { { 0x0000d384, 0xf3307ff0 }, }; +static const u_int32_t ar9271Common_normal_cck_fir_coeff_9271[][2] = { + { 0x0000a1f4, 0x00fffeff }, + { 0x0000a1f8, 0x00f5f9ff }, + { 0x0000a1fc, 0xb79f6427 }, +}; + +static const u_int32_t ar9271Common_japan_2484_cck_fir_coeff_9271[][2] = { + { 0x0000a1f4, 0x00000000 }, + { 0x0000a1f8, 0xefff0301 }, + { 0x0000a1fc, 0xca9228ee }, +}; + static const u_int32_t ar9271Modes_9271_1_0_only[][6] = { { 0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311 }, { 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, }; + +static const u_int32_t ar9271Modes_9271_ANI_reg[][6] = { + { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 }, + { 0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e }, + { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, + { 0x0000986c, 0x06903881, 0x06903881, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, + { 0x0000a208, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8 }, + { 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d }, + { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, +}; + +static const u_int32_t ar9271Modes_normal_power_tx_gain_9271[][6] = { + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000 }, + { 0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000 }, + { 0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610, 0x00000000 }, + { 0x0000a314, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0, 0x00000000 }, + { 0x0000a318, 0x00000000, 0x00000000, 0x00039758, 0x00039758, 0x00000000 }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759, 0x00000000 }, + { 0x0000a320, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a, 0x00000000 }, + { 0x0000a324, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c, 0x00000000 }, + { 0x0000a328, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x0004979f, 0x0004979f, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x0004d7df, 0x0004d7df, 0x00000000 }, + { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 }, + { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 }, + { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 }, + { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x00007838, 0x00000029, 0x00000029, 0x00000029, 0x00000029, 0x00000029 }, + { 0x00007824, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff }, + { 0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4 }, + { 0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04 }, + { 0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a218652, 0x0a218652, 0x0a22a652 }, + { 0x0000a278, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd }, + { 0x0000a27c, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd }, + { 0x0000a394, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd }, + { 0x0000a398, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd }, + { 0x0000a3dc, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd }, + { 0x0000a3e0, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd }, +}; + +static const u_int32_t ar9271Modes_high_power_tx_gain_9271[][6] = { + { 0x0000a300, 0x00000000, 0x00000000, 0x00010000, 0x00010000, 0x00000000 }, + { 0x0000a304, 0x00000000, 0x00000000, 0x00016200, 0x00016200, 0x00000000 }, + { 0x0000a308, 0x00000000, 0x00000000, 0x00018201, 0x00018201, 0x00000000 }, + { 0x0000a30c, 0x00000000, 0x00000000, 0x0001b240, 0x0001b240, 0x00000000 }, + { 0x0000a310, 0x00000000, 0x00000000, 0x0001d241, 0x0001d241, 0x00000000 }, + { 0x0000a314, 0x00000000, 0x00000000, 0x0001f600, 0x0001f600, 0x00000000 }, + { 0x0000a318, 0x00000000, 0x00000000, 0x00022800, 0x00022800, 0x00000000 }, + { 0x0000a31c, 0x00000000, 0x00000000, 0x00026802, 0x00026802, 0x00000000 }, + { 0x0000a320, 0x00000000, 0x00000000, 0x0002b805, 0x0002b805, 0x00000000 }, + { 0x0000a324, 0x00000000, 0x00000000, 0x0002ea41, 0x0002ea41, 0x00000000 }, + { 0x0000a328, 0x00000000, 0x00000000, 0x00038b00, 0x00038b00, 0x00000000 }, + { 0x0000a32c, 0x00000000, 0x00000000, 0x0003ab40, 0x0003ab40, 0x00000000 }, + { 0x0000a330, 0x00000000, 0x00000000, 0x0003cd80, 0x0003cd80, 0x00000000 }, + { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 }, + { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 }, + { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 }, + { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 }, + { 0x00007838, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b }, + { 0x00007824, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff }, + { 0x0000786c, 0x08609eb6, 0x08609eb6, 0x08609eba, 0x08609eba, 0x08609eb6 }, + { 0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a212652, 0x0a212652, 0x0a22a652 }, + { 0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 }, + { 0x0000a27c, 0x05018063, 0x05038063, 0x05018063, 0x05018063, 0x05018063 }, + { 0x0000a394, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63 }, + { 0x0000a398, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063 }, + { 0x0000a3dc, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63 }, + { 0x0000a3e0, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063 }, +}; diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 589490b69ddc..7af823a1527d 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -351,7 +351,7 @@ void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds, ads->ds_ctl6 = SM(keyType, AR_EncrType); - if (AR_SREV_9285(ah)) { + if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) { ads->ds_ctl8 = 0; ads->ds_ctl9 = 0; ads->ds_ctl10 = 0; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 29851e6376a9..a5e543bd2271 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -150,6 +150,32 @@ struct ath_rx_status { u32 evm2; }; +struct ath_htc_rx_status { + u64 rs_tstamp; + u16 rs_datalen; + u8 rs_status; + u8 rs_phyerr; + int8_t rs_rssi; + int8_t rs_rssi_ctl0; + int8_t rs_rssi_ctl1; + int8_t rs_rssi_ctl2; + int8_t rs_rssi_ext0; + int8_t rs_rssi_ext1; + int8_t rs_rssi_ext2; + u8 rs_keyix; + u8 rs_rate; + u8 rs_antenna; + u8 rs_more; + u8 rs_isaggr; + u8 rs_moreaggr; + u8 rs_num_delims; + u8 rs_flags; + u8 rs_dummy; + u32 evm0; + u32 evm1; + u32 evm2; +}; + #define ATH9K_RXERR_CRC 0x01 #define ATH9K_RXERR_PHY 0x02 #define ATH9K_RXERR_FIFO 0x04 diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index f4818e4fa4b0..36083dde863d 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -110,8 +110,8 @@ struct ath_rate_table { int rate_cnt; int mcs_start; struct { - int valid; - int valid_single_stream; + u8 valid; + u8 valid_single_stream; u8 phy; u32 ratekbps; u32 user_ratekbps; diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 72cfa8ebd9ae..198e41dd38a6 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -940,6 +940,7 @@ enum { #define AR928X_NUM_GPIO 10 #define AR9285_NUM_GPIO 12 #define AR9287_NUM_GPIO 11 +#define AR9271_NUM_GPIO 16 #define AR_GPIO_IN_OUT 0x4048 #define AR_GPIO_IN_VAL 0x0FFFC000 @@ -950,6 +951,8 @@ enum { #define AR9285_GPIO_IN_VAL_S 12 #define AR9287_GPIO_IN_VAL 0x003FF800 #define AR9287_GPIO_IN_VAL_S 11 +#define AR9271_GPIO_IN_VAL 0xFFFF0000 +#define AR9271_GPIO_IN_VAL_S 16 #define AR_GPIO_OE_OUT 0x404c #define AR_GPIO_OE_OUT_DRV 0x3 diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c new file mode 100644 index 000000000000..818dea0164ec --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2010 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 "htc.h" + +static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd) +{ + switch (wmi_cmd) { + case WMI_ECHO_CMDID: + return "WMI_ECHO_CMDID"; + case WMI_ACCESS_MEMORY_CMDID: + return "WMI_ACCESS_MEMORY_CMDID"; + case WMI_DISABLE_INTR_CMDID: + return "WMI_DISABLE_INTR_CMDID"; + case WMI_ENABLE_INTR_CMDID: + return "WMI_ENABLE_INTR_CMDID"; + case WMI_RX_LINK_CMDID: + return "WMI_RX_LINK_CMDID"; + case WMI_ATH_INIT_CMDID: + return "WMI_ATH_INIT_CMDID"; + case WMI_ABORT_TXQ_CMDID: + return "WMI_ABORT_TXQ_CMDID"; + case WMI_STOP_TX_DMA_CMDID: + return "WMI_STOP_TX_DMA_CMDID"; + case WMI_STOP_DMA_RECV_CMDID: + return "WMI_STOP_DMA_RECV_CMDID"; + case WMI_ABORT_TX_DMA_CMDID: + return "WMI_ABORT_TX_DMA_CMDID"; + case WMI_DRAIN_TXQ_CMDID: + return "WMI_DRAIN_TXQ_CMDID"; + case WMI_DRAIN_TXQ_ALL_CMDID: + return "WMI_DRAIN_TXQ_ALL_CMDID"; + case WMI_START_RECV_CMDID: + return "WMI_START_RECV_CMDID"; + case WMI_STOP_RECV_CMDID: + return "WMI_STOP_RECV_CMDID"; + case WMI_FLUSH_RECV_CMDID: + return "WMI_FLUSH_RECV_CMDID"; + case WMI_SET_MODE_CMDID: + return "WMI_SET_MODE_CMDID"; + case WMI_RESET_CMDID: + return "WMI_RESET_CMDID"; + case WMI_NODE_CREATE_CMDID: + return "WMI_NODE_CREATE_CMDID"; + case WMI_NODE_REMOVE_CMDID: + return "WMI_NODE_REMOVE_CMDID"; + case WMI_VAP_REMOVE_CMDID: + return "WMI_VAP_REMOVE_CMDID"; + case WMI_VAP_CREATE_CMDID: + return "WMI_VAP_CREATE_CMDID"; + case WMI_BEACON_UPDATE_CMDID: + return "WMI_BEACON_UPDATE_CMDID"; + case WMI_REG_READ_CMDID: + return "WMI_REG_READ_CMDID"; + case WMI_REG_WRITE_CMDID: + return "WMI_REG_WRITE_CMDID"; + case WMI_RC_STATE_CHANGE_CMDID: + return "WMI_RC_STATE_CHANGE_CMDID"; + case WMI_RC_RATE_UPDATE_CMDID: + return "WMI_RC_RATE_UPDATE_CMDID"; + case WMI_DEBUG_INFO_CMDID: + return "WMI_DEBUG_INFO_CMDID"; + case WMI_HOST_ATTACH: + return "WMI_HOST_ATTACH"; + case WMI_TARGET_IC_UPDATE_CMDID: + return "WMI_TARGET_IC_UPDATE_CMDID"; + case WMI_TGT_STATS_CMDID: + return "WMI_TGT_STATS_CMDID"; + case WMI_TX_AGGR_ENABLE_CMDID: + return "WMI_TX_AGGR_ENABLE_CMDID"; + case WMI_TGT_DETACH_CMDID: + return "WMI_TGT_DETACH_CMDID"; + case WMI_TGT_TXQ_ENABLE_CMDID: + return "WMI_TGT_TXQ_ENABLE_CMDID"; + } + + return "Bogus"; +} + +struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv) +{ + struct wmi *wmi; + + wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL); + if (!wmi) + return NULL; + + wmi->drv_priv = priv; + wmi->stopped = false; + mutex_init(&wmi->op_mutex); + init_completion(&wmi->cmd_wait); + + return wmi; +} + +void ath9k_deinit_wmi(struct ath9k_htc_priv *priv) +{ + struct wmi *wmi = priv->wmi; + + mutex_lock(&wmi->op_mutex); + wmi->stopped = true; + mutex_unlock(&wmi->op_mutex); + + kfree(priv->wmi); +} + +void ath9k_wmi_tasklet(unsigned long data) +{ + struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; + struct ath_common *common = ath9k_hw_common(priv->ah); + struct wmi_cmd_hdr *hdr; + struct wmi_swba *swba_hdr; + enum wmi_event_id event; + struct sk_buff *skb; + void *wmi_event; + unsigned long flags; +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + u32 txrate; +#endif + + spin_lock_irqsave(&priv->wmi->wmi_lock, flags); + skb = priv->wmi->wmi_skb; + spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags); + + hdr = (struct wmi_cmd_hdr *) skb->data; + event = be16_to_cpu(hdr->command_id); + wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + + ath_print(common, ATH_DBG_WMI, + "WMI Event: 0x%x\n", event); + + switch (event) { + case WMI_TGT_RDY_EVENTID: + break; + case WMI_SWBA_EVENTID: + swba_hdr = (struct wmi_swba *) wmi_event; + ath9k_htc_swba(priv, swba_hdr->beacon_pending); + break; + case WMI_FATAL_EVENTID: + break; + case WMI_TXTO_EVENTID: + break; + case WMI_BMISS_EVENTID: + break; + case WMI_WLAN_TXCOMP_EVENTID: + break; + case WMI_DELBA_EVENTID: + break; + case WMI_TXRATE_EVENTID: +#ifdef CONFIG_ATH9K_HTC_DEBUGFS + txrate = ((struct wmi_event_txrate *)wmi_event)->txrate; + priv->debug.txrate = be32_to_cpu(txrate); +#endif + break; + default: + break; + } + + dev_kfree_skb_any(skb); +} + +static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb) +{ + skb_pull(skb, sizeof(struct wmi_cmd_hdr)); + + if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0) + memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len); + + complete(&wmi->cmd_wait); +} + +static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb, + enum htc_endpoint_id epid) +{ + struct wmi *wmi = (struct wmi *) priv; + struct wmi_cmd_hdr *hdr; + u16 cmd_id; + + if (unlikely(wmi->stopped)) + goto free_skb; + + hdr = (struct wmi_cmd_hdr *) skb->data; + cmd_id = be16_to_cpu(hdr->command_id); + + if (cmd_id & 0x1000) { + spin_lock(&wmi->wmi_lock); + wmi->wmi_skb = skb; + spin_unlock(&wmi->wmi_lock); + tasklet_schedule(&wmi->drv_priv->wmi_tasklet); + return; + } + + /* WMI command response */ + ath9k_wmi_rsp_callback(wmi, skb); + +free_skb: + dev_kfree_skb_any(skb); +} + +static void ath9k_wmi_ctrl_tx(void *priv, struct sk_buff *skb, + enum htc_endpoint_id epid, bool txok) +{ + dev_kfree_skb_any(skb); +} + +int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi, + enum htc_endpoint_id *wmi_ctrl_epid) +{ + struct htc_service_connreq connect; + int ret; + + wmi->htc = htc; + + memset(&connect, 0, sizeof(connect)); + + connect.ep_callbacks.priv = wmi; + connect.ep_callbacks.tx = ath9k_wmi_ctrl_tx; + connect.ep_callbacks.rx = ath9k_wmi_ctrl_rx; + connect.service_id = WMI_CONTROL_SVC; + + ret = htc_connect_service(htc, &connect, &wmi->ctrl_epid); + if (ret) + return ret; + + *wmi_ctrl_epid = wmi->ctrl_epid; + + return 0; +} + +static int ath9k_wmi_cmd_issue(struct wmi *wmi, + struct sk_buff *skb, + enum wmi_cmd_id cmd, u16 len) +{ + struct wmi_cmd_hdr *hdr; + + hdr = (struct wmi_cmd_hdr *) skb_push(skb, sizeof(struct wmi_cmd_hdr)); + hdr->command_id = cpu_to_be16(cmd); + hdr->seq_no = cpu_to_be16(++wmi->tx_seq_id); + + return htc_send(wmi->htc, skb, wmi->ctrl_epid, NULL); +} + +int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, + u8 *cmd_buf, u32 cmd_len, + u8 *rsp_buf, u32 rsp_len, + u32 timeout) +{ + struct ath_hw *ah = wmi->drv_priv->ah; + struct ath_common *common = ath9k_hw_common(ah); + u16 headroom = sizeof(struct htc_frame_hdr) + + sizeof(struct wmi_cmd_hdr); + struct sk_buff *skb; + u8 *data; + int time_left, ret = 0; + + if (!wmi) + return -EINVAL; + + skb = dev_alloc_skb(headroom + cmd_len); + if (!skb) + return -ENOMEM; + + skb_reserve(skb, headroom); + + if (cmd_len != 0 && cmd_buf != NULL) { + data = (u8 *) skb_put(skb, cmd_len); + memcpy(data, cmd_buf, cmd_len); + } + + mutex_lock(&wmi->op_mutex); + + /* check if wmi stopped flag is set */ + if (unlikely(wmi->stopped)) { + ret = -EPROTO; + goto out; + } + + /* record the rsp buffer and length */ + wmi->cmd_rsp_buf = rsp_buf; + wmi->cmd_rsp_len = rsp_len; + + ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len); + if (ret) + goto out; + + time_left = wait_for_completion_timeout(&wmi->cmd_wait, timeout); + if (!time_left) { + ath_print(common, ATH_DBG_WMI, + "Timeout waiting for WMI command: %s\n", + wmi_cmd_to_name(cmd_id)); + mutex_unlock(&wmi->op_mutex); + return -ETIMEDOUT; + } + + mutex_unlock(&wmi->op_mutex); + + return 0; + +out: + ath_print(common, ATH_DBG_WMI, + "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id)); + mutex_unlock(&wmi->op_mutex); + dev_kfree_skb_any(skb); + + return ret; +} diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h new file mode 100644 index 000000000000..39ef926f27c2 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/wmi.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2010 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 WMI_H +#define WMI_H + + +struct wmi_event_txrate { + u32 txrate; + struct { + u8 rssi_thresh; + u8 per; + } rc_stats; +} __packed; + +struct wmi_cmd_hdr { + u16 command_id; + u16 seq_no; +} __packed; + +struct wmi_swba { + u8 beacon_pending; +} __packed; + +enum wmi_cmd_id { + WMI_ECHO_CMDID = 0x0001, + WMI_ACCESS_MEMORY_CMDID, + + /* Commands to Target */ + WMI_DISABLE_INTR_CMDID, + WMI_ENABLE_INTR_CMDID, + WMI_RX_LINK_CMDID, + WMI_ATH_INIT_CMDID, + WMI_ABORT_TXQ_CMDID, + WMI_STOP_TX_DMA_CMDID, + WMI_STOP_DMA_RECV_CMDID, + WMI_ABORT_TX_DMA_CMDID, + WMI_DRAIN_TXQ_CMDID, + WMI_DRAIN_TXQ_ALL_CMDID, + WMI_START_RECV_CMDID, + WMI_STOP_RECV_CMDID, + WMI_FLUSH_RECV_CMDID, + WMI_SET_MODE_CMDID, + WMI_RESET_CMDID, + WMI_NODE_CREATE_CMDID, + WMI_NODE_REMOVE_CMDID, + WMI_VAP_REMOVE_CMDID, + WMI_VAP_CREATE_CMDID, + WMI_BEACON_UPDATE_CMDID, + WMI_REG_READ_CMDID, + WMI_REG_WRITE_CMDID, + WMI_RC_STATE_CHANGE_CMDID, + WMI_RC_RATE_UPDATE_CMDID, + WMI_DEBUG_INFO_CMDID, + WMI_HOST_ATTACH, + WMI_TARGET_IC_UPDATE_CMDID, + WMI_TGT_STATS_CMDID, + WMI_TX_AGGR_ENABLE_CMDID, + WMI_TGT_DETACH_CMDID, + WMI_TGT_TXQ_ENABLE_CMDID, +}; + +enum wmi_event_id { + WMI_TGT_RDY_EVENTID = 0x1001, + WMI_SWBA_EVENTID, + WMI_FATAL_EVENTID, + WMI_TXTO_EVENTID, + WMI_BMISS_EVENTID, + WMI_WLAN_TXCOMP_EVENTID, + WMI_DELBA_EVENTID, + WMI_TXRATE_EVENTID, +}; + +struct wmi { + struct ath9k_htc_priv *drv_priv; + struct htc_target *htc; + enum htc_endpoint_id ctrl_epid; + struct mutex op_mutex; + struct completion cmd_wait; + u16 tx_seq_id; + u8 *cmd_rsp_buf; + u32 cmd_rsp_len; + bool stopped; + + struct sk_buff *wmi_skb; + spinlock_t wmi_lock; +}; + +struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv); +void ath9k_deinit_wmi(struct ath9k_htc_priv *priv); +int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi, + enum htc_endpoint_id *wmi_ctrl_epid); +int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id, + u8 *cmd_buf, u32 cmd_len, + u8 *rsp_buf, u32 rsp_len, + u32 timeout); +void ath9k_wmi_tasklet(unsigned long data); + +#define WMI_CMD(_wmi_cmd) \ + do { \ + ret = ath9k_wmi_cmd(priv->wmi, _wmi_cmd, NULL, 0, \ + (u8 *) &cmd_rsp, \ + sizeof(cmd_rsp), HZ); \ + } while (0) + +#define WMI_CMD_BUF(_wmi_cmd, _buf) \ + do { \ + ret = ath9k_wmi_cmd(priv->wmi, _wmi_cmd, \ + (u8 *) _buf, sizeof(*_buf), \ + &cmd_rsp, sizeof(cmd_rsp), HZ); \ + } while (0) + +#endif /* WMI_H */ diff --git a/drivers/net/wireless/ath/debug.h b/drivers/net/wireless/ath/debug.h index 8263633c003c..873bf526e11f 100644 --- a/drivers/net/wireless/ath/debug.h +++ b/drivers/net/wireless/ath/debug.h @@ -59,6 +59,7 @@ enum ATH_DEBUG { ATH_DBG_PS = 0x00000800, ATH_DBG_HWTIMER = 0x00001000, ATH_DBG_BTCOEX = 0x00002000, + ATH_DBG_WMI = 0x00004000, ATH_DBG_ANY = 0xffffffff }; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 1521b1e78d21..14cf3bd7ea51 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4348,11 +4348,10 @@ static int b43_wireless_core_init(struct b43_wldev *dev) b43_set_phytxctl_defaults(dev); /* Minimum Contention Window */ - if (phy->type == B43_PHYTYPE_B) { + if (phy->type == B43_PHYTYPE_B) b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0x1F); - } else { + else b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0xF); - } /* Maximum Contention Window */ b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF); diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 6dc0733df46b..cb3ba134865e 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -9995,49 +9995,48 @@ static int ipw_wx_sw_reset(struct net_device *dev, } /* Rebase the WE IOCTLs to zero for the handler array */ -#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT] static iw_handler ipw_wx_handlers[] = { - IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname, - IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq, - IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq, - IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode, - IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode, - IW_IOCTL(SIOCSIWSENS) = ipw_wx_set_sens, - IW_IOCTL(SIOCGIWSENS) = ipw_wx_get_sens, - IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range, - IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap, - IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap, - IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan, - IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan, - IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid, - IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid, - IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick, - IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick, - IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate, - IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate, - IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts, - IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts, - IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag, - IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag, - IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow, - IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow, - IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry, - IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry, - IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode, - IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode, - IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power, - IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power, - IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy, - IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy, - IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy, - IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy, - IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie, - IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie, - IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme, - IW_IOCTL(SIOCSIWAUTH) = ipw_wx_set_auth, - IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth, - IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext, - IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext, + IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), + IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq), + IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq), + IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode), + IW_HANDLER(SIOCGIWMODE, ipw_wx_get_mode), + IW_HANDLER(SIOCSIWSENS, ipw_wx_set_sens), + IW_HANDLER(SIOCGIWSENS, ipw_wx_get_sens), + IW_HANDLER(SIOCGIWRANGE, ipw_wx_get_range), + IW_HANDLER(SIOCSIWAP, ipw_wx_set_wap), + IW_HANDLER(SIOCGIWAP, ipw_wx_get_wap), + IW_HANDLER(SIOCSIWSCAN, ipw_wx_set_scan), + IW_HANDLER(SIOCGIWSCAN, ipw_wx_get_scan), + IW_HANDLER(SIOCSIWESSID, ipw_wx_set_essid), + IW_HANDLER(SIOCGIWESSID, ipw_wx_get_essid), + IW_HANDLER(SIOCSIWNICKN, ipw_wx_set_nick), + IW_HANDLER(SIOCGIWNICKN, ipw_wx_get_nick), + IW_HANDLER(SIOCSIWRATE, ipw_wx_set_rate), + IW_HANDLER(SIOCGIWRATE, ipw_wx_get_rate), + IW_HANDLER(SIOCSIWRTS, ipw_wx_set_rts), + IW_HANDLER(SIOCGIWRTS, ipw_wx_get_rts), + IW_HANDLER(SIOCSIWFRAG, ipw_wx_set_frag), + IW_HANDLER(SIOCGIWFRAG, ipw_wx_get_frag), + IW_HANDLER(SIOCSIWTXPOW, ipw_wx_set_txpow), + IW_HANDLER(SIOCGIWTXPOW, ipw_wx_get_txpow), + IW_HANDLER(SIOCSIWRETRY, ipw_wx_set_retry), + IW_HANDLER(SIOCGIWRETRY, ipw_wx_get_retry), + IW_HANDLER(SIOCSIWENCODE, ipw_wx_set_encode), + IW_HANDLER(SIOCGIWENCODE, ipw_wx_get_encode), + IW_HANDLER(SIOCSIWPOWER, ipw_wx_set_power), + IW_HANDLER(SIOCGIWPOWER, ipw_wx_get_power), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWGENIE, ipw_wx_set_genie), + IW_HANDLER(SIOCGIWGENIE, ipw_wx_get_genie), + IW_HANDLER(SIOCSIWMLME, ipw_wx_set_mlme), + IW_HANDLER(SIOCSIWAUTH, ipw_wx_set_auth), + IW_HANDLER(SIOCGIWAUTH, ipw_wx_get_auth), + IW_HANDLER(SIOCSIWENCODEEXT, ipw_wx_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, ipw_wx_get_encodeext), }; enum { diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 59b092eaa829..9e392896005d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -212,6 +212,9 @@ static struct iwl_lib_ops iwl1000_lib = { .set_ct_kill = iwl1000_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_tx_stall = iwl_bg_monitor_recover, + .check_plcp_health = iwl_good_plcp_health, + .check_ack_health = iwl_good_ack_health, }; static const struct iwl_ops iwl1000_ops = { @@ -223,7 +226,7 @@ static const struct iwl_ops iwl1000_ops = { }; struct iwl_cfg iwl1000_bgn_cfg = { - .name = "1000 Series BGN", + .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", .fw_name_pre = IWL1000_FW_PRE, .ucode_api_max = IWL1000_UCODE_API_MAX, .ucode_api_min = IWL1000_UCODE_API_MIN, @@ -249,10 +252,11 @@ struct iwl_cfg iwl1000_bgn_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl1000_bg_cfg = { - .name = "1000 Series BG", + .name = "Intel(R) Centrino(R) Wireless-N 1000 BG", .fw_name_pre = IWL1000_FW_PRE, .ucode_api_max = IWL1000_UCODE_API_MAX, .ucode_api_min = IWL1000_UCODE_API_MIN, @@ -277,6 +281,7 @@ struct iwl_cfg iwl1000_bg_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c index b588cb69536a..605aca4c78c8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c @@ -329,16 +329,25 @@ static void iwl3945_collect_tx_data(struct iwl3945_rs_sta *rs_sta, } -static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta) +/* + * Called after adding a new station to initialize rate scaling + */ +void iwl3945_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id) { - struct iwl3945_rs_sta *rs_sta = priv_sta; - struct iwl_priv *priv = (struct iwl_priv *)priv_r; + struct ieee80211_hw *hw = priv->hw; + struct ieee80211_conf *conf = &priv->hw->conf; + struct iwl3945_sta_priv *psta; + struct iwl3945_rs_sta *rs_sta; + struct ieee80211_supported_band *sband; int i; - IWL_DEBUG_RATE(priv, "enter\n"); + IWL_DEBUG_INFO(priv, "enter \n"); + if (sta_id == priv->hw_params.bcast_sta_id) + goto out; - spin_lock_init(&rs_sta->lock); + psta = (struct iwl3945_sta_priv *) sta->drv_priv; + rs_sta = &psta->rs_sta; + sband = hw->wiphy->bands[conf->channel->band]; rs_sta->priv = priv; @@ -351,9 +360,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, rs_sta->last_flush = jiffies; rs_sta->flush_time = IWL_RATE_FLUSH; rs_sta->last_tx_packets = 0; - rs_sta->ibss_sta_added = 0; - init_timer(&rs_sta->rate_scale_flush); rs_sta->rate_scale_flush.data = (unsigned long)rs_sta; rs_sta->rate_scale_flush.function = iwl3945_bg_rate_scale_flush; @@ -380,8 +387,10 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, IWL_FIRST_OFDM_RATE; } +out: + priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; - IWL_DEBUG_RATE(priv, "leave\n"); + IWL_DEBUG_INFO(priv, "leave\n"); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) @@ -405,6 +414,9 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp) rs_sta = &psta->rs_sta; + spin_lock_init(&rs_sta->lock); + init_timer(&rs_sta->rate_scale_flush); + IWL_DEBUG_RATE(priv, "leave\n"); return rs_sta; @@ -413,13 +425,14 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp) static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta, void *priv_sta) { - struct iwl3945_sta_priv *psta = (void *) sta->drv_priv; - struct iwl3945_rs_sta *rs_sta = &psta->rs_sta; - struct iwl_priv *priv __maybe_unused = rs_sta->priv; + struct iwl3945_rs_sta *rs_sta = priv_sta; - IWL_DEBUG_RATE(priv, "enter\n"); + /* + * Be careful not to use any members of iwl3945_rs_sta (like trying + * to use iwl_priv to print out debugging) since it may not be fully + * initialized at this point. + */ del_timer_sync(&rs_sta->rate_scale_flush); - IWL_DEBUG_RATE(priv, "leave\n"); } @@ -458,6 +471,13 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband return; } + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!rs_sta->priv) { + IWL_DEBUG_RATE(priv, "leave: STA priv data uninitialized!\n"); + return; + } + + rs_sta->tx_packets++; scale_rate_index = first_index; @@ -625,7 +645,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, u32 fail_count; s8 scale_action = 0; unsigned long flags; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0; s8 max_rate_idx = -1; struct iwl_priv *priv = (struct iwl_priv *)priv_r; @@ -633,6 +652,12 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, IWL_DEBUG_RATE(priv, "enter\n"); + /* Treat uninitialized rate scaling data same as non-existing. */ + if (rs_sta && !rs_sta->priv) { + IWL_DEBUG_RATE(priv, "Rate scaling information not initialized yet.\n"); + priv_sta = NULL; + } + if (rate_control_send_low(sta, priv_sta, txrc)) return; @@ -650,20 +675,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, if (sband->band == IEEE80211_BAND_5GHZ) rate_mask = rate_mask << IWL_FIRST_OFDM_RATE; - if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) && - !rs_sta->ibss_sta_added) { - u8 sta_id = iwl_find_station(priv, hdr->addr1); - - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", - hdr->addr1); - sta_id = iwl_add_station(priv, hdr->addr1, false, - CMD_ASYNC, NULL); - } - if (sta_id != IWL_INVALID_STATION) - rs_sta->ibss_sta_added = 1; - } - spin_lock_irqsave(&rs_sta->lock, flags); /* for recent assoc, choose best rate regarding @@ -883,12 +894,22 @@ static void iwl3945_remove_debugfs(void *priv, void *priv_sta) } #endif +/* + * Initialization of rate scaling information is done by driver after + * the station is added. Since mac80211 calls this function before a + * station is added we ignore it. + */ +static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta) +{ +} + static struct rate_control_ops rs_ops = { .module = NULL, .name = RS_NAME, .tx_status = rs_tx_status, .get_rate = rs_get_rate, - .rate_init = rs_rate_init, + .rate_init = rs_rate_init_stub, .alloc = rs_alloc, .free = rs_free, .alloc_sta = rs_alloc_sta, @@ -899,7 +920,6 @@ static struct rate_control_ops rs_ops = { #endif }; - void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) { struct iwl_priv *priv = hw->priv; @@ -916,6 +936,7 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) sta = ieee80211_find_sta(priv->vif, priv->stations[sta_id].sta.sta.addr); if (!sta) { + IWL_DEBUG_RATE(priv, "Unable to find station to initialize rate scaling.\n"); rcu_read_unlock(); return; } diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c index 12a42fc743d7..3b5889d8d662 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlwifi/iwl-3945.c @@ -1911,6 +1911,8 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) "configuration (%d).\n", rc); return rc; } + iwl_clear_ucode_stations(priv, false); + iwl_restore_stations(priv); } IWL_DEBUG_INFO(priv, "Sending RXON\n" @@ -1941,7 +1943,10 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) memcpy(active_rxon, staging_rxon, sizeof(*active_rxon)); - iwl_clear_stations_table(priv); + if (!new_assoc) { + iwl_clear_ucode_stations(priv, false); + iwl_restore_stations(priv); + } /* If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames */ @@ -1951,19 +1956,6 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv) return rc; } - /* Add the broadcast address so we can send broadcast frames */ - priv->cfg->ops->lib->add_bcast_station(priv); - - /* If we have set the ASSOC_MSK and we are in BSS mode then - * add the IWL_AP_ID to the station rate table */ - if (iwl_is_associated(priv) && - (priv->iw_mode == NL80211_IFTYPE_STATION)) - if (iwl_add_station(priv, priv->active_rxon.bssid_addr, - true, CMD_SYNC, NULL) == IWL_INVALID_STATION) { - IWL_ERR(priv, "Error adding AP address for transmit\n"); - return -EIO; - } - /* Init the hardware's rate fallback order based on the band */ rc = iwl3945_init_hw_rate_table(priv); if (rc) { @@ -2828,6 +2820,7 @@ static struct iwl_cfg iwl3945_bg_cfg = { .led_compensation = 64, .broken_powersave = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; static struct iwl_cfg iwl3945_abg_cfg = { @@ -2846,6 +2839,7 @@ static struct iwl_cfg iwl3945_abg_cfg = { .led_compensation = 64, .broken_powersave = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; DEFINE_PCI_DEVICE_TABLE(iwl3945_hw_card_ids) = { diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlwifi/iwl-3945.h index 452dfd5456c6..b89219573b91 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.h +++ b/drivers/net/wireless/iwlwifi/iwl-3945.h @@ -95,7 +95,6 @@ struct iwl3945_rs_sta { u8 tgg; u8 flush_pending; u8 start_rate; - u8 ibss_sta_added; struct timer_list rate_scale_flush; struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945]; #ifdef CONFIG_MAC80211_DEBUGFS diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 644aacfbd7df..3949133d9ee2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -2187,6 +2187,7 @@ static struct iwl_lib_ops iwl4965_lib = { .load_ucode = iwl4965_load_bsm, .dump_nic_event_log = iwl_dump_nic_event_log, .dump_nic_error_log = iwl_dump_nic_error_log, + .dump_fh = iwl_dump_fh, .set_channel_switch = iwl4965_hw_channel_switch, .apm_ops = { .init = iwl_apm_init, @@ -2220,6 +2221,7 @@ static struct iwl_lib_ops iwl4965_lib = { .set_ct_kill = iwl4965_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, + .check_plcp_health = iwl_good_plcp_health, }; static const struct iwl_ops iwl4965_ops = { @@ -2231,7 +2233,7 @@ static const struct iwl_ops iwl4965_ops = { }; struct iwl_cfg iwl4965_agn_cfg = { - .name = "4965AGN", + .name = "Intel(R) Wireless WiFi Link 4965AGN", .fw_name_pre = IWL4965_FW_PRE, .ucode_api_max = IWL4965_UCODE_API_MAX, .ucode_api_min = IWL4965_UCODE_API_MIN, @@ -2254,6 +2256,7 @@ struct iwl_cfg iwl4965_agn_cfg = { .led_compensation = 61, .chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; /* Module firmware */ diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 37e1e77f513d..2267cad49cbf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -544,7 +544,6 @@ void iwl5000_init_alive_start(struct iwl_priv *priv) goto restart; } - iwl_clear_stations_table(priv); ret = priv->cfg->ops->lib->alive_notify(priv); if (ret) { IWL_WARN(priv, @@ -1500,6 +1499,9 @@ struct iwl_lib_ops iwl5000_lib = { .set_ct_kill = iwl5000_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_tx_stall = iwl_bg_monitor_recover, + .check_plcp_health = iwl_good_plcp_health, + .check_ack_health = iwl_good_ack_health, }; static struct iwl_lib_ops iwl5150_lib = { @@ -1554,6 +1556,9 @@ static struct iwl_lib_ops iwl5150_lib = { .set_ct_kill = iwl5150_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_tx_stall = iwl_bg_monitor_recover, + .check_plcp_health = iwl_good_plcp_health, + .check_ack_health = iwl_good_ack_health, }; static const struct iwl_ops iwl5000_ops = { @@ -1580,7 +1585,7 @@ struct iwl_mod_params iwl50_mod_params = { struct iwl_cfg iwl5300_agn_cfg = { - .name = "5300AGN", + .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, @@ -1603,10 +1608,11 @@ struct iwl_cfg iwl5300_agn_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5100_bgn_cfg = { - .name = "5100BGN", + .name = "Intel(R) WiFi Link 5100 BGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, @@ -1629,10 +1635,11 @@ struct iwl_cfg iwl5100_bgn_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5100_abg_cfg = { - .name = "5100ABG", + .name = "Intel(R) WiFi Link 5100 ABG", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, @@ -1653,10 +1660,11 @@ struct iwl_cfg iwl5100_abg_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5100_agn_cfg = { - .name = "5100AGN", + .name = "Intel(R) WiFi Link 5100 AGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, @@ -1679,10 +1687,11 @@ struct iwl_cfg iwl5100_agn_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5350_agn_cfg = { - .name = "5350AGN", + .name = "Intel(R) WiMAX/WiFi Link 5350 AGN", .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, @@ -1705,10 +1714,11 @@ struct iwl_cfg iwl5350_agn_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5150_agn_cfg = { - .name = "5150AGN", + .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", .fw_name_pre = IWL5150_FW_PRE, .ucode_api_max = IWL5150_UCODE_API_MAX, .ucode_api_min = IWL5150_UCODE_API_MIN, @@ -1731,10 +1741,11 @@ struct iwl_cfg iwl5150_agn_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl5150_abg_cfg = { - .name = "5150ABG", + .name = "Intel(R) WiMAX/WiFi Link 5150 ABG", .fw_name_pre = IWL5150_FW_PRE, .ucode_api_max = IWL5150_UCODE_API_MAX, .ucode_api_min = IWL5150_UCODE_API_MIN, @@ -1755,6 +1766,7 @@ struct iwl_cfg iwl5150_abg_cfg = { .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 4b7bc008220f..d75799946a7e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -278,6 +278,9 @@ static struct iwl_lib_ops iwl6000_lib = { .set_ct_kill = iwl6000_set_ct_threshold, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_tx_stall = iwl_bg_monitor_recover, + .check_plcp_health = iwl_good_plcp_health, + .check_ack_health = iwl_good_ack_health, }; static const struct iwl_ops iwl6000_ops = { @@ -343,6 +346,9 @@ static struct iwl_lib_ops iwl6050_lib = { .set_calib_version = iwl6050_set_calib_version, }, .add_bcast_station = iwl_add_bcast_station, + .recover_from_tx_stall = iwl_bg_monitor_recover, + .check_plcp_health = iwl_good_plcp_health, + .check_ack_health = iwl_good_ack_health, }; static const struct iwl_ops iwl6050_ops = { @@ -357,7 +363,7 @@ static const struct iwl_ops iwl6050_ops = { * "i": Internal configuration, use internal Power Amplifier */ struct iwl_cfg iwl6000i_2agn_cfg = { - .name = "6000 Series 2x2 AGN", + .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, @@ -386,10 +392,11 @@ struct iwl_cfg iwl6000i_2agn_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl6000i_2abg_cfg = { - .name = "6000 Series 2x2 ABG", + .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, @@ -417,10 +424,11 @@ struct iwl_cfg iwl6000i_2abg_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl6000i_2bg_cfg = { - .name = "6000 Series 2x2 BG", + .name = "Intel(R) Centrino(R) Advanced-N 6200 BG", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, @@ -448,10 +456,11 @@ struct iwl_cfg iwl6000i_2bg_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl6050_2agn_cfg = { - .name = "6050 Series 2x2 AGN", + .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", .fw_name_pre = IWL6050_FW_PRE, .ucode_api_max = IWL6050_UCODE_API_MAX, .ucode_api_min = IWL6050_UCODE_API_MIN, @@ -480,10 +489,11 @@ struct iwl_cfg iwl6050_2agn_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1500, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl6050_2abg_cfg = { - .name = "6050 Series 2x2 ABG", + .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG", .fw_name_pre = IWL6050_FW_PRE, .ucode_api_max = IWL6050_UCODE_API_MAX, .ucode_api_min = IWL6050_UCODE_API_MIN, @@ -511,10 +521,11 @@ struct iwl_cfg iwl6050_2abg_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1500, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; struct iwl_cfg iwl6000_3agn_cfg = { - .name = "6000 Series 3x3 AGN", + .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, @@ -543,6 +554,7 @@ struct iwl_cfg iwl6000_3agn_cfg = { .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .chain_noise_scale = 1000, + .monitor_recover_period = IWL_MONITORING_PERIOD, }; MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 84271cc62afa..5155b1a027eb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -769,6 +769,15 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!lq_sta) { + IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n"); + return; + } else if (!lq_sta->drv) { + IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); + return; + } + if (!ieee80211_is_data(hdr->frame_control) || info->flags & IEEE80211_TX_CTL_NO_ACK) return; @@ -778,10 +787,6 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, !(info->flags & IEEE80211_TX_STAT_AMPDU)) return; - if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) && - !lq_sta->ibss_sta_added) - return; - /* * Ignore this Tx frame response if its initial rate doesn't match * that of latest Link Quality command. There may be stragglers @@ -827,7 +832,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, lq_sta->missed_rate_counter++; if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { lq_sta->missed_rate_counter = 0; - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false); } /* Regardless, ignore this status info for outdated rate */ return; @@ -1915,7 +1920,7 @@ static u32 rs_update_rate_tbl(struct iwl_priv *priv, /* Update uCode's rate table. */ rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); rs_fill_link_cmd(priv, lq_sta, rate); - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false); return rate; } @@ -2291,7 +2296,7 @@ lq_update: IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n", tbl->current_rate, index); rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false); } else done_search = 1; } @@ -2340,7 +2345,20 @@ out: return; } - +/** + * rs_initialize_lq - Initialize a station's hardware rate table + * + * The uCode's station table contains a table of fallback rates + * for automatic fallback during transmission. + * + * NOTE: This sets up a default set of values. These will be replaced later + * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of + * rc80211_simple. + * + * NOTE: Run REPLY_ADD_STA command to set up station table entry, before + * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, + * which requires station table entry to exist). + */ static void rs_initialize_lq(struct iwl_priv *priv, struct ieee80211_conf *conf, struct ieee80211_sta *sta, @@ -2359,10 +2377,6 @@ static void rs_initialize_lq(struct iwl_priv *priv, i = lq_sta->last_txrate_idx; - if ((lq_sta->lq.sta_id == 0xff) && - (priv->iw_mode == NL80211_IFTYPE_ADHOC)) - goto out; - valid_tx_ant = priv->hw_params.valid_tx_ant; if (!lq_sta->search_better_tbl) @@ -2390,7 +2404,8 @@ static void rs_initialize_lq(struct iwl_priv *priv, tbl->current_rate = rate; rs_set_expected_tpt_table(lq_sta, tbl); rs_fill_link_cmd(NULL, lq_sta, rate); - iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC); + priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; + iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_SYNC, true); out: return; } @@ -2402,9 +2417,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb = txrc->skb; struct ieee80211_supported_band *sband = txrc->sband; struct iwl_priv *priv = (struct iwl_priv *)priv_r; - struct ieee80211_conf *conf = &priv->hw->conf; - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct iwl_lq_sta *lq_sta = priv_sta; int rate_idx; @@ -2422,30 +2434,18 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, lq_sta->max_rate_idx = -1; } + /* Treat uninitialized rate scaling data same as non-existing. */ + if (lq_sta && !lq_sta->drv) { + IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); + priv_sta = NULL; + } + /* Send management frames and NO_ACK data using lowest rate. */ if (rate_control_send_low(sta, priv_sta, txrc)) return; rate_idx = lq_sta->last_txrate_idx; - if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) && - !lq_sta->ibss_sta_added) { - u8 sta_id = iwl_find_station(priv, hdr->addr1); - - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", - hdr->addr1); - sta_id = iwl_add_station(priv, hdr->addr1, - false, CMD_ASYNC, ht_cap); - } - if ((sta_id != IWL_INVALID_STATION)) { - lq_sta->lq.sta_id = sta_id; - lq_sta->lq.rs_table[0].rate_n_flags = 0; - lq_sta->ibss_sta_added = 1; - rs_initialize_lq(priv, conf, sta, lq_sta); - } - } - if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { rate_idx -= IWL_FIRST_OFDM_RATE; /* 6M and 9M shared same MCS index */ @@ -2495,16 +2495,25 @@ static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta, return lq_sta; } -static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, - struct ieee80211_sta *sta, void *priv_sta) +/* + * Called after adding a new station to initialize rate scaling + */ +void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id) { int i, j; - struct iwl_priv *priv = (struct iwl_priv *)priv_r; + struct ieee80211_hw *hw = priv->hw; struct ieee80211_conf *conf = &priv->hw->conf; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - struct iwl_lq_sta *lq_sta = priv_sta; + struct iwl_station_priv *sta_priv; + struct iwl_lq_sta *lq_sta; + struct ieee80211_supported_band *sband; + + sta_priv = (struct iwl_station_priv *) sta->drv_priv; + lq_sta = &sta_priv->lq_sta; + sband = hw->wiphy->bands[conf->channel->band]; + - lq_sta->lq.sta_id = 0xff; + lq_sta->lq.sta_id = sta_id; for (j = 0; j < LQ_SIZE; j++) for (i = 0; i < IWL_RATE_COUNT; i++) @@ -2516,33 +2525,13 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband, for (i = 0; i < IWL_RATE_COUNT; i++) rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); - IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init ***\n"); + IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n", + sta_id); /* TODO: what is a good starting rate for STA? About middle? Maybe not * the lowest or the highest rate.. Could consider using RSSI from * previous packets? Need to have IEEE 802.1X auth succeed immediately * after assoc.. */ - lq_sta->ibss_sta_added = 0; - if (priv->iw_mode == NL80211_IFTYPE_AP) { - u8 sta_id = iwl_find_station(priv, - sta->addr); - - /* for IBSS the call are from tasklet */ - IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", sta->addr); - - if (sta_id == IWL_INVALID_STATION) { - IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", sta->addr); - sta_id = iwl_add_station(priv, sta->addr, false, - CMD_ASYNC, ht_cap); - } - if ((sta_id != IWL_INVALID_STATION)) { - lq_sta->lq.sta_id = sta_id; - lq_sta->lq.rs_table[0].rate_n_flags = 0; - } - /* FIXME: this is w/a remove it later */ - priv->assoc_station_added = 1; - } - lq_sta->is_dup = 0; lq_sta->max_rate_idx = -1; lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; @@ -2795,7 +2784,7 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, if (lq_sta->dbg_fixed_rate) { rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); - iwl_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC); + iwl_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false); } return count; @@ -2992,12 +2981,21 @@ static void rs_remove_debugfs(void *priv, void *priv_sta) } #endif +/* + * Initialization of rate scaling information is done by driver after + * the station is added. Since mac80211 calls this function before a + * station is added we ignore it. + */ +static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta) +{ +} static struct rate_control_ops rs_ops = { .module = NULL, .name = RS_NAME, .tx_status = rs_tx_status, .get_rate = rs_get_rate, - .rate_init = rs_rate_init, + .rate_init = rs_rate_init_stub, .alloc = rs_alloc, .free = rs_free, .alloc_sta = rs_alloc_sta, diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h index e182f5a0f736..8292f6d48ec6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h @@ -403,7 +403,6 @@ struct iwl_lq_sta { u8 is_green; u8 is_dup; enum ieee80211_band band; - u8 ibss_sta_added; /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ u32 supp_rates; @@ -478,6 +477,12 @@ static inline u8 iwl3945_get_prev_ieee_rate(u8 rate_index) */ extern void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); +/* Initialize station's rate scaling information after adding station */ +extern void iwl_rs_rate_init(struct iwl_priv *priv, + struct ieee80211_sta *sta, u8 sta_id); +extern void iwl3945_rs_rate_init(struct iwl_priv *priv, + struct ieee80211_sta *sta, u8 sta_id); + /** * iwl_rate_control_register - Register the rate control algorithm callbacks * diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index efee4e39d282..0a376f720d78 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -144,9 +144,6 @@ int iwl_commit_rxon(struct iwl_priv *priv) return 0; } - /* station table will be cleared */ - priv->assoc_station_added = 0; - /* If we are currently associated and the new config requires * an RXON_ASSOC and the new config wants the associated mask enabled, * we must clear the associated from the active configuration @@ -166,6 +163,8 @@ int iwl_commit_rxon(struct iwl_priv *priv) IWL_ERR(priv, "Error clearing ASSOC_MSK (%d)\n", ret); return ret; } + iwl_clear_ucode_stations(priv, false); + iwl_restore_stations(priv); } IWL_DEBUG_INFO(priv, "Sending RXON\n" @@ -179,9 +178,8 @@ int iwl_commit_rxon(struct iwl_priv *priv) iwl_set_rxon_hwcrypto(priv, !priv->cfg->mod_params->sw_crypto); /* Apply the new configuration - * RXON unassoc clears the station table in uCode, send it before - * we add the bcast station. If assoc bit is set, we will send RXON - * after having added the bcast and bssid station. + * RXON unassoc clears the station table in uCode so restoration of + * stations is needed after it (the RXON command) completes */ if (!new_assoc) { ret = iwl_send_cmd_pdu(priv, REPLY_RXON, @@ -190,35 +188,14 @@ int iwl_commit_rxon(struct iwl_priv *priv) IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); return ret; } + IWL_DEBUG_INFO(priv, "Return from !new_assoc RXON. \n"); memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); + iwl_clear_ucode_stations(priv, false); + iwl_restore_stations(priv); } - iwl_clear_stations_table(priv); - priv->start_calib = 0; - - /* Add the broadcast address so we can send broadcast frames */ - priv->cfg->ops->lib->add_bcast_station(priv); - - - /* If we have set the ASSOC_MSK and we are in BSS mode then - * add the IWL_AP_ID to the station rate table */ if (new_assoc) { - if (priv->iw_mode == NL80211_IFTYPE_STATION) { - ret = iwl_rxon_add_station(priv, - priv->active_rxon.bssid_addr, 1); - if (ret == IWL_INVALID_STATION) { - IWL_ERR(priv, - "Error adding AP address for TX.\n"); - return -EIO; - } - priv->assoc_station_added = 1; - if (priv->default_wep_key && - iwl_send_static_wepkey_cmd(priv, 0)) - IWL_ERR(priv, - "Could not send WEP static key.\n"); - } - /* * allow CTS-to-self if possible for new association. * this is relevant only for 5000 series and up, @@ -2087,7 +2064,6 @@ static void iwl_alive_start(struct iwl_priv *priv) goto restart; } - iwl_clear_stations_table(priv); ret = priv->cfg->ops->lib->alive_notify(priv); if (ret) { IWL_WARN(priv, @@ -2098,6 +2074,13 @@ static void iwl_alive_start(struct iwl_priv *priv) /* After the ALIVE response, we can send host commands to the uCode */ set_bit(STATUS_ALIVE, &priv->status); + if (priv->cfg->ops->lib->recover_from_tx_stall) { + /* Enable timer to monitor the driver queues */ + mod_timer(&priv->monitor_recover, + jiffies + + msecs_to_jiffies(priv->cfg->monitor_recover_period)); + } + if (iwl_is_rfkill(priv)) return; @@ -2143,6 +2126,8 @@ static void iwl_alive_start(struct iwl_priv *priv) wake_up_interruptible(&priv->wait_command_queue); iwl_power_update_mode(priv, true); + IWL_DEBUG_INFO(priv, "Updated power mode\n"); + return; @@ -2162,7 +2147,7 @@ static void __iwl_down(struct iwl_priv *priv) if (!exit_pending) set_bit(STATUS_EXIT_PENDING, &priv->status); - iwl_clear_stations_table(priv); + iwl_clear_ucode_stations(priv, true); /* Unblock any waiting calls */ wake_up_interruptible_all(&priv->wait_command_queue); @@ -2359,8 +2344,6 @@ static int __iwl_up(struct iwl_priv *priv) for (i = 0; i < MAX_HW_RESTARTS; i++) { - iwl_clear_stations_table(priv); - /* load bootstrap state machine, * load bootstrap program into processor's memory, * prepare to load the "initialize" uCode */ @@ -2501,10 +2484,6 @@ void iwl_post_associate(struct iwl_priv *priv) return; } - IWL_DEBUG_ASSOC(priv, "Associated as %d to: %pM\n", - priv->assoc_id, priv->active_rxon.bssid_addr); - - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; @@ -2556,6 +2535,9 @@ void iwl_post_associate(struct iwl_priv *priv) iwlcore_commit_rxon(priv); + IWL_DEBUG_ASSOC(priv, "Associated as %d to: %pM\n", + priv->assoc_id, priv->active_rxon.bssid_addr); + switch (priv->iw_mode) { case NL80211_IFTYPE_STATION: break; @@ -2565,7 +2547,7 @@ void iwl_post_associate(struct iwl_priv *priv) /* assume default assoc id */ priv->assoc_id = 1; - iwl_rxon_add_station(priv, priv->bssid, 0); + iwl_add_local_station(priv, priv->bssid, true); iwl_send_beacon_cmd(priv); break; @@ -2576,9 +2558,6 @@ void iwl_post_associate(struct iwl_priv *priv) break; } - if (priv->iw_mode == NL80211_IFTYPE_ADHOC) - priv->assoc_station_added = 1; - spin_lock_irqsave(&priv->lock, flags); iwl_activate_qos(priv, 0); spin_unlock_irqrestore(&priv->lock, flags); @@ -2937,10 +2916,21 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw, return ret; case IEEE80211_AMPDU_TX_START: IWL_DEBUG_HT(priv, "start Tx\n"); - return iwl_tx_agg_start(priv, sta->addr, tid, ssn); + ret = iwl_tx_agg_start(priv, sta->addr, tid, ssn); + if (ret == 0) { + priv->_agn.agg_tids_count++; + IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n", + priv->_agn.agg_tids_count); + } + return ret; case IEEE80211_AMPDU_TX_STOP: IWL_DEBUG_HT(priv, "stop Tx\n"); ret = iwl_tx_agg_stop(priv, sta->addr, tid); + if ((ret == 0) && (priv->_agn.agg_tids_count > 0)) { + priv->_agn.agg_tids_count--; + IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n", + priv->_agn.agg_tids_count); + } if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return 0; else @@ -2977,18 +2967,7 @@ static void iwl_mac_sta_notify(struct ieee80211_hw *hw, struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; int sta_id; - /* - * TODO: We really should use this callback to - * actually maintain the station table in - * the device. - */ - switch (cmd) { - case STA_NOTIFY_ADD: - atomic_set(&sta_priv->pending_frames, 0); - if (vif->type == NL80211_IFTYPE_AP) - sta_priv->client = true; - break; case STA_NOTIFY_SLEEP: WARN_ON(!sta_priv->client); sta_priv->asleep = true; @@ -3009,6 +2988,55 @@ static void iwl_mac_sta_notify(struct ieee80211_hw *hw, } } +/** + * iwl_restore_wepkeys - Restore WEP keys to device + */ +static void iwl_restore_wepkeys(struct iwl_priv *priv) +{ + mutex_lock(&priv->mutex); + if (priv->iw_mode == NL80211_IFTYPE_STATION && + priv->default_wep_key && + iwl_send_static_wepkey_cmd(priv, 0)) + IWL_ERR(priv, "Could not send WEP static key\n"); + mutex_unlock(&priv->mutex); +} + +static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION; + int ret; + u8 sta_id; + + IWL_DEBUG_INFO(priv, "received request to add station %pM\n", + sta->addr); + + atomic_set(&sta_priv->pending_frames, 0); + if (vif->type == NL80211_IFTYPE_AP) + sta_priv->client = true; + + ret = iwl_add_station_common(priv, sta->addr, is_ap, &sta->ht_cap, + &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM (%d)\n", + sta->addr, ret); + /* Should we return success if return code is EEXIST ? */ + return ret; + } + + iwl_restore_wepkeys(priv); + + /* Initialize rate scaling */ + IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM \n", + sta->addr); + iwl_rs_rate_init(priv, sta, sta_id); + + return ret; +} + /***************************************************************************** * * sysfs attributes @@ -3214,6 +3242,13 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) priv->ucode_trace.data = (unsigned long)priv; priv->ucode_trace.function = iwl_bg_ucode_trace; + if (priv->cfg->ops->lib->recover_from_tx_stall) { + init_timer(&priv->monitor_recover); + priv->monitor_recover.data = (unsigned long)priv; + priv->monitor_recover.function = + priv->cfg->ops->lib->recover_from_tx_stall; + } + if (!priv->cfg->use_isr_legacy) tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) iwl_irq_tasklet, (unsigned long)priv); @@ -3233,6 +3268,8 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv) cancel_work_sync(&priv->beacon_update); del_timer_sync(&priv->statistics_periodic); del_timer_sync(&priv->ucode_trace); + if (priv->cfg->ops->lib->recover_from_tx_stall) + del_timer_sync(&priv->monitor_recover); } static void iwl_init_hw_rates(struct iwl_priv *priv, @@ -3270,9 +3307,6 @@ static int iwl_init_drv(struct iwl_priv *priv) mutex_init(&priv->mutex); mutex_init(&priv->sync_cmd_mutex); - /* Clear the driver's (not device's) station table */ - iwl_clear_stations_table(priv); - priv->ieee_channels = NULL; priv->ieee_rates = NULL; priv->band = IEEE80211_BAND_2GHZ; @@ -3280,6 +3314,7 @@ static int iwl_init_drv(struct iwl_priv *priv) priv->iw_mode = NL80211_IFTYPE_STATION; priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; + priv->_agn.agg_tids_count = 0; /* initialize force reset */ priv->force_reset[IWL_RF_RESET].reset_duration = @@ -3365,6 +3400,8 @@ static struct ieee80211_ops iwl_hw_ops = { .ampdu_action = iwl_mac_ampdu_action, .hw_scan = iwl_mac_hw_scan, .sta_notify = iwl_mac_sta_notify, + .sta_add = iwlagn_mac_sta_add, + .sta_remove = iwl_mac_sta_remove, }; static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -3468,7 +3505,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); iwl_hw_detect(priv); - IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s REV=0x%X\n", + IWL_INFO(priv, "Detected %s, REV=0x%X\n", priv->cfg->name, priv->hw_rev); /* We disable the RETRY_TIMEOUT register (0x41) to keep @@ -3649,7 +3686,6 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) iwl_rx_queue_free(priv, &priv->rxq); iwl_hw_txq_ctx_free(priv); - iwl_clear_stations_table(priv); iwl_eeprom_free(priv); diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index ec435e5491d9..5180fb24cd38 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -2283,8 +2283,6 @@ static int iwl_set_mode(struct iwl_priv *priv, struct ieee80211_vif *vif) memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN); - iwl_clear_stations_table(priv); - return iwlcore_commit_rxon(priv); } @@ -2317,6 +2315,10 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) err = iwl_set_mode(priv, vif); if (err) goto out_err; + + /* Add the broadcast address so we can send broadcast frames */ + priv->cfg->ops->lib->add_bcast_station(priv); + goto out; out_err: @@ -2339,6 +2341,8 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw, mutex_lock(&priv->mutex); + iwl_clear_ucode_stations(priv, true); + if (iwl_is_ready_rf(priv)) { iwl_scan_cancel_timeout(priv, 100); priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; @@ -2526,7 +2530,6 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw) spin_lock_irqsave(&priv->lock, flags); priv->assoc_id = 0; priv->assoc_capability = 0; - priv->assoc_station_added = 0; /* new association get rid of ibss beacon skb */ if (priv->ibss_beacon) @@ -3048,6 +3051,99 @@ int iwl_force_reset(struct iwl_priv *priv, int mode) } return 0; } +EXPORT_SYMBOL(iwl_force_reset); + +/** + * iwl_bg_monitor_recover - Timer callback to check for stuck queue and recover + * + * During normal condition (no queue is stuck), the timer is continually set to + * execute every monitor_recover_period milliseconds after the last timer + * expired. When the queue read_ptr is at the same place, the timer is + * shorten to 100mSecs. This is + * 1) to reduce the chance that the read_ptr may wrap around (not stuck) + * 2) to detect the stuck queues quicker before the station and AP can + * disassociate each other. + * + * This function monitors all the tx queues and recover from it if any + * of the queues are stuck. + * 1. It first check the cmd queue for stuck conditions. If it is stuck, + * it will recover by resetting the firmware and return. + * 2. Then, it checks for station association. If it associates it will check + * other queues. If any queue is stuck, it will recover by resetting + * the firmware. + * Note: It the number of times the queue read_ptr to be at the same place to + * be MAX_REPEAT+1 in order to consider to be stuck. + */ +/* + * The maximum number of times the read pointer of the tx queue at the + * same place without considering to be stuck. + */ +#define MAX_REPEAT (2) +static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt) +{ + struct iwl_tx_queue *txq; + struct iwl_queue *q; + + txq = &priv->txq[cnt]; + q = &txq->q; + /* queue is empty, skip */ + if (q->read_ptr != q->write_ptr) { + if (q->read_ptr == q->last_read_ptr) { + /* a queue has not been read from last time */ + if (q->repeat_same_read_ptr > MAX_REPEAT) { + IWL_ERR(priv, + "queue %d stuck %d time. Fw reload.\n", + q->id, q->repeat_same_read_ptr); + q->repeat_same_read_ptr = 0; + iwl_force_reset(priv, IWL_FW_RESET); + } else { + q->repeat_same_read_ptr++; + IWL_DEBUG_RADIO(priv, + "queue %d, not read %d time\n", + q->id, + q->repeat_same_read_ptr); + mod_timer(&priv->monitor_recover, jiffies + + msecs_to_jiffies(IWL_ONE_HUNDRED_MSECS)); + } + return 1; + } else { + q->last_read_ptr = q->read_ptr; + q->repeat_same_read_ptr = 0; + } + } + return 0; +} + +void iwl_bg_monitor_recover(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + int cnt; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* monitor and check for stuck cmd queue */ + if (iwl_check_stuck_queue(priv, IWL_CMD_QUEUE_NUM)) + return; + + /* monitor and check for other stuck queues */ + if (iwl_is_associated(priv)) { + for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { + /* skip as we already checked the command queue */ + if (cnt == IWL_CMD_QUEUE_NUM) + continue; + if (iwl_check_stuck_queue(priv, cnt)) + return; + } + } + /* + * Reschedule the timer to occur in + * priv->cfg->monitor_recover_period + */ + mod_timer(&priv->monitor_recover, + jiffies + msecs_to_jiffies(priv->cfg->monitor_recover_period)); +} +EXPORT_SYMBOL(iwl_bg_monitor_recover); #ifdef CONFIG_PM diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index aced12f1611e..b3e698b576e1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -191,6 +191,14 @@ struct iwl_lib_ops { struct iwl_temp_ops temp_ops; /* station management */ void (*add_bcast_station)(struct iwl_priv *priv); + /* recover from tx queue stall */ + void (*recover_from_tx_stall)(unsigned long data); + /* check for plcp health */ + bool (*check_plcp_health)(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); + /* check for ack health */ + bool (*check_ack_health)(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); }; struct iwl_led_ops { @@ -295,6 +303,8 @@ struct iwl_cfg { const bool support_wimax_coexist; u8 plcp_delta_threshold; s32 chain_noise_scale; + /* timer period for monitor the driver queues */ + u32 monitor_recover_period; }; /*************************** @@ -430,6 +440,10 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); +bool iwl_good_plcp_health(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); +bool iwl_good_ack_health(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); void iwl_reply_statistics(struct iwl_priv *priv, @@ -568,6 +582,9 @@ static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv) pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl); return pci_lnk_ctl; } + +void iwl_bg_monitor_recover(unsigned long data); + #ifdef CONFIG_PM int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state); int iwl_pci_resume(struct pci_dev *pdev); @@ -667,7 +684,7 @@ extern int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear); extern int iwl_verify_ucode(struct iwl_priv *priv); extern int iwl_send_lq_cmd(struct iwl_priv *priv, - struct iwl_link_quality_cmd *lq, u8 flags); + struct iwl_link_quality_cmd *lq, u8 flags, bool init); extern void iwl_rx_reply_rx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 2e4d47c7139b..e847e6197a3d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -183,6 +183,10 @@ struct iwl_queue { int n_bd; /* number of BDs in this queue */ int write_ptr; /* 1-st empty entry (index) host_w*/ int read_ptr; /* last used entry (index) host_r*/ + /* use for monitoring and recovering the stuck queue */ + int last_read_ptr; /* storing the last read_ptr */ + /* number of time read_ptr and last_read_ptr are the same */ + u8 repeat_same_read_ptr; dma_addr_t dma_addr; /* physical addr for BD's */ int n_window; /* safe queue window */ u32 id; @@ -544,11 +548,18 @@ struct iwl_qos_info { struct iwl_qosparam_cmd def_qos_parm; }; +/* + * Structure should be accessed with sta_lock held. When station addition + * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only + * the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock + * held. + */ struct iwl_station_entry { struct iwl_addsta_cmd sta; struct iwl_tid_data tid[MAX_TID_COUNT]; u8 used; struct iwl_hw_key keyinfo; + struct iwl_link_quality_cmd *lq; }; /* @@ -1037,6 +1048,11 @@ struct iwl_event_log { #define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3) #define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) +/* timer constants use to monitor and recover stuck tx queues in mSecs */ +#define IWL_MONITORING_PERIOD (1000) +#define IWL_ONE_HUNDRED_MSECS (100) +#define IWL_SIXTY_SECS (60000) + enum iwl_reset { IWL_RF_RESET = 0, IWL_FW_RESET, @@ -1163,7 +1179,6 @@ struct iwl_priv { u16 active_rate; - u8 assoc_station_added; u8 start_calib; struct iwl_sensitivity_data sensitivity_data; struct iwl_chain_noise_data chain_noise_data; @@ -1285,6 +1300,11 @@ struct iwl_priv { int ict_index; u32 inta; bool use_ict; + /* + * reporting the number of tids has AGG on. 0 means + * no AGGREGATION + */ + u8 agg_tids_count; } _agn; #endif }; @@ -1348,6 +1368,7 @@ struct iwl_priv { struct work_struct run_time_calib_work; struct timer_list statistics_periodic; struct timer_list ucode_trace; + struct timer_list monitor_recover; bool hw_ready; struct iwl_event_log event_log; diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index 8116aa0d7678..2fa30dfb7c59 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -616,29 +616,77 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, #define REG_RECALIB_PERIOD (60) -#define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n" -void iwl_rx_statistics(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */ +#define ACK_CNT_RATIO (50) +#define BA_TIMEOUT_CNT (5) +#define BA_TIMEOUT_MAX (16) + +#if defined(CONFIG_IWLAGN) || defined(CONFIG_IWLAGN_MODULE) +/** + * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries. + * + * When the ACK count ratio is 0 and aggregated BA timeout retries exceeding + * the BA_TIMEOUT_MAX, reload firmware and bring system back to normal + * operation state. + */ +bool iwl_good_ack_health(struct iwl_priv *priv, + struct iwl_rx_packet *pkt) { - int change; - struct iwl_rx_packet *pkt = rxb_addr(rxb); + bool rc = true; + int actual_ack_cnt_delta, expected_ack_cnt_delta; + int ba_timeout_delta; + + actual_ack_cnt_delta = + le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) - + le32_to_cpu(priv->statistics.tx.actual_ack_cnt); + expected_ack_cnt_delta = + le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) - + le32_to_cpu(priv->statistics.tx.expected_ack_cnt); + ba_timeout_delta = + le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) - + le32_to_cpu(priv->statistics.tx.agg.ba_timeout); + if ((priv->_agn.agg_tids_count > 0) && + (expected_ack_cnt_delta > 0) && + (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta) + < ACK_CNT_RATIO) && + (ba_timeout_delta > BA_TIMEOUT_CNT)) { + IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d," + " expected_ack_cnt = %d\n", + actual_ack_cnt_delta, expected_ack_cnt_delta); + +#ifdef CONFIG_IWLWIFI_DEBUG + IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta = %d\n", + priv->delta_statistics.tx.rx_detected_cnt); + IWL_DEBUG_RADIO(priv, + "ack_or_ba_timeout_collision delta = %d\n", + priv->delta_statistics.tx. + ack_or_ba_timeout_collision); +#endif + IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n", + ba_timeout_delta); + if (!actual_ack_cnt_delta && + (ba_timeout_delta >= BA_TIMEOUT_MAX)) + rc = false; + } + return rc; +} +EXPORT_SYMBOL(iwl_good_ack_health); +#endif + +/** + * iwl_good_plcp_health - checks for plcp error. + * + * When the plcp error is exceeding the thresholds, reset the radio + * to improve the throughput. + */ +bool iwl_good_plcp_health(struct iwl_priv *priv, + struct iwl_rx_packet *pkt) +{ + bool rc = true; int combined_plcp_delta; unsigned int plcp_msec; unsigned long plcp_received_jiffies; - IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", - (int)sizeof(priv->statistics), - le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); - - change = ((priv->statistics.general.temperature != - pkt->u.stats.general.temperature) || - ((priv->statistics.flag & - STATISTICS_REPLY_FLG_HT40_MODE_MSK) != - (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); - -#ifdef CONFIG_IWLWIFI_DEBUG - iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); -#endif /* * check for plcp_err and trigger radio reset if it exceeds * the plcp error threshold plcp_delta. @@ -659,11 +707,11 @@ void iwl_rx_statistics(struct iwl_priv *priv, le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err)); if ((combined_plcp_delta > 0) && - ((combined_plcp_delta * 100) / plcp_msec) > + ((combined_plcp_delta * 100) / plcp_msec) > priv->cfg->plcp_delta_threshold) { /* - * if plcp_err exceed the threshold, the following - * data is printed in csv format: + * if plcp_err exceed the threshold, + * the following data is printed in csv format: * Text: plcp_err exceeded %d, * Received ofdm.plcp_err, * Current ofdm.plcp_err, @@ -672,22 +720,73 @@ void iwl_rx_statistics(struct iwl_priv *priv, * combined_plcp_delta, * plcp_msec */ - IWL_DEBUG_RADIO(priv, PLCP_MSG, + IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, " + "%u, %u, %u, %u, %d, %u mSecs\n", priv->cfg->plcp_delta_threshold, le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err), le32_to_cpu(priv->statistics.rx.ofdm.plcp_err), le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err), le32_to_cpu( - priv->statistics.rx.ofdm_ht.plcp_err), + priv->statistics.rx.ofdm_ht.plcp_err), combined_plcp_delta, plcp_msec); + rc = false; + } + } + return rc; +} +EXPORT_SYMBOL(iwl_good_plcp_health); - /* - * Reset the RF radio due to the high plcp - * error rate - */ - iwl_force_reset(priv, IWL_RF_RESET); +static void iwl_recover_from_statistics(struct iwl_priv *priv, + struct iwl_rx_packet *pkt) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + if (iwl_is_associated(priv)) { + if (priv->cfg->ops->lib->check_ack_health) { + if (!priv->cfg->ops->lib->check_ack_health( + priv, pkt)) { + /* + * low ack count detected + * restart Firmware + */ + IWL_ERR(priv, "low ack count detected, " + "restart firmware\n"); + iwl_force_reset(priv, IWL_FW_RESET); + } + } else if (priv->cfg->ops->lib->check_plcp_health) { + if (!priv->cfg->ops->lib->check_plcp_health( + priv, pkt)) { + /* + * high plcp error detected + * reset Radio + */ + iwl_force_reset(priv, IWL_RF_RESET); + } } } +} + +void iwl_rx_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + int change; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + + IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", + (int)sizeof(priv->statistics), + le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); + + change = ((priv->statistics.general.temperature != + pkt->u.stats.general.temperature) || + ((priv->statistics.flag & + STATISTICS_REPLY_FLG_HT40_MODE_MSK) != + (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK))); + +#ifdef CONFIG_IWLWIFI_DEBUG + iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); +#endif + iwl_recover_from_statistics(priv, pkt); memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics)); diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index b1aad306efa9..d401b6f226f9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -29,14 +29,12 @@ #include <net/mac80211.h> #include <linux/etherdevice.h> +#include <linux/sched.h> #include "iwl-dev.h" #include "iwl-core.h" #include "iwl-sta.h" -#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ -#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ - u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr) { int i; @@ -64,6 +62,19 @@ u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr) addr, priv->num_stations); out: + /* + * It may be possible that more commands interacting with stations + * arrive before we completed processing the adding of + * station + */ + if (ret != IWL_INVALID_STATION && + (!(priv->stations[ret].used & IWL_STA_UCODE_ACTIVE) || + ((priv->stations[ret].used & IWL_STA_UCODE_ACTIVE) && + (priv->stations[ret].used & IWL_STA_UCODE_INPROGRESS)))) { + IWL_ERR(priv, "Requested station info for sta %d before ready. \n", + ret); + ret = IWL_INVALID_STATION; + } spin_unlock_irqrestore(&priv->sta_lock, flags); return ret; } @@ -158,13 +169,6 @@ static void iwl_process_add_sta_resp(struct iwl_priv *priv, priv->stations[sta_id].sta.mode == STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", addsta->sta.addr); - - /* - * Determine if we wanted to modify or add a station, - * if adding a station succeeded we have some more initialization - * to do when using station notification. TODO - */ - spin_unlock_irqrestore(&priv->sta_lock, flags); } @@ -190,6 +194,10 @@ int iwl_send_add_sta(struct iwl_priv *priv, .flags = flags, .data = data, }; + u8 sta_id = sta->sta.sta_id; + + IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", + sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); if (flags & CMD_ASYNC) cmd.callback = iwl_add_sta_callback; @@ -263,18 +271,19 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, } /** - * iwl_add_station - Add station to tables in driver and device + * iwl_prep_station - Prepare station information for addition + * + * should be called with sta_lock held */ -u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags, - struct ieee80211_sta_ht_cap *ht_info) +static u8 iwl_prep_station(struct iwl_priv *priv, const u8 *addr, + bool is_ap, + struct ieee80211_sta_ht_cap *ht_info) { struct iwl_station_entry *station; - unsigned long flags_spin; int i; - int sta_id = IWL_INVALID_STATION; + u8 sta_id = IWL_INVALID_STATION; u16 rate; - spin_lock_irqsave(&priv->sta_lock, flags_spin); if (is_ap) sta_id = IWL_AP_ID; else if (is_broadcast_ether_addr(addr)) @@ -292,20 +301,32 @@ u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags, sta_id = i; } - /* These two conditions have the same outcome, but keep them separate - since they have different meanings */ - if (unlikely(sta_id == IWL_INVALID_STATION)) { - spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + /* + * These two conditions have the same outcome, but keep them + * separate + */ + if (unlikely(sta_id == IWL_INVALID_STATION)) + return sta_id; + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { + IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n", + sta_id); return sta_id; } - if (priv->stations[sta_id].used && + if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && + (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && !compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) { - spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n", + sta_id, addr); return sta_id; } - station = &priv->stations[sta_id]; station->used = IWL_STA_DRIVER_ACTIVE; IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", @@ -330,86 +351,185 @@ u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags, /* Turn on both antennas for the station... */ station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); + return sta_id; + +} + +#define STA_WAIT_TIMEOUT (HZ/2) + +/** + * iwl_add_station_common - + */ +int iwl_add_station_common(struct iwl_priv *priv, const u8 *addr, + bool is_ap, + struct ieee80211_sta_ht_cap *ht_info, + u8 *sta_id_r) +{ + struct iwl_station_entry *station; + unsigned long flags_spin; + int ret = 0; + u8 sta_id; + + *sta_id_r = 0; + spin_lock_irqsave(&priv->sta_lock, flags_spin); + sta_id = iwl_prep_station(priv, addr, is_ap, ht_info); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Unable to prepare station %pM for addition\n", + addr); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return -EINVAL; + } + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { + IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n", + sta_id); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return -EEXIST; + } + + if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && + (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n", + sta_id, addr); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return -EEXIST; + } + + priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; + station = &priv->stations[sta_id]; spin_unlock_irqrestore(&priv->sta_lock, flags_spin); /* Add station to device's station table */ - iwl_send_add_sta(priv, &station->sta, flags); - return sta_id; - + ret = iwl_send_add_sta(priv, &station->sta, CMD_SYNC); + if (ret) { + IWL_ERR(priv, "Adding station %pM failed.\n", station->sta.sta.addr); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + } + *sta_id_r = sta_id; + return ret; } -EXPORT_SYMBOL(iwl_add_station); +EXPORT_SYMBOL(iwl_add_station_common); -static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, const u8 *addr) +static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, bool is_ap) { - unsigned long flags; - u8 sta_id = iwl_find_station(priv, addr); + int i, r; + struct iwl_link_quality_cmd link_cmd = { + .reserved1 = 0, + }; + u32 rate_flags; - BUG_ON(sta_id == IWL_INVALID_STATION); + /* Set up the rate scaling to start at selected rate, fall back + * all the way down to 1M in IEEE order, and then spin on 1M */ + if (is_ap) + r = IWL_RATE_54M_INDEX; + else if (priv->band == IEEE80211_BAND_5GHZ) + r = IWL_RATE_6M_INDEX; + else + r = IWL_RATE_1M_INDEX; + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + rate_flags = 0; + if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; - IWL_DEBUG_ASSOC(priv, "Removed STA from Ucode: %pM\n", addr); + rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << + RATE_MCS_ANT_POS; - spin_lock_irqsave(&priv->sta_lock, flags); + link_cmd.rs_table[i].rate_n_flags = + iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); + r = iwl_get_prev_ieee_rate(r); + } - /* Ucode must be active and driver must be non active */ - if (priv->stations[sta_id].used != IWL_STA_UCODE_ACTIVE) - IWL_ERR(priv, "removed non active STA %d\n", sta_id); + link_cmd.general_params.single_stream_ant_msk = + first_antenna(priv->hw_params.valid_tx_ant); + link_cmd.general_params.dual_stream_ant_msk = 3; + link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + link_cmd.agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; + /* Update the rate scaling for control frame Tx to AP */ + link_cmd.sta_id = is_ap ? IWL_AP_ID : priv->hw_params.bcast_sta_id; - memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); - spin_unlock_irqrestore(&priv->sta_lock, flags); + iwl_send_cmd_pdu(priv, REPLY_TX_LINK_QUALITY_CMD, + sizeof(link_cmd), &link_cmd); } -static void iwl_remove_sta_callback(struct iwl_priv *priv, - struct iwl_device_cmd *cmd, - struct iwl_rx_packet *pkt) +/* + * iwl_add_local_stations - Add stations not requested by mac80211 + * + * This will be either the broadcast station or the bssid station needed by + * ad-hoc. + * + * Function sleeps. + */ +int iwl_add_local_station(struct iwl_priv *priv, const u8 *addr, bool init_rs) { - struct iwl_rem_sta_cmd *rm_sta = - (struct iwl_rem_sta_cmd *)cmd->cmd.payload; - const u8 *addr = rm_sta->addr; + int ret; + u8 sta_id; - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", - pkt->hdr.flags); - return; + ret = iwl_add_station_common(priv, addr, 0, NULL, &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM\n", addr); + return ret; } - switch (pkt->u.rem_sta.status) { - case REM_STA_SUCCESS_MSK: - iwl_sta_ucode_deactivate(priv, addr); - break; - default: - IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); - break; - } + if (init_rs) + /* Set up default rate scaling table in device's station table */ + iwl_sta_init_lq(priv, addr, false); + return 0; +} +EXPORT_SYMBOL(iwl_add_local_station); + +/** + * iwl_sta_ucode_deactivate - deactivate ucode status for a station + * + * priv->sta_lock must be held + */ +static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) +{ + /* Ucode must be active and driver must be non active */ + if (priv->stations[sta_id].used != IWL_STA_UCODE_ACTIVE) + IWL_ERR(priv, "removed non active STA %u\n", sta_id); + + priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; + + memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); + IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); } -static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, - u8 flags) +static int iwl_send_remove_station(struct iwl_priv *priv, + struct iwl_station_entry *station) { struct iwl_rx_packet *pkt; int ret; + unsigned long flags_spin; struct iwl_rem_sta_cmd rm_sta_cmd; struct iwl_host_cmd cmd = { .id = REPLY_REMOVE_STA, .len = sizeof(struct iwl_rem_sta_cmd), - .flags = flags, + .flags = CMD_SYNC, .data = &rm_sta_cmd, }; memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); rm_sta_cmd.num_sta = 1; - memcpy(&rm_sta_cmd.addr, addr , ETH_ALEN); + memcpy(&rm_sta_cmd.addr, &station->sta.sta.addr , ETH_ALEN); + + cmd.flags |= CMD_WANT_SKB; - if (flags & CMD_ASYNC) - cmd.callback = iwl_remove_sta_callback; - else - cmd.flags |= CMD_WANT_SKB; ret = iwl_send_cmd(priv, &cmd); - if (ret || (flags & CMD_ASYNC)) + if (ret) return ret; pkt = (struct iwl_rx_packet *)cmd.reply_page; @@ -422,7 +542,9 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, if (!ret) { switch (pkt->u.rem_sta.status) { case REM_STA_SUCCESS_MSK: - iwl_sta_ucode_deactivate(priv, addr); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + iwl_sta_ucode_deactivate(priv, station->sta.sta.sta_id); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); break; default: @@ -439,23 +561,35 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, /** * iwl_remove_station - Remove driver's knowledge of station. */ -int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) +static int iwl_remove_station(struct iwl_priv *priv, struct ieee80211_sta *sta) { int sta_id = IWL_INVALID_STATION; int i, ret = -EINVAL; unsigned long flags; + bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION; + struct iwl_station_entry *station; + + if (!iwl_is_ready(priv)) { + IWL_DEBUG_INFO(priv, + "Unable to remove station %pM, device not ready. \n", + sta->addr); + /* + * It is typical for stations to be removed when we are + * going down. Return success since device will be down + * soon anyway + */ + return 0; + } spin_lock_irqsave(&priv->sta_lock, flags); if (is_ap) sta_id = IWL_AP_ID; - else if (is_broadcast_ether_addr(addr)) - sta_id = priv->hw_params.bcast_sta_id; else for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++) if (priv->stations[i].used && !compare_ether_addr(priv->stations[i].sta.sta.addr, - addr)) { + sta->addr)) { sta_id = i; break; } @@ -464,17 +598,17 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) goto out; IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", - sta_id, addr); + sta_id, sta->addr); if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { - IWL_ERR(priv, "Removing %pM but non DRIVER active\n", - addr); + IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", + sta->addr); goto out; } if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { - IWL_ERR(priv, "Removing %pM but non UCODE active\n", - addr); + IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", + sta->addr); goto out; } @@ -485,9 +619,10 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) BUG_ON(priv->num_stations < 0); + station = &priv->stations[sta_id]; spin_unlock_irqrestore(&priv->sta_lock, flags); - ret = iwl_send_remove_station(priv, addr, CMD_ASYNC); + ret = iwl_send_remove_station(priv, station); return ret; out: spin_unlock_irqrestore(&priv->sta_lock, flags); @@ -495,37 +630,122 @@ out: } /** - * iwl_clear_stations_table - Clear the driver's station table - * - * NOTE: This does not clear or otherwise alter the device's station table. + * iwl_clear_ucode_stations() - clear entire station table driver and/or ucode + * @priv: + * @force: If set then the uCode station table needs to be cleared here. If + * not set then the uCode station table has already been cleared, + * for example after sending it a RXON command without ASSOC bit + * set, and we just need to change driver state here. */ -void iwl_clear_stations_table(struct iwl_priv *priv) +void iwl_clear_ucode_stations(struct iwl_priv *priv, bool force) { - unsigned long flags; int i; + unsigned long flags_spin; + bool cleared = false; + + IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver%s\n", + force ? " and ucode" : ""); + + if (force) { + if (!iwl_is_ready(priv)) { + /* + * If device is not ready at this point the station + * table is likely already empty (uCode not ready + * to receive station requests) or will soon be + * due to interface going down. + */ + IWL_DEBUG_INFO(priv, "Unable to remove stations from device - device not ready\n"); + } else { + iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL); + } + } - spin_lock_irqsave(&priv->sta_lock, flags); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + if (force) { + IWL_DEBUG_INFO(priv, "Clearing all station information in driver\n"); + priv->num_stations = 0; + memset(priv->stations, 0, sizeof(priv->stations)); + } else { + for (i = 0; i < priv->hw_params.max_stations; i++) { + if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { + IWL_DEBUG_INFO(priv, "Clearing ucode active for station %d \n", i); + priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; + cleared = true; + } + } + } + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); - if (iwl_is_alive(priv) && - !test_bit(STATUS_EXIT_PENDING, &priv->status) && - iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL)) - IWL_ERR(priv, "Couldn't clear the station table\n"); + if (!cleared) + IWL_DEBUG_INFO(priv, "No active stations found to be cleared\n"); +} +EXPORT_SYMBOL(iwl_clear_ucode_stations); - priv->num_stations = 0; - memset(priv->stations, 0, sizeof(priv->stations)); +/** + * iwl_restore_stations() - Restore driver known stations to device + * + * All stations considered active by driver, but not present in ucode, is + * restored. + * + * Function sleeps. + */ +void iwl_restore_stations(struct iwl_priv *priv) +{ + struct iwl_station_entry *station; + unsigned long flags_spin; + int i; + bool found = false; + int ret; - /* clean ucode key table bit map */ - priv->ucode_key_table = 0; + if (!iwl_is_ready(priv)) { + IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n"); + return; + } - /* keep track of static keys */ - for (i = 0; i < WEP_KEYS_MAX ; i++) { - if (priv->wep_keys[i].key_size) - set_bit(i, &priv->ucode_key_table); + IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + for (i = 0; i < priv->hw_params.max_stations; i++) { + if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && + !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", + priv->stations[i].sta.sta.addr); + priv->stations[i].sta.mode = 0; + priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; + found = true; + } } - spin_unlock_irqrestore(&priv->sta_lock, flags); + for (i = 0; i < priv->hw_params.max_stations; i++) { + if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + station = &priv->stations[i]; + ret = iwl_send_add_sta(priv, &priv->stations[i].sta, CMD_SYNC); + if (ret) { + IWL_ERR(priv, "Adding station %pM failed.\n", + station->sta.sta.addr); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + priv->stations[i].used &= ~IWL_STA_DRIVER_ACTIVE; + priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + } + /* + * Rate scaling has already been initialized, send + * current LQ command + */ + if (station->lq) + iwl_send_lq_cmd(priv, station->lq, CMD_SYNC, true); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; + } + } + + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + if (!found) + IWL_DEBUG_INFO(priv, "Restoring all known stations .... no stations to be restored.\n"); + else + IWL_DEBUG_INFO(priv, "Restoring all known stations .... complete.\n"); } -EXPORT_SYMBOL(iwl_clear_stations_table); +EXPORT_SYMBOL(iwl_restore_stations); int iwl_get_free_ucode_key_index(struct iwl_priv *priv) { @@ -948,9 +1168,22 @@ static inline void iwl_dump_lq_cmd(struct iwl_priv *priv, } #endif +/** + * iwl_send_lq_cmd() - Send link quality command + * @init: This command is sent as part of station initialization right + * after station has been added. + * + * The link quality command is sent as the last step of station creation. + * This is the special case in which init is set and we call a callback in + * this case to clear the state indicating that station creation is in + * progress. + */ int iwl_send_lq_cmd(struct iwl_priv *priv, - struct iwl_link_quality_cmd *lq, u8 flags) + struct iwl_link_quality_cmd *lq, u8 flags, bool init) { + int ret = 0; + unsigned long flags_spin; + struct iwl_host_cmd cmd = { .id = REPLY_TX_LINK_QUALITY_CMD, .len = sizeof(struct iwl_link_quality_cmd), @@ -966,167 +1199,31 @@ int iwl_send_lq_cmd(struct iwl_priv *priv, lq->sta_id = IWL_AP_ID; iwl_dump_lq_cmd(priv, lq); + BUG_ON(init && (cmd.flags & CMD_ASYNC)); - if (iwl_is_associated(priv) && priv->assoc_station_added) - return iwl_send_cmd(priv, &cmd); + iwl_dump_lq_cmd(priv, lq); + ret = iwl_send_cmd(priv, &cmd); + if (ret || (cmd.flags & CMD_ASYNC)) + return ret; + if (init) { + IWL_DEBUG_INFO(priv, "init LQ command complete, clearing sta addition status for sta %d \n", + lq->sta_id); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + } return 0; } EXPORT_SYMBOL(iwl_send_lq_cmd); /** - * iwl_sta_init_lq - Initialize a station's hardware rate table - * - * The uCode's station table contains a table of fallback rates - * for automatic fallback during transmission. - * - * NOTE: This sets up a default set of values. These will be replaced later - * if the driver's iwl-agn-rs rate scaling algorithm is used, instead of - * rc80211_simple. - * - * NOTE: Run REPLY_ADD_STA command to set up station table entry, before - * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, - * which requires station table entry to exist). - */ -static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, bool is_ap) -{ - int i, r; - struct iwl_link_quality_cmd link_cmd = { - .reserved1 = 0, - }; - u32 rate_flags; - - /* Set up the rate scaling to start at selected rate, fall back - * all the way down to 1M in IEEE order, and then spin on 1M */ - if (is_ap) - r = IWL_RATE_54M_INDEX; - else if (priv->band == IEEE80211_BAND_5GHZ) - r = IWL_RATE_6M_INDEX; - else - r = IWL_RATE_1M_INDEX; - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - rate_flags = 0; - if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; - - rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << - RATE_MCS_ANT_POS; - - link_cmd.rs_table[i].rate_n_flags = - iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); - r = iwl_get_prev_ieee_rate(r); - } - - link_cmd.general_params.single_stream_ant_msk = - first_antenna(priv->hw_params.valid_tx_ant); - link_cmd.general_params.dual_stream_ant_msk = 3; - link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; - link_cmd.agg_params.agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - - /* Update the rate scaling for control frame Tx to AP */ - link_cmd.sta_id = is_ap ? IWL_AP_ID : priv->hw_params.bcast_sta_id; - - iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD, - sizeof(link_cmd), &link_cmd, NULL); -} - -/** - * iwl_rxon_add_station - add station into station table. - * - * there is only one AP station with id= IWL_AP_ID - * NOTE: mutex must be held before calling this function - */ -int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap) -{ - struct ieee80211_sta *sta; - struct ieee80211_sta_ht_cap ht_config; - struct ieee80211_sta_ht_cap *cur_ht_config = NULL; - u8 sta_id; - - /* - * Set HT capabilities. It is ok to set this struct even if not using - * HT config: the priv->current_ht_config.is_ht flag will just be false - */ - rcu_read_lock(); - sta = ieee80211_find_sta(priv->vif, addr); - if (sta) { - memcpy(&ht_config, &sta->ht_cap, sizeof(ht_config)); - cur_ht_config = &ht_config; - } - rcu_read_unlock(); - - /* Add station to device's station table */ - sta_id = iwl_add_station(priv, addr, is_ap, CMD_SYNC, cur_ht_config); - - /* Set up default rate scaling table in device's station table */ - iwl_sta_init_lq(priv, addr, is_ap); - - return sta_id; -} -EXPORT_SYMBOL(iwl_rxon_add_station); - -/** - * iwl_sta_init_bcast_lq - Initialize a bcast station's hardware rate table - * - * NOTE: Run REPLY_ADD_STA command to set up station table entry, before - * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, - * which requires station table entry to exist). - */ -static void iwl_sta_init_bcast_lq(struct iwl_priv *priv) -{ - int i, r; - struct iwl_link_quality_cmd link_cmd = { - .reserved1 = 0, - }; - u32 rate_flags; - - /* Set up the rate scaling to start at selected rate, fall back - * all the way down to 1M in IEEE order, and then spin on 1M */ - if (priv->band == IEEE80211_BAND_5GHZ) - r = IWL_RATE_6M_INDEX; - else - r = IWL_RATE_1M_INDEX; - - for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - rate_flags = 0; - if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) - rate_flags |= RATE_MCS_CCK_MSK; - - rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) << - RATE_MCS_ANT_POS; - - link_cmd.rs_table[i].rate_n_flags = - iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags); - r = iwl_get_prev_ieee_rate(r); - } - - link_cmd.general_params.single_stream_ant_msk = - first_antenna(priv->hw_params.valid_tx_ant); - link_cmd.general_params.dual_stream_ant_msk = 3; - link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; - link_cmd.agg_params.agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); - - /* Update the rate scaling for control frame Tx to AP */ - link_cmd.sta_id = priv->hw_params.bcast_sta_id; - - iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD, - sizeof(link_cmd), &link_cmd, NULL); -} - - -/** * iwl_add_bcast_station - add broadcast station into station table. */ void iwl_add_bcast_station(struct iwl_priv *priv) { IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n"); - iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL); - - /* Set up default rate scaling table in device's station table */ - iwl_sta_init_bcast_lq(priv); + iwl_add_local_station(priv, iwl_bcast_addr, true); } EXPORT_SYMBOL(iwl_add_bcast_station); @@ -1136,7 +1233,14 @@ EXPORT_SYMBOL(iwl_add_bcast_station); void iwl3945_add_bcast_station(struct iwl_priv *priv) { IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n"); - iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL); + iwl_add_local_station(priv, iwl_bcast_addr, false); + /* + * It is assumed that when station is added more initialization + * needs to be done, but for 3945 it is not the case and we can + * just release station table access right here. + */ + priv->stations[priv->hw_params.bcast_sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + } EXPORT_SYMBOL(iwl3945_add_bcast_station); @@ -1159,6 +1263,13 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) /* If we are a client station in a BSS network, use the special * AP station entry (that's the only station we communicate with) */ case NL80211_IFTYPE_STATION: + /* + * If addition of station not complete yet, which means + * that rate scaling has not been initialized, then return + * the broadcast station. + */ + if (!(priv->stations[IWL_AP_ID].used & IWL_STA_UCODE_ACTIVE)) + return priv->hw_params.bcast_sta_id; return IWL_AP_ID; /* If we are an AP, then find the station, or use BCAST */ @@ -1175,13 +1286,6 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) if (sta_id != IWL_INVALID_STATION) return sta_id; - /* Create new station table entry */ - sta_id = iwl_add_station(priv, hdr->addr1, false, - CMD_ASYNC, NULL); - - if (sta_id != IWL_INVALID_STATION) - return sta_id; - IWL_DEBUG_DROP(priv, "Station %pM not in station map. " "Defaulting to broadcast...\n", hdr->addr1); @@ -1291,3 +1395,19 @@ void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); } + +int iwl_mac_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + int ret; + struct iwl_priv *priv = hw->priv; + IWL_DEBUG_INFO(priv, "received request to remove station %pM\n", + sta->addr); + ret = iwl_remove_station(priv, sta); + if (ret) + IWL_ERR(priv, "Error removing station %pM\n", + sta->addr); + return ret; +} +EXPORT_SYMBOL(iwl_mac_sta_remove); diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.h b/drivers/net/wireless/iwlwifi/iwl-sta.h index 2dc35fe28f56..87a34997a758 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.h +++ b/drivers/net/wireless/iwlwifi/iwl-sta.h @@ -32,6 +32,12 @@ #define HW_KEY_DYNAMIC 0 #define HW_KEY_DEFAULT 1 +#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ +#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ +#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of + being activated */ + + /** * iwl_find_station - Find station id for a given BSSID * @bssid: MAC address of station ID to find @@ -51,18 +57,22 @@ void iwl_update_tkip_key(struct iwl_priv *priv, struct ieee80211_key_conf *keyconf, const u8 *addr, u32 iv32, u16 *phase1key); -int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap); void iwl_add_bcast_station(struct iwl_priv *priv); void iwl3945_add_bcast_station(struct iwl_priv *priv); -int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap); -void iwl_clear_stations_table(struct iwl_priv *priv); +void iwl_restore_stations(struct iwl_priv *priv); +void iwl_clear_ucode_stations(struct iwl_priv *priv, bool force); int iwl_get_free_ucode_key_index(struct iwl_priv *priv); int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr); int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr); int iwl_send_add_sta(struct iwl_priv *priv, struct iwl_addsta_cmd *sta, u8 flags); -u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags, - struct ieee80211_sta_ht_cap *ht_info); +int iwl_add_local_station(struct iwl_priv *priv, const u8 *addr, bool init_rs); +int iwl_add_station_common(struct iwl_priv *priv, const u8 *addr, + bool is_ap, + struct ieee80211_sta_ht_cap *ht_info, + u8 *sta_id_r); +int iwl_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid); int iwl_sta_rx_agg_start(struct iwl_priv *priv, const u8 *addr, int tid, u16 ssn); diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index d6222aabe6ed..1e481f3fcabf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -322,6 +322,8 @@ static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q, q->high_mark = 2; q->write_ptr = q->read_ptr = 0; + q->last_read_ptr = 0; + q->repeat_same_read_ptr = 0; return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index 2579bbcaab36..4995134d7e4a 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2480,8 +2480,6 @@ static void iwl3945_alive_start(struct iwl_priv *priv) goto restart; } - iwl_clear_stations_table(priv); - rfkill = iwl_read_prph(priv, APMG_RFKILL_REG); IWL_DEBUG_INFO(priv, "RFKILL status: 0x%x\n", rfkill); @@ -2503,6 +2501,13 @@ static void iwl3945_alive_start(struct iwl_priv *priv) /* After the ALIVE response, we can send commands to 3945 uCode */ set_bit(STATUS_ALIVE, &priv->status); + if (priv->cfg->ops->lib->recover_from_tx_stall) { + /* Enable timer to monitor the driver queues */ + mod_timer(&priv->monitor_recover, + jiffies + + msecs_to_jiffies(priv->cfg->monitor_recover_period)); + } + if (iwl_is_rfkill(priv)) return; @@ -2558,7 +2563,8 @@ static void __iwl3945_down(struct iwl_priv *priv) if (!exit_pending) set_bit(STATUS_EXIT_PENDING, &priv->status); - iwl_clear_stations_table(priv); + /* Station information will now be cleared in device */ + iwl_clear_ucode_stations(priv, true); /* Unblock any waiting calls */ wake_up_interruptible_all(&priv->wait_command_queue); @@ -2692,8 +2698,6 @@ static int __iwl3945_up(struct iwl_priv *priv) for (i = 0; i < MAX_HW_RESTARTS; i++) { - iwl_clear_stations_table(priv); - /* load bootstrap state machine, * load bootstrap program into processor's memory, * prepare to load the "initialize" uCode */ @@ -3119,12 +3123,13 @@ void iwl3945_post_associate(struct iwl_priv *priv) case NL80211_IFTYPE_ADHOC: priv->assoc_id = 1; - iwl_add_station(priv, priv->bssid, 0, CMD_SYNC, NULL); + iwl_add_local_station(priv, priv->bssid, false); iwl3945_sync_sta(priv, IWL_STA_ID, - (priv->band == IEEE80211_BAND_5GHZ) ? - IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP, + (priv->band == IEEE80211_BAND_5GHZ) ? + IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP, CMD_ASYNC); iwl3945_rate_scale_init(priv->hw, IWL_STA_ID); + iwl3945_send_beacon_cmd(priv); break; @@ -3309,7 +3314,7 @@ void iwl3945_config_ap(struct iwl_priv *priv) /* restore RXON assoc */ priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; iwlcore_commit_rxon(priv); - iwl_add_station(priv, iwl_bcast_addr, 0, CMD_SYNC, NULL); + iwl_add_local_station(priv, iwl_bcast_addr, false); } iwl3945_send_beacon_cmd(priv); @@ -3376,6 +3381,38 @@ static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return ret; } +static int iwl3945_mac_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = hw->priv; + int ret; + bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION; + u8 sta_id; + + IWL_DEBUG_INFO(priv, "received request to add station %pM\n", + sta->addr); + + ret = iwl_add_station_common(priv, sta->addr, is_ap, &sta->ht_cap, + &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM (%d)\n", + sta->addr, ret); + /* Should we return success if return code is EEXIST ? */ + return ret; + } + + /* Initialize rate scaling */ + IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM \n", + sta->addr); + iwl3945_rs_rate_init(priv, sta, sta_id); + + return 0; + + + + return ret; +} /***************************************************************************** * * sysfs attributes @@ -3766,6 +3803,13 @@ static void iwl3945_setup_deferred_work(struct iwl_priv *priv) iwl3945_hw_setup_deferred_work(priv); + if (priv->cfg->ops->lib->recover_from_tx_stall) { + init_timer(&priv->monitor_recover); + priv->monitor_recover.data = (unsigned long)priv; + priv->monitor_recover.function = + priv->cfg->ops->lib->recover_from_tx_stall; + } + tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) iwl3945_irq_tasklet, (unsigned long)priv); } @@ -3778,6 +3822,8 @@ static void iwl3945_cancel_deferred_work(struct iwl_priv *priv) cancel_delayed_work(&priv->scan_check); cancel_delayed_work(&priv->alive_start); cancel_work_sync(&priv->beacon_update); + if (priv->cfg->ops->lib->recover_from_tx_stall) + del_timer_sync(&priv->monitor_recover); } static struct attribute *iwl3945_sysfs_entries[] = { @@ -3815,7 +3861,9 @@ static struct ieee80211_ops iwl3945_hw_ops = { .conf_tx = iwl_mac_conf_tx, .reset_tsf = iwl_mac_reset_tsf, .bss_info_changed = iwl_bss_info_changed, - .hw_scan = iwl_mac_hw_scan + .hw_scan = iwl_mac_hw_scan, + .sta_add = iwl3945_mac_sta_add, + .sta_remove = iwl_mac_sta_remove, }; static int iwl3945_init_drv(struct iwl_priv *priv) @@ -3834,9 +3882,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv) mutex_init(&priv->mutex); mutex_init(&priv->sync_cmd_mutex); - /* Clear the driver's (not device's) station table */ - iwl_clear_stations_table(priv); - priv->ieee_channels = NULL; priv->ieee_rates = NULL; priv->band = IEEE80211_BAND_2GHZ; @@ -4196,7 +4241,6 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) iwl3945_hw_txq_ctx_free(priv); iwl3945_unset_hw_params(priv); - iwl_clear_stations_table(priv); /*netif_stop_queue(dev); */ flush_workqueue(priv->workqueue); diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c index f03d5e4e59c3..95d3d4c5e08b 100644 --- a/drivers/net/wireless/libertas/assoc.c +++ b/drivers/net/wireless/libertas/assoc.c @@ -31,6 +31,9 @@ u8 lbs_bg_rates[MAX_RATES] = 0x00, 0x00 }; +static int assoc_helper_wep_keys(struct lbs_private *priv, + struct assoc_request *assoc_req); + /** * @brief This function finds common rates between rates and card rates. * @@ -610,7 +613,7 @@ static int lbs_assoc_post(struct lbs_private *priv, if (status_code) { lbs_mac_event_disconnected(priv); - ret = -1; + ret = status_code; goto done; } @@ -813,7 +816,24 @@ static int lbs_try_associate(struct lbs_private *priv, goto out; ret = lbs_associate(priv, assoc_req, CMD_802_11_ASSOCIATE); + /* If the association fails with current auth mode, let's + * try by changing the auth mode + */ + if ((priv->authtype_auto) && + (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) && + (assoc_req->secinfo.wep_enabled) && + (priv->connect_status != LBS_CONNECTED)) { + if (priv->secinfo.auth_mode == IW_AUTH_ALG_OPEN_SYSTEM) + priv->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; + else + priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; + if (!assoc_helper_wep_keys(priv, assoc_req)) + ret = lbs_associate(priv, assoc_req, + CMD_802_11_ASSOCIATE); + } + if (ret) + ret = -1; out: lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); return ret; diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index 6977ee820214..058d1720242e 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -133,6 +133,7 @@ struct lbs_private { u8 wpa_ie_len; u16 wep_tx_keyidx; struct enc_key wep_keys[4]; + u8 authtype_auto; /* Wake On LAN */ uint32_t wol_criteria; diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 28a1c9d1627a..3c889f43d909 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c @@ -835,6 +835,7 @@ static int lbs_init_adapter(struct lbs_private *priv) priv->is_auto_deep_sleep_enabled = 0; priv->wakeup_dev_required = 0; init_waitqueue_head(&priv->ds_awake_q); + priv->authtype_auto = 1; mutex_init(&priv->lock); diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c index 71f88a08e090..aad6263dee6d 100644 --- a/drivers/net/wireless/libertas/wext.c +++ b/drivers/net/wireless/libertas/wext.c @@ -1440,8 +1440,10 @@ static int lbs_set_encode(struct net_device *dev, set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); if (dwrq->flags & IW_ENCODE_RESTRICTED) { + priv->authtype_auto = 0; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; } else if (dwrq->flags & IW_ENCODE_OPEN) { + priv->authtype_auto = 0; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; } @@ -1620,8 +1622,10 @@ static int lbs_set_encodeext(struct net_device *dev, goto out; if (dwrq->flags & IW_ENCODE_RESTRICTED) { + priv->authtype_auto = 0; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; } else if (dwrq->flags & IW_ENCODE_OPEN) { + priv->authtype_auto = 0; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; } diff --git a/drivers/net/wireless/orinoco/wext.c b/drivers/net/wireless/orinoco/wext.c index 31ca241f7753..29f9bc03190a 100644 --- a/drivers/net/wireless/orinoco/wext.c +++ b/drivers/net/wireless/orinoco/wext.c @@ -1505,46 +1505,44 @@ static const struct iw_priv_args orinoco_privtab[] = { * Structures to export the Wireless Handlers */ -#define STD_IW_HANDLER(id, func) \ - [IW_IOCTL_IDX(id)] = (iw_handler) func static const iw_handler orinoco_handler[] = { - STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), - STD_IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname), - STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), - STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), - STD_IW_HANDLER(SIOCSIWMODE, cfg80211_wext_siwmode), - STD_IW_HANDLER(SIOCGIWMODE, cfg80211_wext_giwmode), - STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), - STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), - STD_IW_HANDLER(SIOCGIWRANGE, cfg80211_wext_giwrange), - STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), - STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), - STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), - STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), - STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), - STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), - STD_IW_HANDLER(SIOCSIWSCAN, cfg80211_wext_siwscan), - STD_IW_HANDLER(SIOCGIWSCAN, cfg80211_wext_giwscan), - STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), - STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), - STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), - STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), - STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts), - STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts), - STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag), - STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag), - STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry), - STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), - STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), - STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), - STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), - STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), - STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), - STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), - STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), - STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), - STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), - STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), + IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)orinoco_ioctl_commit), + IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname), + IW_HANDLER(SIOCSIWFREQ, (iw_handler)orinoco_ioctl_setfreq), + IW_HANDLER(SIOCGIWFREQ, (iw_handler)orinoco_ioctl_getfreq), + IW_HANDLER(SIOCSIWMODE, (iw_handler)cfg80211_wext_siwmode), + IW_HANDLER(SIOCGIWMODE, (iw_handler)cfg80211_wext_giwmode), + IW_HANDLER(SIOCSIWSENS, (iw_handler)orinoco_ioctl_setsens), + IW_HANDLER(SIOCGIWSENS, (iw_handler)orinoco_ioctl_getsens), + IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWAP, (iw_handler)orinoco_ioctl_setwap), + IW_HANDLER(SIOCGIWAP, (iw_handler)orinoco_ioctl_getwap), + IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan), + IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan), + IW_HANDLER(SIOCSIWESSID, (iw_handler)orinoco_ioctl_setessid), + IW_HANDLER(SIOCGIWESSID, (iw_handler)orinoco_ioctl_getessid), + IW_HANDLER(SIOCSIWRATE, (iw_handler)orinoco_ioctl_setrate), + IW_HANDLER(SIOCGIWRATE, (iw_handler)orinoco_ioctl_getrate), + IW_HANDLER(SIOCSIWRTS, (iw_handler)orinoco_ioctl_setrts), + IW_HANDLER(SIOCGIWRTS, (iw_handler)orinoco_ioctl_getrts), + IW_HANDLER(SIOCSIWFRAG, (iw_handler)orinoco_ioctl_setfrag), + IW_HANDLER(SIOCGIWFRAG, (iw_handler)orinoco_ioctl_getfrag), + IW_HANDLER(SIOCGIWRETRY, (iw_handler)orinoco_ioctl_getretry), + IW_HANDLER(SIOCSIWENCODE, (iw_handler)orinoco_ioctl_setiwencode), + IW_HANDLER(SIOCGIWENCODE, (iw_handler)orinoco_ioctl_getiwencode), + IW_HANDLER(SIOCSIWPOWER, (iw_handler)orinoco_ioctl_setpower), + IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower), + IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), + IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), + IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), + IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), + IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), + IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), + IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext), }; @@ -1552,15 +1550,15 @@ static const iw_handler orinoco_handler[] = { Added typecasting since we no longer use iwreq_data -- Moustafa */ static const iw_handler orinoco_private_handler[] = { - [0] = (iw_handler) orinoco_ioctl_reset, - [1] = (iw_handler) orinoco_ioctl_reset, - [2] = (iw_handler) orinoco_ioctl_setport3, - [3] = (iw_handler) orinoco_ioctl_getport3, - [4] = (iw_handler) orinoco_ioctl_setpreamble, - [5] = (iw_handler) orinoco_ioctl_getpreamble, - [6] = (iw_handler) orinoco_ioctl_setibssport, - [7] = (iw_handler) orinoco_ioctl_getibssport, - [9] = (iw_handler) orinoco_ioctl_getrid, + [0] = (iw_handler)orinoco_ioctl_reset, + [1] = (iw_handler)orinoco_ioctl_reset, + [2] = (iw_handler)orinoco_ioctl_setport3, + [3] = (iw_handler)orinoco_ioctl_getport3, + [4] = (iw_handler)orinoco_ioctl_setpreamble, + [5] = (iw_handler)orinoco_ioctl_getpreamble, + [6] = (iw_handler)orinoco_ioctl_setibssport, + [7] = (iw_handler)orinoco_ioctl_getibssport, + [9] = (iw_handler)orinoco_ioctl_getrid, }; const struct iw_handler_def orinoco_handler_def = { diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 84c530aa52f9..4f5bdb528ef7 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -1113,10 +1113,10 @@ static const struct ethtool_ops netdev_ethtool_ops = { /* * Wireless Handler : get protocol name */ -static int ray_get_name(struct net_device *dev, - struct iw_request_info *info, char *cwrq, char *extra) +static int ray_get_name(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { - strcpy(cwrq, "IEEE 802.11-FH"); + strcpy(wrqu->name, "IEEE 802.11-FH"); return 0; } @@ -1124,9 +1124,8 @@ static int ray_get_name(struct net_device *dev, /* * Wireless Handler : set frequency */ -static int ray_set_freq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *fwrq, char *extra) +static int ray_set_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ @@ -1136,10 +1135,10 @@ static int ray_set_freq(struct net_device *dev, return -EBUSY; /* Setting by channel number */ - if ((fwrq->m > USA_HOP_MOD) || (fwrq->e > 0)) + if ((wrqu->freq.m > USA_HOP_MOD) || (wrqu->freq.e > 0)) err = -EOPNOTSUPP; else - local->sparm.b5.a_hop_pattern = fwrq->m; + local->sparm.b5.a_hop_pattern = wrqu->freq.m; return err; } @@ -1148,14 +1147,13 @@ static int ray_set_freq(struct net_device *dev, /* * Wireless Handler : get frequency */ -static int ray_get_freq(struct net_device *dev, - struct iw_request_info *info, - struct iw_freq *fwrq, char *extra) +static int ray_get_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - fwrq->m = local->sparm.b5.a_hop_pattern; - fwrq->e = 0; + wrqu->freq.m = local->sparm.b5.a_hop_pattern; + wrqu->freq.e = 0; return 0; } @@ -1163,9 +1161,8 @@ static int ray_get_freq(struct net_device *dev, /* * Wireless Handler : set ESSID */ -static int ray_set_essid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, char *extra) +static int ray_set_essid(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); @@ -1174,19 +1171,17 @@ static int ray_set_essid(struct net_device *dev, return -EBUSY; /* Check if we asked for `any' */ - if (dwrq->flags == 0) { + if (wrqu->essid.flags == 0) /* Corey : can you do that ? */ return -EOPNOTSUPP; - } else { - /* Check the size of the string */ - if (dwrq->length > IW_ESSID_MAX_SIZE) { - return -E2BIG; - } - /* Set the ESSID in the card */ - memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE); - memcpy(local->sparm.b5.a_current_ess_id, extra, dwrq->length); - } + /* Check the size of the string */ + if (wrqu->essid.length > IW_ESSID_MAX_SIZE) + return -E2BIG; + + /* Set the ESSID in the card */ + memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE); + memcpy(local->sparm.b5.a_current_ess_id, extra, wrqu->essid.length); return -EINPROGRESS; /* Call commit handler */ } @@ -1195,9 +1190,8 @@ static int ray_set_essid(struct net_device *dev, /* * Wireless Handler : get ESSID */ -static int ray_get_essid(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, char *extra) +static int ray_get_essid(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); @@ -1205,8 +1199,8 @@ static int ray_get_essid(struct net_device *dev, memcpy(extra, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE); /* Push it out ! */ - dwrq->length = strlen(extra); - dwrq->flags = 1; /* active */ + wrqu->essid.length = strlen(extra); + wrqu->essid.flags = 1; /* active */ return 0; } @@ -1215,14 +1209,13 @@ static int ray_get_essid(struct net_device *dev, /* * Wireless Handler : get AP address */ -static int ray_get_wap(struct net_device *dev, - struct iw_request_info *info, - struct sockaddr *awrq, char *extra) +static int ray_get_wap(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - memcpy(awrq->sa_data, local->bss_id, ETH_ALEN); - awrq->sa_family = ARPHRD_ETHER; + memcpy(wrqu->ap_addr.sa_data, local->bss_id, ETH_ALEN); + wrqu->ap_addr.sa_family = ARPHRD_ETHER; return 0; } @@ -1231,9 +1224,8 @@ static int ray_get_wap(struct net_device *dev, /* * Wireless Handler : set Bit-Rate */ -static int ray_set_rate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_set_rate(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); @@ -1242,15 +1234,15 @@ static int ray_set_rate(struct net_device *dev, return -EBUSY; /* Check if rate is in range */ - if ((vwrq->value != 1000000) && (vwrq->value != 2000000)) + if ((wrqu->bitrate.value != 1000000) && (wrqu->bitrate.value != 2000000)) return -EINVAL; /* Hack for 1.5 Mb/s instead of 2 Mb/s */ if ((local->fw_ver == 0x55) && /* Please check */ - (vwrq->value == 2000000)) + (wrqu->bitrate.value == 2000000)) local->net_default_tx_rate = 3; else - local->net_default_tx_rate = vwrq->value / 500000; + local->net_default_tx_rate = wrqu->bitrate.value / 500000; return 0; } @@ -1259,17 +1251,16 @@ static int ray_set_rate(struct net_device *dev, /* * Wireless Handler : get Bit-Rate */ -static int ray_get_rate(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_get_rate(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); if (local->net_default_tx_rate == 3) - vwrq->value = 2000000; /* Hum... */ + wrqu->bitrate.value = 2000000; /* Hum... */ else - vwrq->value = local->net_default_tx_rate * 500000; - vwrq->fixed = 0; /* We are in auto mode */ + wrqu->bitrate.value = local->net_default_tx_rate * 500000; + wrqu->bitrate.fixed = 0; /* We are in auto mode */ return 0; } @@ -1278,19 +1269,18 @@ static int ray_get_rate(struct net_device *dev, /* * Wireless Handler : set RTS threshold */ -static int ray_set_rts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_set_rts(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - int rthr = vwrq->value; + int rthr = wrqu->rts.value; /* Reject if card is already initialised */ if (local->card_status != CARD_AWAITING_PARAM) return -EBUSY; /* if(wrq->u.rts.fixed == 0) we should complain */ - if (vwrq->disabled) + if (wrqu->rts.disabled) rthr = 32767; else { if ((rthr < 0) || (rthr > 2347)) /* What's the max packet size ??? */ @@ -1306,16 +1296,15 @@ static int ray_set_rts(struct net_device *dev, /* * Wireless Handler : get RTS threshold */ -static int ray_get_rts(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_get_rts(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - vwrq->value = (local->sparm.b5.a_rts_threshold[0] << 8) + wrqu->rts.value = (local->sparm.b5.a_rts_threshold[0] << 8) + local->sparm.b5.a_rts_threshold[1]; - vwrq->disabled = (vwrq->value == 32767); - vwrq->fixed = 1; + wrqu->rts.disabled = (wrqu->rts.value == 32767); + wrqu->rts.fixed = 1; return 0; } @@ -1324,19 +1313,18 @@ static int ray_get_rts(struct net_device *dev, /* * Wireless Handler : set Fragmentation threshold */ -static int ray_set_frag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_set_frag(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - int fthr = vwrq->value; + int fthr = wrqu->frag.value; /* Reject if card is already initialised */ if (local->card_status != CARD_AWAITING_PARAM) return -EBUSY; /* if(wrq->u.frag.fixed == 0) should complain */ - if (vwrq->disabled) + if (wrqu->frag.disabled) fthr = 32767; else { if ((fthr < 256) || (fthr > 2347)) /* To check out ! */ @@ -1352,16 +1340,15 @@ static int ray_set_frag(struct net_device *dev, /* * Wireless Handler : get Fragmentation threshold */ -static int ray_get_frag(struct net_device *dev, - struct iw_request_info *info, - struct iw_param *vwrq, char *extra) +static int ray_get_frag(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); - vwrq->value = (local->sparm.b5.a_frag_threshold[0] << 8) + wrqu->frag.value = (local->sparm.b5.a_frag_threshold[0] << 8) + local->sparm.b5.a_frag_threshold[1]; - vwrq->disabled = (vwrq->value == 32767); - vwrq->fixed = 1; + wrqu->frag.disabled = (wrqu->frag.value == 32767); + wrqu->frag.fixed = 1; return 0; } @@ -1370,8 +1357,8 @@ static int ray_get_frag(struct net_device *dev, /* * Wireless Handler : set Mode of Operation */ -static int ray_set_mode(struct net_device *dev, - struct iw_request_info *info, __u32 *uwrq, char *extra) +static int ray_set_mode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); int err = -EINPROGRESS; /* Call commit handler */ @@ -1381,7 +1368,7 @@ static int ray_set_mode(struct net_device *dev, if (local->card_status != CARD_AWAITING_PARAM) return -EBUSY; - switch (*uwrq) { + switch (wrqu->mode) { case IW_MODE_ADHOC: card_mode = 0; /* Fall through */ @@ -1399,15 +1386,15 @@ static int ray_set_mode(struct net_device *dev, /* * Wireless Handler : get Mode of Operation */ -static int ray_get_mode(struct net_device *dev, - struct iw_request_info *info, __u32 *uwrq, char *extra) +static int ray_get_mode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { ray_dev_t *local = netdev_priv(dev); if (local->sparm.b5.a_network_type) - *uwrq = IW_MODE_INFRA; + wrqu->mode = IW_MODE_INFRA; else - *uwrq = IW_MODE_ADHOC; + wrqu->mode = IW_MODE_ADHOC; return 0; } @@ -1416,16 +1403,15 @@ static int ray_get_mode(struct net_device *dev, /* * Wireless Handler : get range info */ -static int ray_get_range(struct net_device *dev, - struct iw_request_info *info, - struct iw_point *dwrq, char *extra) +static int ray_get_range(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) { struct iw_range *range = (struct iw_range *)extra; - memset((char *)range, 0, sizeof(struct iw_range)); + memset(range, 0, sizeof(struct iw_range)); /* Set the length (very important for backward compatibility) */ - dwrq->length = sizeof(struct iw_range); + wrqu->data.length = sizeof(struct iw_range); /* Set the Wireless Extension versions */ range->we_version_compiled = WIRELESS_EXT; @@ -1448,8 +1434,7 @@ static int ray_get_range(struct net_device *dev, /* * Wireless Private Handler : set framing mode */ -static int ray_set_framing(struct net_device *dev, - struct iw_request_info *info, +static int ray_set_framing(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { translate = *(extra); /* Set framing mode */ @@ -1461,8 +1446,7 @@ static int ray_set_framing(struct net_device *dev, /* * Wireless Private Handler : get framing mode */ -static int ray_get_framing(struct net_device *dev, - struct iw_request_info *info, +static int ray_get_framing(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { *(extra) = translate; @@ -1474,8 +1458,7 @@ static int ray_get_framing(struct net_device *dev, /* * Wireless Private Handler : get country */ -static int ray_get_country(struct net_device *dev, - struct iw_request_info *info, +static int ray_get_country(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { *(extra) = country; @@ -1487,10 +1470,9 @@ static int ray_get_country(struct net_device *dev, /* * Commit handler : called after a bunch of SET operations */ -static int ray_commit(struct net_device *dev, struct iw_request_info *info, /* NULL */ - void *zwrq, /* NULL */ - char *extra) -{ /* NULL */ +static int ray_commit(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ return 0; } @@ -1531,28 +1513,28 @@ static iw_stats *ray_get_wireless_stats(struct net_device *dev) */ static const iw_handler ray_handler[] = { - [SIOCSIWCOMMIT - SIOCIWFIRST] = (iw_handler) ray_commit, - [SIOCGIWNAME - SIOCIWFIRST] = (iw_handler) ray_get_name, - [SIOCSIWFREQ - SIOCIWFIRST] = (iw_handler) ray_set_freq, - [SIOCGIWFREQ - SIOCIWFIRST] = (iw_handler) ray_get_freq, - [SIOCSIWMODE - SIOCIWFIRST] = (iw_handler) ray_set_mode, - [SIOCGIWMODE - SIOCIWFIRST] = (iw_handler) ray_get_mode, - [SIOCGIWRANGE - SIOCIWFIRST] = (iw_handler) ray_get_range, + IW_HANDLER(SIOCSIWCOMMIT, ray_commit), + IW_HANDLER(SIOCGIWNAME, ray_get_name), + IW_HANDLER(SIOCSIWFREQ, ray_set_freq), + IW_HANDLER(SIOCGIWFREQ, ray_get_freq), + IW_HANDLER(SIOCSIWMODE, ray_set_mode), + IW_HANDLER(SIOCGIWMODE, ray_get_mode), + IW_HANDLER(SIOCGIWRANGE, ray_get_range), #ifdef WIRELESS_SPY - [SIOCSIWSPY - SIOCIWFIRST] = (iw_handler) iw_handler_set_spy, - [SIOCGIWSPY - SIOCIWFIRST] = (iw_handler) iw_handler_get_spy, - [SIOCSIWTHRSPY - SIOCIWFIRST] = (iw_handler) iw_handler_set_thrspy, - [SIOCGIWTHRSPY - SIOCIWFIRST] = (iw_handler) iw_handler_get_thrspy, + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), #endif /* WIRELESS_SPY */ - [SIOCGIWAP - SIOCIWFIRST] = (iw_handler) ray_get_wap, - [SIOCSIWESSID - SIOCIWFIRST] = (iw_handler) ray_set_essid, - [SIOCGIWESSID - SIOCIWFIRST] = (iw_handler) ray_get_essid, - [SIOCSIWRATE - SIOCIWFIRST] = (iw_handler) ray_set_rate, - [SIOCGIWRATE - SIOCIWFIRST] = (iw_handler) ray_get_rate, - [SIOCSIWRTS - SIOCIWFIRST] = (iw_handler) ray_set_rts, - [SIOCGIWRTS - SIOCIWFIRST] = (iw_handler) ray_get_rts, - [SIOCSIWFRAG - SIOCIWFIRST] = (iw_handler) ray_set_frag, - [SIOCGIWFRAG - SIOCIWFIRST] = (iw_handler) ray_get_frag, + IW_HANDLER(SIOCGIWAP, ray_get_wap), + IW_HANDLER(SIOCSIWESSID, ray_set_essid), + IW_HANDLER(SIOCGIWESSID, ray_get_essid), + IW_HANDLER(SIOCSIWRATE, ray_set_rate), + IW_HANDLER(SIOCGIWRATE, ray_get_rate), + IW_HANDLER(SIOCSIWRTS, ray_set_rts), + IW_HANDLER(SIOCGIWRTS, ray_get_rts), + IW_HANDLER(SIOCSIWFRAG, ray_set_frag), + IW_HANDLER(SIOCGIWFRAG, ray_get_frag), }; #define SIOCSIPFRAMING SIOCIWFIRSTPRIV /* Set framing mode */ @@ -1560,9 +1542,9 @@ static const iw_handler ray_handler[] = { #define SIOCGIPCOUNTRY SIOCIWFIRSTPRIV + 3 /* Get country code */ static const iw_handler ray_private_handler[] = { - [0] = (iw_handler) ray_set_framing, - [1] = (iw_handler) ray_get_framing, - [3] = (iw_handler) ray_get_country, + [0] = ray_set_framing, + [1] = ray_get_framing, + [3] = ray_get_country, }; static const struct iw_priv_args ray_private_args[] = { diff --git a/drivers/net/wireless/wl12xx/wl1271.h b/drivers/net/wireless/wl12xx/wl1271.h index 0deb4fdf916b..8f11506f8310 100644 --- a/drivers/net/wireless/wl12xx/wl1271.h +++ b/drivers/net/wireless/wl12xx/wl1271.h @@ -53,6 +53,8 @@ enum { DEBUG_MAC80211 = BIT(11), DEBUG_CMD = BIT(12), DEBUG_ACX = BIT(13), + DEBUG_SDIO = BIT(14), + DEBUG_FILTERS = BIT(15), DEBUG_ALL = ~0, }; @@ -344,12 +346,14 @@ struct wl1271_if_operations { bool fixed); void (*reset)(struct wl1271 *wl); void (*init)(struct wl1271 *wl); + void (*power)(struct wl1271 *wl, bool enable); struct device* (*dev)(struct wl1271 *wl); void (*enable_irq)(struct wl1271 *wl); void (*disable_irq)(struct wl1271 *wl); }; struct wl1271 { + struct platform_device *plat_dev; struct ieee80211_hw *hw; bool mac80211_registered; @@ -456,6 +460,7 @@ struct wl1271 { /* Default key (for WEP) */ u32 default_key; + unsigned int filters; unsigned int rx_config; unsigned int rx_filter; @@ -483,6 +488,8 @@ struct wl1271 { /* Current chipset configuration */ struct conf_drv_settings conf; + bool sg_enabled; + struct list_head list; }; diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.c b/drivers/net/wireless/wl12xx/wl1271_acx.c index 60e20876e6d8..7e337cea9905 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.c +++ b/drivers/net/wireless/wl12xx/wl1271_acx.c @@ -534,7 +534,7 @@ out: } -int wl1271_acx_sg_enable(struct wl1271 *wl) +int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable) { struct acx_bt_wlan_coex *pta; int ret; @@ -547,7 +547,10 @@ int wl1271_acx_sg_enable(struct wl1271 *wl) goto out; } - pta->enable = SG_ENABLE; + if (enable) + pta->enable = wl->conf.sg.state; + else + pta->enable = CONF_SG_DISABLE; ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta)); if (ret < 0) { @@ -564,7 +567,7 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl) { struct acx_bt_wlan_coex_param *param; struct conf_sg_settings *c = &wl->conf.sg; - int ret; + int i, ret; wl1271_debug(DEBUG_ACX, "acx sg cfg"); @@ -575,19 +578,9 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl) } /* BT-WLAN coext parameters */ - param->per_threshold = cpu_to_le32(c->per_threshold); - param->max_scan_compensation_time = - cpu_to_le32(c->max_scan_compensation_time); - param->nfs_sample_interval = cpu_to_le16(c->nfs_sample_interval); - param->load_ratio = c->load_ratio; - param->auto_ps_mode = c->auto_ps_mode; - param->probe_req_compensation = c->probe_req_compensation; - param->scan_window_compensation = c->scan_window_compensation; - param->antenna_config = c->antenna_config; - param->beacon_miss_threshold = c->beacon_miss_threshold; - param->rate_adaptation_threshold = - cpu_to_le32(c->rate_adaptation_threshold); - param->rate_adaptation_snr = c->rate_adaptation_snr; + for (i = 0; i < CONF_SG_PARAMS_MAX; i++) + param->params[i] = c->params[i]; + param->param_idx = CONF_SG_PARAMS_ALL; ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/wl1271_acx.h b/drivers/net/wireless/wl12xx/wl1271_acx.h index aeccc98581eb..8e5870fa9609 100644 --- a/drivers/net/wireless/wl12xx/wl1271_acx.h +++ b/drivers/net/wireless/wl12xx/wl1271_acx.h @@ -392,81 +392,27 @@ struct acx_conn_monit_params { __le32 bss_lose_timeout; /* number of TU's from synch fail */ } __attribute__ ((packed)); -enum { - SG_ENABLE = 0, - SG_DISABLE, - SG_SENSE_NO_ACTIVITY, - SG_SENSE_ACTIVE -}; - struct acx_bt_wlan_coex { struct acx_header header; - /* - * 0 -> PTA enabled - * 1 -> PTA disabled - * 2 -> sense no active mode, i.e. - * an interrupt is sent upon - * BT activity. - * 3 -> PTA is switched on in response - * to the interrupt sending. - */ u8 enable; u8 pad[3]; } __attribute__ ((packed)); -struct acx_dco_itrim_params { +struct acx_bt_wlan_coex_param { struct acx_header header; - u8 enable; + __le32 params[CONF_SG_PARAMS_MAX]; + u8 param_idx; u8 padding[3]; - __le32 timeout; } __attribute__ ((packed)); -#define PTA_ANTENNA_TYPE_DEF (0) -#define PTA_BT_HP_MAXTIME_DEF (2000) -#define PTA_WLAN_HP_MAX_TIME_DEF (5000) -#define PTA_SENSE_DISABLE_TIMER_DEF (1350) -#define PTA_PROTECTIVE_RX_TIME_DEF (1500) -#define PTA_PROTECTIVE_TX_TIME_DEF (1500) -#define PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF (3000) -#define PTA_SIGNALING_TYPE_DEF (1) -#define PTA_AFH_LEVERAGE_ON_DEF (0) -#define PTA_NUMBER_QUIET_CYCLE_DEF (0) -#define PTA_MAX_NUM_CTS_DEF (3) -#define PTA_NUMBER_OF_WLAN_PACKETS_DEF (2) -#define PTA_NUMBER_OF_BT_PACKETS_DEF (2) -#define PTA_PROTECTIVE_RX_TIME_FAST_DEF (1500) -#define PTA_PROTECTIVE_TX_TIME_FAST_DEF (3000) -#define PTA_CYCLE_TIME_FAST_DEF (8700) -#define PTA_RX_FOR_AVALANCHE_DEF (5) -#define PTA_ELP_HP_DEF (0) -#define PTA_ANTI_STARVE_PERIOD_DEF (500) -#define PTA_ANTI_STARVE_NUM_CYCLE_DEF (4) -#define PTA_ALLOW_PA_SD_DEF (1) -#define PTA_TIME_BEFORE_BEACON_DEF (6300) -#define PTA_HPDM_MAX_TIME_DEF (1600) -#define PTA_TIME_OUT_NEXT_WLAN_DEF (2550) -#define PTA_AUTO_MODE_NO_CTS_DEF (0) -#define PTA_BT_HP_RESPECTED_DEF (3) -#define PTA_WLAN_RX_MIN_RATE_DEF (24) -#define PTA_ACK_MODE_DEF (1) - -struct acx_bt_wlan_coex_param { +struct acx_dco_itrim_params { struct acx_header header; - __le32 per_threshold; - __le32 max_scan_compensation_time; - __le16 nfs_sample_interval; - u8 load_ratio; - u8 auto_ps_mode; - u8 probe_req_compensation; - u8 scan_window_compensation; - u8 antenna_config; - u8 beacon_miss_threshold; - __le32 rate_adaptation_threshold; - s8 rate_adaptation_snr; + u8 enable; u8 padding[3]; + __le32 timeout; } __attribute__ ((packed)); struct acx_energy_detection { @@ -1059,7 +1005,7 @@ int wl1271_acx_dco_itrim_params(struct wl1271 *wl); int wl1271_acx_beacon_filter_opt(struct wl1271 *wl, bool enable_filter); int wl1271_acx_beacon_filter_table(struct wl1271 *wl); int wl1271_acx_conn_monit_params(struct wl1271 *wl); -int wl1271_acx_sg_enable(struct wl1271 *wl); +int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable); int wl1271_acx_sg_cfg(struct wl1271 *wl); int wl1271_acx_cca_threshold(struct wl1271 *wl); int wl1271_acx_bcn_dtim_options(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_boot.c b/drivers/net/wireless/wl12xx/wl1271_boot.c index f88d52e87e82..41c6affbee29 100644 --- a/drivers/net/wireless/wl12xx/wl1271_boot.c +++ b/drivers/net/wireless/wl12xx/wl1271_boot.c @@ -228,6 +228,14 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl) nvs_len = sizeof(wl->nvs->nvs); nvs_ptr = (u8 *)wl->nvs->nvs; + /* update current MAC address to NVS */ + nvs_ptr[11] = wl->mac_addr[0]; + nvs_ptr[10] = wl->mac_addr[1]; + nvs_ptr[6] = wl->mac_addr[2]; + nvs_ptr[5] = wl->mac_addr[3]; + nvs_ptr[4] = wl->mac_addr[4]; + nvs_ptr[3] = wl->mac_addr[5]; + /* * Layout before the actual NVS tables: * 1 byte : burst length. diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.c b/drivers/net/wireless/wl12xx/wl1271_cmd.c index d59b3830a6a5..d005729e0312 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.c @@ -26,6 +26,7 @@ #include <linux/crc7.h> #include <linux/spi/spi.h> #include <linux/etherdevice.h> +#include <linux/ieee80211.h> #include "wl1271.h" #include "wl1271_reg.h" @@ -280,15 +281,6 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type) join->rx_filter_options = cpu_to_le32(wl->rx_filter); join->bss_type = bss_type; - /* - * FIXME: disable temporarily all filters because after commit - * 9cef8737 "mac80211: fix managed mode BSSID handling" broke - * association. The filter logic needs to be implemented properly - * and once that is done, this hack can be removed. - */ - join->rx_config_options = cpu_to_le32(0); - join->rx_filter_options = cpu_to_le32(WL1271_DEFAULT_RX_FILTER); - if (wl->band == IEEE80211_BAND_2GHZ) join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS | CONF_HW_BIT_RATE_2MBPS | @@ -546,9 +538,9 @@ out: return ret; } -int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 band, - u8 probe_requests) +int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len, u8 active_scan, + u8 high_prio, u8 band, u8 probe_requests) { struct wl1271_cmd_trigger_scan_to *trigger = NULL; @@ -619,12 +611,13 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, params->params.num_channels = j; - if (len && ssid) { - params->params.ssid_len = len; - memcpy(params->params.ssid, ssid, len); + if (ssid_len && ssid) { + params->params.ssid_len = ssid_len; + memcpy(params->params.ssid, ssid, ssid_len); } - ret = wl1271_cmd_build_probe_req(wl, ssid, len, ieee_band); + ret = wl1271_cmd_build_probe_req(wl, ssid, ssid_len, + ie, ie_len, ieee_band); if (ret < 0) { wl1271_error("PROBE request template failed"); goto out; @@ -655,9 +648,9 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, wl->scan.active = active_scan; wl->scan.high_prio = high_prio; wl->scan.probe_requests = probe_requests; - if (len && ssid) { - wl->scan.ssid_len = len; - memcpy(wl->scan.ssid, ssid, len); + if (ssid_len && ssid) { + wl->scan.ssid_len = ssid_len; + memcpy(wl->scan.ssid, ssid, ssid_len); } else wl->scan.ssid_len = 0; } @@ -714,155 +707,102 @@ out: return ret; } -static int wl1271_build_basic_rates(u8 *rates, u8 band) +int wl1271_cmd_build_null_data(struct wl1271 *wl) { - u8 index = 0; - - if (band == IEEE80211_BAND_2GHZ) { - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; - } else if (band == IEEE80211_BAND_5GHZ) { - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_6MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_12MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB; - } else { - wl1271_error("build_basic_rates invalid band: %d", band); - } + struct sk_buff *skb = NULL; + int size; + void *ptr; + int ret = -ENOMEM; - return index; -} -static int wl1271_build_extended_rates(u8 *rates, u8 band) -{ - u8 index = 0; - - if (band == IEEE80211_BAND_2GHZ) { - rates[index++] = IEEE80211_OFDM_RATE_6MB; - rates[index++] = IEEE80211_OFDM_RATE_9MB; - rates[index++] = IEEE80211_OFDM_RATE_12MB; - rates[index++] = IEEE80211_OFDM_RATE_18MB; - rates[index++] = IEEE80211_OFDM_RATE_24MB; - rates[index++] = IEEE80211_OFDM_RATE_36MB; - rates[index++] = IEEE80211_OFDM_RATE_48MB; - rates[index++] = IEEE80211_OFDM_RATE_54MB; - } else if (band == IEEE80211_BAND_5GHZ) { - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_9MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_18MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_24MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_36MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_48MB; - rates[index++] = - IEEE80211_BASIC_RATE_MASK | IEEE80211_OFDM_RATE_54MB; + if (wl->bss_type == BSS_TYPE_IBSS) { + size = sizeof(struct wl12xx_null_data_template); + ptr = NULL; } else { - wl1271_error("build_basic_rates invalid band: %d", band); + skb = ieee80211_nullfunc_get(wl->hw, wl->vif); + if (!skb) + goto out; + size = skb->len; + ptr = skb->data; } - return index; -} + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size); -int wl1271_cmd_build_null_data(struct wl1271 *wl) -{ - struct wl12xx_null_data_template template; - - if (!is_zero_ether_addr(wl->bssid)) { - memcpy(template.header.da, wl->bssid, ETH_ALEN); - memcpy(template.header.bssid, wl->bssid, ETH_ALEN); - } else { - memset(template.header.da, 0xff, ETH_ALEN); - memset(template.header.bssid, 0xff, ETH_ALEN); - } - - memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); - template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC | - IEEE80211_FCTL_TODS); +out: + dev_kfree_skb(skb); + if (ret) + wl1271_warning("cmd buld null data failed %d", ret); - return wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, &template, - sizeof(template)); + return ret; } int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) { - struct wl12xx_ps_poll_template template; - - memcpy(template.bssid, wl->bssid, ETH_ALEN); - memcpy(template.ta, wl->mac_addr, ETH_ALEN); - - /* aid in PS-Poll has its two MSBs each set to 1 */ - template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); + struct sk_buff *skb; + int ret = 0; - template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); + skb = ieee80211_pspoll_get(wl->hw, wl->vif); + if (!skb) + goto out; - return wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, &template, - sizeof(template)); + ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data, + skb->len); +out: + dev_kfree_skb(skb); + return ret; } -int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len, - u8 band) +int wl1271_cmd_build_probe_req(struct wl1271 *wl, + const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len, u8 band) { - struct wl12xx_probe_req_template template; - struct wl12xx_ie_rates *rates; - char *ptr; - u16 size; + struct sk_buff *skb; int ret; - ptr = (char *)&template; - size = sizeof(struct ieee80211_header); - - memset(template.header.da, 0xff, ETH_ALEN); - memset(template.header.bssid, 0xff, ETH_ALEN); - memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); - template.header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - - /* IEs */ - /* SSID */ - template.ssid.header.id = WLAN_EID_SSID; - template.ssid.header.len = ssid_len; - if (ssid_len && ssid) - memcpy(template.ssid.ssid, ssid, ssid_len); - size += sizeof(struct wl12xx_ie_header) + ssid_len; - ptr += size; - - /* Basic Rates */ - rates = (struct wl12xx_ie_rates *)ptr; - rates->header.id = WLAN_EID_SUPP_RATES; - rates->header.len = wl1271_build_basic_rates(rates->rates, band); - size += sizeof(struct wl12xx_ie_header) + rates->header.len; - ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; - - /* Extended rates */ - rates = (struct wl12xx_ie_rates *)ptr; - rates->header.id = WLAN_EID_EXT_SUPP_RATES; - rates->header.len = wl1271_build_extended_rates(rates->rates, band); - size += sizeof(struct wl12xx_ie_header) + rates->header.len; - - wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); + skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, + ie, ie_len); + if (!skb) { + ret = -ENOMEM; + goto out; + } + + wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); if (band == IEEE80211_BAND_2GHZ) ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, - &template, size); + skb->data, skb->len); else ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, - &template, size); + skb->data, skb->len); + +out: + dev_kfree_skb(skb); return ret; } +int wl1271_build_qos_null_data(struct wl1271 *wl) +{ + struct ieee80211_qos_hdr template; + + memset(&template, 0, sizeof(template)); + + memcpy(template.addr1, wl->bssid, ETH_ALEN); + memcpy(template.addr2, wl->mac_addr, ETH_ALEN); + memcpy(template.addr3, wl->bssid, ETH_ALEN); + + template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_TODS); + + /* FIXME: not sure what priority to use here */ + template.qos_ctrl = cpu_to_le16(0); + + return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template, + sizeof(template)); +} + int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id) { struct wl1271_cmd_set_keys *cmd; diff --git a/drivers/net/wireless/wl12xx/wl1271_cmd.h b/drivers/net/wireless/wl12xx/wl1271_cmd.h index 4297205b8d6d..6324bbf36843 100644 --- a/drivers/net/wireless/wl12xx/wl1271_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1271_cmd.h @@ -41,15 +41,17 @@ int wl1271_cmd_data_path(struct wl1271 *wl, bool enable); int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send); int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, size_t len); -int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 band, - u8 probe_requests); +int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len, u8 active_scan, + u8 high_prio, u8 band, u8 probe_requests); int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, void *buf, size_t buf_len); int wl1271_cmd_build_null_data(struct wl1271 *wl); int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid); -int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len, - u8 band); +int wl1271_cmd_build_probe_req(struct wl1271 *wl, + const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len, u8 band); +int wl1271_build_qos_null_data(struct wl1271 *wl); int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id); int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, u8 key_size, const u8 *key, const u8 *addr, diff --git a/drivers/net/wireless/wl12xx/wl1271_conf.h b/drivers/net/wireless/wl12xx/wl1271_conf.h index 6f9e75cc5640..7fcfe06b1412 100644 --- a/drivers/net/wireless/wl12xx/wl1271_conf.h +++ b/drivers/net/wireless/wl12xx/wl1271_conf.h @@ -65,110 +65,318 @@ enum { CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS, }; -struct conf_sg_settings { +enum { + CONF_SG_DISABLE = 0, + CONF_SG_PROTECTIVE, + CONF_SG_OPPORTUNISTIC +}; + +enum { /* - * Defines the PER threshold in PPM of the BT voice of which reaching - * this value will trigger raising the priority of the BT voice by - * the BT IP until next NFS sample interval time as defined in - * nfs_sample_interval. + * PER threshold in PPM of the BT voice * - * Unit: PER value in PPM (parts per million) - * #Error_packets / #Total_packets + * Range: 0 - 10000000 + */ + CONF_SG_BT_PER_THRESHOLD = 0, - * Range: u32 + /* + * Number of consequent RX_ACTIVE activities to override BT voice + * frames to ensure WLAN connection + * + * Range: 0 - 100 + */ + CONF_SG_HV3_MAX_OVERRIDE, + + /* + * Defines the PER threshold of the BT voice + * + * Range: 0 - 65000 + */ + CONF_SG_BT_NFS_SAMPLE_INTERVAL, + + /* + * Defines the load ratio of BT + * + * Range: 0 - 100 (%) + */ + CONF_SG_BT_LOAD_RATIO, + + /* + * Defines whether the SG will force WLAN host to enter/exit PSM + * + * Range: 1 - SG can force, 0 - host handles PSM + */ + CONF_SG_AUTO_PS_MODE, + + /* + * Compensation percentage of probe requests when scan initiated + * during BT voice/ACL link. + * + * Range: 0 - 255 (%) + */ + CONF_SG_AUTO_SCAN_PROBE_REQ, + + /* + * Compensation percentage of probe requests when active scan initiated + * during BT voice + * + * Range: 0 - 255 (%) + */ + CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3, + + /* + * Defines antenna configuration (single/dual antenna) + * + * Range: 0 - single antenna, 1 - dual antenna + */ + CONF_SG_ANTENNA_CONFIGURATION, + + /* + * The threshold (percent) of max consequtive beacon misses before + * increasing priority of beacon reception. + * + * Range: 0 - 100 (%) + */ + CONF_SG_BEACON_MISS_PERCENT, + + /* + * The rate threshold below which receiving a data frame from the AP + * will increase the priority of the data frame above BT traffic. + * + * Range: 0,2, 5(=5.5), 6, 9, 11, 12, 18, 24, 36, 48, 54 + */ + CONF_SG_RATE_ADAPT_THRESH, + + /* + * Not used currently. + * + * Range: 0 + */ + CONF_SG_RATE_ADAPT_SNR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN PSM / BT master basic rate + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR, + CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR, + + /* + * The time after it expires no new WLAN trigger frame is trasmitted + * in WLAN PSM / BT master basic rate + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN PSM / BT slave basic rate + * + * Range: 0 - 255 (ms) */ - u32 per_threshold; + CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR, + CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR, /* - * This value is an absolute time in micro-seconds to limit the - * maximum scan duration compensation while in SG + * The time after it expires no new WLAN trigger frame is trasmitted + * in WLAN PSM / BT slave basic rate + * + * Range: 0 - 255 (ms) */ - u32 max_scan_compensation_time; + CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR, - /* Defines the PER threshold of the BT voice of which reaching this - * value will trigger raising the priority of the BT voice until next - * NFS sample interval time as defined in sample_interval. + /* + * Configure the min and max time BT gains the antenna + * in WLAN PSM / BT master EDR * - * Unit: msec - * Range: 1-65000 + * Range: 0 - 255 (ms) */ - u16 nfs_sample_interval; + CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR, + CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR, /* - * Defines the load ratio for the BT. - * The WLAN ratio is: 100 - load_ratio + * The time after it expires no new WLAN trigger frame is trasmitted + * in WLAN PSM / BT master EDR * - * Unit: Percent - * Range: 0-100 + * Range: 0 - 255 (ms) */ - u8 load_ratio; + CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR, /* - * true - Co-ex is allowed to enter/exit P.S automatically and - * transparently to the host + * Configure the min and max time BT gains the antenna + * in WLAN PSM / BT slave EDR * - * false - Co-ex is disallowed to enter/exit P.S and will trigger an - * event to the host to notify for the need to enter/exit P.S - * due to BT change state + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR, + CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR, + + /* + * The time after it expires no new WLAN trigger frame is trasmitted + * in WLAN PSM / BT slave EDR * + * Range: 0 - 255 (ms) */ - u8 auto_ps_mode; + CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR, /* - * This parameter defines the compensation percentage of num of probe - * requests in case scan is initiated during BT voice/BT ACL - * guaranteed link. + * RX guard time before the beginning of a new BT voice frame during + * which no new WLAN trigger frame is transmitted. * - * Unit: Percent - * Range: 0-255 (0 - No compensation) + * Range: 0 - 100000 (us) */ - u8 probe_req_compensation; + CONF_SG_RXT, /* - * This parameter defines the compensation percentage of scan window - * size in case scan is initiated during BT voice/BT ACL Guaranteed - * link. + * TX guard time before the beginning of a new BT voice frame during + * which no new WLAN frame is transmitted. * - * Unit: Percent - * Range: 0-255 (0 - No compensation) + * Range: 0 - 100000 (us) */ - u8 scan_window_compensation; + + CONF_SG_TXT, /* - * Defines the antenna configuration. + * Enable adaptive RXT/TXT algorithm. If disabled, the host values + * will be utilized. * - * Range: 0 - Single Antenna; 1 - Dual Antenna + * Range: 0 - disable, 1 - enable */ - u8 antenna_config; + CONF_SG_ADAPTIVE_RXT_TXT, /* - * The percent out of the Max consecutive beacon miss roaming trigger - * which is the threshold for raising the priority of beacon - * reception. + * The used WLAN legacy service period during active BT ACL link * - * Range: 1-100 - * N = MaxConsecutiveBeaconMiss - * P = coexMaxConsecutiveBeaconMissPrecent - * Threshold = MIN( N-1, round(N * P / 100)) + * Range: 0 - 255 (ms) */ - u8 beacon_miss_threshold; + CONF_SG_PS_POLL_TIMEOUT, /* - * The RX rate threshold below which rate adaptation is assumed to be - * occurring at the AP which will raise priority for ACTIVE_RX and RX - * SP. + * The used WLAN UPSD service period during active BT ACL link * - * Range: HW_BIT_RATE_* + * Range: 0 - 255 (ms) */ - u32 rate_adaptation_threshold; + CONF_SG_UPSD_TIMEOUT, /* - * The SNR above which the RX rate threshold indicating AP rate - * adaptation is valid + * Configure the min and max time BT gains the antenna + * in WLAN Active / BT master EDR * - * Range: -128 - 127 + * Range: 0 - 255 (ms) */ - s8 rate_adaptation_snr; + CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR, + CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR, + + /* + * The maximum time WLAN can gain the antenna for + * in WLAN Active / BT master EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN Active / BT slave EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR, + CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR, + + /* + * The maximum time WLAN can gain the antenna for + * in WLAN Active / BT slave EDR + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR, + + /* + * Configure the min and max time BT gains the antenna + * in WLAN Active / BT basic rate + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR, + CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR, + + /* + * The maximum time WLAN can gain the antenna for + * in WLAN Active / BT basic rate + * + * Range: 0 - 255 (ms) + */ + CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR, + + /* + * Compensation percentage of WLAN passive scan window if initiated + * during BT voice + * + * Range: 0 - 1000 (%) + */ + CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3, + + /* + * Compensation percentage of WLAN passive scan window if initiated + * during BT A2DP + * + * Range: 0 - 1000 (%) + */ + CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP, + + /* + * Fixed time ensured for BT traffic to gain the antenna during WLAN + * passive scan. + * + * Range: 0 - 1000 ms + */ + CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME, + + /* + * Fixed time ensured for WLAN traffic to gain the antenna during WLAN + * passive scan. + * + * Range: 0 - 1000 ms + */ + CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME, + + /* + * Number of consequent BT voice frames not interrupted by WLAN + * + * Range: 0 - 100 + */ + CONF_SG_HV3_MAX_SERVED, + + /* + * Protection time of the DHCP procedure. + * + * Range: 0 - 100000 (ms) + */ + CONF_SG_DHCP_TIME, + + /* + * Compensation percentage of WLAN active scan window if initiated + * during BT A2DP + * + * Range: 0 - 1000 (%) + */ + CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP, + CONF_SG_TEMP_PARAM_1, + CONF_SG_TEMP_PARAM_2, + CONF_SG_TEMP_PARAM_3, + CONF_SG_TEMP_PARAM_4, + CONF_SG_TEMP_PARAM_5, + CONF_SG_PARAMS_MAX, + CONF_SG_PARAMS_ALL = 0xff +}; + +struct conf_sg_settings { + __le32 params[CONF_SG_PARAMS_MAX]; + u8 state; }; enum conf_rx_queue_type { diff --git a/drivers/net/wireless/wl12xx/wl1271_debugfs.c b/drivers/net/wireless/wl12xx/wl1271_debugfs.c index 8d7588ca68fd..3c0f5b1ac272 100644 --- a/drivers/net/wireless/wl12xx/wl1271_debugfs.c +++ b/drivers/net/wireless/wl12xx/wl1271_debugfs.c @@ -28,6 +28,7 @@ #include "wl1271.h" #include "wl1271_acx.h" #include "wl1271_ps.h" +#include "wl1271_io.h" /* ms */ #define WL1271_DEBUGFS_STATS_LIFETIME 1000 @@ -276,13 +277,10 @@ static ssize_t gpio_power_write(struct file *file, goto out; } - if (value) { - wl->set_power(true); - set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); - } else { - wl->set_power(false); - clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); - } + if (value) + wl1271_power_on(wl); + else + wl1271_power_off(wl); out: mutex_unlock(&wl->mutex); diff --git a/drivers/net/wireless/wl12xx/wl1271_event.c b/drivers/net/wireless/wl12xx/wl1271_event.c index 5533519a1418..4d35af96c597 100644 --- a/drivers/net/wireless/wl12xx/wl1271_event.c +++ b/drivers/net/wireless/wl12xx/wl1271_event.c @@ -44,7 +44,9 @@ static int wl1271_event_scan_complete(struct wl1271 *wl, * scanning as it checks that. */ clear_bit(WL1271_FLAG_SCANNING, &wl->flags); + /* FIXME: ie missing! */ wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len, + NULL, 0, wl->scan.active, wl->scan.high_prio, WL1271_SCAN_BAND_5_GHZ, diff --git a/drivers/net/wireless/wl12xx/wl1271_init.c b/drivers/net/wireless/wl12xx/wl1271_init.c index 86c30a86a456..d9335fc2a575 100644 --- a/drivers/net/wireless/wl12xx/wl1271_init.c +++ b/drivers/net/wireless/wl12xx/wl1271_init.c @@ -160,11 +160,11 @@ int wl1271_init_pta(struct wl1271 *wl) { int ret; - ret = wl1271_acx_sg_enable(wl); + ret = wl1271_acx_sg_cfg(wl); if (ret < 0) return ret; - ret = wl1271_acx_sg_cfg(wl); + ret = wl1271_acx_sg_enable(wl, wl->sg_enabled); if (ret < 0) return ret; diff --git a/drivers/net/wireless/wl12xx/wl1271_io.h b/drivers/net/wireless/wl12xx/wl1271_io.h index 95d2168f8af4..d8837ef0bb40 100644 --- a/drivers/net/wireless/wl12xx/wl1271_io.h +++ b/drivers/net/wireless/wl12xx/wl1271_io.h @@ -138,6 +138,18 @@ static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val) wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val); } +static inline void wl1271_power_off(struct wl1271 *wl) +{ + wl->if_ops->power(wl, false); + clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); +} + +static inline void wl1271_power_on(struct wl1271 *wl) +{ + wl->if_ops->power(wl, true); + set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); +} + /* Top Register IO */ void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val); @@ -149,6 +161,7 @@ int wl1271_set_partition(struct wl1271 *wl, /* Functions from wl1271_main.c */ int wl1271_register_hw(struct wl1271 *wl); +void wl1271_unregister_hw(struct wl1271 *wl); int wl1271_init_ieee80211(struct wl1271 *wl); struct ieee80211_hw *wl1271_alloc_hw(void); int wl1271_free_hw(struct wl1271 *wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_main.c b/drivers/net/wireless/wl12xx/wl1271_main.c index 0a4ff7b02f59..3daba6c0c77f 100644 --- a/drivers/net/wireless/wl12xx/wl1271_main.c +++ b/drivers/net/wireless/wl12xx/wl1271_main.c @@ -29,6 +29,7 @@ #include <linux/etherdevice.h> #include <linux/vmalloc.h> #include <linux/inetdevice.h> +#include <linux/platform_device.h> #include "wl1271.h" #include "wl12xx_80211.h" @@ -48,17 +49,57 @@ static struct conf_drv_settings default_conf = { .sg = { - .per_threshold = 7500, - .max_scan_compensation_time = 120000, - .nfs_sample_interval = 400, - .load_ratio = 50, - .auto_ps_mode = 0, - .probe_req_compensation = 170, - .scan_window_compensation = 50, - .antenna_config = 0, - .beacon_miss_threshold = 60, - .rate_adaptation_threshold = CONF_HW_BIT_RATE_12MBPS, - .rate_adaptation_snr = 0 + .params = { + [CONF_SG_BT_PER_THRESHOLD] = 7500, + [CONF_SG_HV3_MAX_OVERRIDE] = 0, + [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400, + [CONF_SG_BT_LOAD_RATIO] = 50, + [CONF_SG_AUTO_PS_MODE] = 0, + [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, + [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, + [CONF_SG_ANTENNA_CONFIGURATION] = 0, + [CONF_SG_BEACON_MISS_PERCENT] = 60, + [CONF_SG_RATE_ADAPT_THRESH] = 12, + [CONF_SG_RATE_ADAPT_SNR] = 0, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR] = 10, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR] = 30, + [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR] = 8, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR] = 20, + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR] = 50, + /* Note: with UPSD, this should be 4 */ + [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR] = 8, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR] = 7, + [CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR] = 25, + [CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR] = 20, + /* Note: with UPDS, this should be 15 */ + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR] = 8, + /* Note: with UPDS, this should be 50 */ + [CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR] = 40, + /* Note: with UPDS, this should be 10 */ + [CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR] = 20, + [CONF_SG_RXT] = 1200, + [CONF_SG_TXT] = 1000, + [CONF_SG_ADAPTIVE_RXT_TXT] = 1, + [CONF_SG_PS_POLL_TIMEOUT] = 10, + [CONF_SG_UPSD_TIMEOUT] = 10, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR] = 8, + [CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR] = 20, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR] = 15, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR] = 20, + [CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR] = 50, + [CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR] = 10, + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200, + [CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800, + [CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME] = 75, + [CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME] = 15, + [CONF_SG_HV3_MAX_SERVED] = 6, + [CONF_SG_DHCP_TIME] = 5000, + [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100, + }, + .state = CONF_SG_PROTECTIVE, }, .rx = { .rx_msdu_life_time = 512000, @@ -240,6 +281,21 @@ static struct conf_drv_settings default_conf = { } }; +static void wl1271_device_release(struct device *dev) +{ + +} + +static struct platform_device wl1271_device = { + .name = "wl1271", + .id = -1, + + /* device model insists to have a release function */ + .dev = { + .release = wl1271_device_release, + }, +}; + static LIST_HEAD(wl_list); static void wl1271_conf_init(struct wl1271 *wl) @@ -359,18 +415,6 @@ static int wl1271_plt_init(struct wl1271 *wl) return ret; } -static void wl1271_power_off(struct wl1271 *wl) -{ - wl->set_power(false); - clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); -} - -static void wl1271_power_on(struct wl1271 *wl) -{ - wl->set_power(true); - set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); -} - static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_status *status) { @@ -526,40 +570,6 @@ out: return ret; } -static int wl1271_update_mac_addr(struct wl1271 *wl) -{ - int ret = 0; - u8 *nvs_ptr = (u8 *)wl->nvs->nvs; - - /* get mac address from the NVS */ - wl->mac_addr[0] = nvs_ptr[11]; - wl->mac_addr[1] = nvs_ptr[10]; - wl->mac_addr[2] = nvs_ptr[6]; - wl->mac_addr[3] = nvs_ptr[5]; - wl->mac_addr[4] = nvs_ptr[4]; - wl->mac_addr[5] = nvs_ptr[3]; - - /* FIXME: if it is a zero-address, we should bail out. Now, instead, - we randomize an address */ - if (is_zero_ether_addr(wl->mac_addr)) { - static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; - memcpy(wl->mac_addr, nokia_oui, 3); - get_random_bytes(wl->mac_addr + 3, 3); - - /* update this address to the NVS */ - nvs_ptr[11] = wl->mac_addr[0]; - nvs_ptr[10] = wl->mac_addr[1]; - nvs_ptr[6] = wl->mac_addr[2]; - nvs_ptr[5] = wl->mac_addr[3]; - nvs_ptr[4] = wl->mac_addr[4]; - nvs_ptr[3] = wl->mac_addr[5]; - } - - SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr); - - return ret; -} - static int wl1271_fetch_nvs(struct wl1271 *wl) { const struct firmware *fw; @@ -589,8 +599,6 @@ static int wl1271_fetch_nvs(struct wl1271 *wl) memcpy(wl->nvs, fw->data, sizeof(struct wl1271_nvs_file)); - ret = wl1271_update_mac_addr(wl); - out: release_firmware(fw); @@ -908,13 +916,58 @@ static struct notifier_block wl1271_dev_notifier = { static int wl1271_op_start(struct ieee80211_hw *hw) { + wl1271_debug(DEBUG_MAC80211, "mac80211 start"); + + /* + * We have to delay the booting of the hardware because + * we need to know the local MAC address before downloading and + * initializing the firmware. The MAC address cannot be changed + * after boot, and without the proper MAC address, the firmware + * will not function properly. + * + * The MAC address is first known when the corresponding interface + * is added. That is where we will initialize the hardware. + */ + + return 0; +} + +static void wl1271_op_stop(struct ieee80211_hw *hw) +{ + wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); +} + +static int wl1271_op_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ struct wl1271 *wl = hw->priv; int retries = WL1271_BOOT_RETRIES; int ret = 0; - wl1271_debug(DEBUG_MAC80211, "mac80211 start"); + wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", + vif->type, vif->addr); mutex_lock(&wl->mutex); + if (wl->vif) { + ret = -EBUSY; + goto out; + } + + wl->vif = vif; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + wl->bss_type = BSS_TYPE_STA_BSS; + break; + case NL80211_IFTYPE_ADHOC: + wl->bss_type = BSS_TYPE_IBSS; + break; + default: + ret = -EOPNOTSUPP; + goto out; + } + + memcpy(wl->mac_addr, vif->addr, ETH_ALEN); if (wl->state != WL1271_STATE_OFF) { wl1271_error("cannot start because not in off state: %d", @@ -970,19 +1023,20 @@ out: return ret; } -static void wl1271_op_stop(struct ieee80211_hw *hw) +static void wl1271_op_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; int i; - wl1271_info("down"); - - wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); - unregister_inetaddr_notifier(&wl1271_dev_notifier); - list_del(&wl->list); mutex_lock(&wl->mutex); + wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); + + wl1271_info("down"); + + list_del(&wl->list); WARN_ON(wl->state != WL1271_STATE_ON); @@ -1026,6 +1080,8 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) wl->rate_set = CONF_TX_RATE_MASK_BASIC; wl->sta_rate_set = 0; wl->flags = 0; + wl->vif = NULL; + wl->filters = 0; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -1034,119 +1090,39 @@ static void wl1271_op_stop(struct ieee80211_hw *hw) mutex_unlock(&wl->mutex); } -static int wl1271_op_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) +static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters) { - struct wl1271 *wl = hw->priv; - int ret = 0; + wl->rx_config = WL1271_DEFAULT_RX_CONFIG; + wl->rx_filter = WL1271_DEFAULT_RX_FILTER; - wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", - vif->type, vif->addr); + /* combine requested filters with current filter config */ + filters = wl->filters | filters; - mutex_lock(&wl->mutex); - if (wl->vif) { - ret = -EBUSY; - goto out; - } + wl1271_debug(DEBUG_FILTERS, "RX filters set: "); - wl->vif = vif; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - wl->bss_type = BSS_TYPE_STA_BSS; - break; - case NL80211_IFTYPE_ADHOC: - wl->bss_type = BSS_TYPE_IBSS; - break; - default: - ret = -EOPNOTSUPP; - goto out; + if (filters & FIF_PROMISC_IN_BSS) { + wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS"); + wl->rx_config &= ~CFG_UNI_FILTER_EN; + wl->rx_config |= CFG_BSSID_FILTER_EN; } - - /* FIXME: what if conf->mac_addr changes? */ - -out: - mutex_unlock(&wl->mutex); - return ret; -} - -static void wl1271_op_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct wl1271 *wl = hw->priv; - - mutex_lock(&wl->mutex); - wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); - wl->vif = NULL; - mutex_unlock(&wl->mutex); -} - -#if 0 -static int wl1271_op_config_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_if_conf *conf) -{ - struct wl1271 *wl = hw->priv; - struct sk_buff *beacon; - int ret; - - wl1271_debug(DEBUG_MAC80211, "mac80211 config_interface bssid %pM", - conf->bssid); - wl1271_dump_ascii(DEBUG_MAC80211, "ssid: ", conf->ssid, - conf->ssid_len); - - mutex_lock(&wl->mutex); - - ret = wl1271_ps_elp_wakeup(wl, false); - if (ret < 0) - goto out; - - if (memcmp(wl->bssid, conf->bssid, ETH_ALEN)) { - wl1271_debug(DEBUG_MAC80211, "bssid changed"); - - memcpy(wl->bssid, conf->bssid, ETH_ALEN); - - ret = wl1271_cmd_join(wl, wl->bss_type); - if (ret < 0) - goto out_sleep; - - ret = wl1271_cmd_build_null_data(wl); - if (ret < 0) - goto out_sleep; + if (filters & FIF_BCN_PRBRESP_PROMISC) { + wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC"); + wl->rx_config &= ~CFG_BSSID_FILTER_EN; + wl->rx_config &= ~CFG_SSID_FILTER_EN; } - - wl->ssid_len = conf->ssid_len; - if (wl->ssid_len) - memcpy(wl->ssid, conf->ssid, wl->ssid_len); - - if (conf->changed & IEEE80211_IFCC_BEACON) { - beacon = ieee80211_beacon_get(hw, vif); - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, - beacon->data, beacon->len); - - if (ret < 0) { - dev_kfree_skb(beacon); - goto out_sleep; - } - - ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, - beacon->data, beacon->len); - - dev_kfree_skb(beacon); - - if (ret < 0) - goto out_sleep; + if (filters & FIF_OTHER_BSS) { + wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS"); + wl->rx_config &= ~CFG_BSSID_FILTER_EN; + } + if (filters & FIF_CONTROL) { + wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL"); + wl->rx_filter |= CFG_RX_CTL_EN; + } + if (filters & FIF_FCSFAIL) { + wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL"); + wl->rx_filter |= CFG_RX_FCS_ERROR; } - -out_sleep: - wl1271_ps_elp_sleep(wl); - -out: - mutex_unlock(&wl->mutex); - - return ret; } -#endif static int wl1271_join_channel(struct wl1271 *wl, int channel) { @@ -1155,12 +1131,12 @@ static int wl1271_join_channel(struct wl1271 *wl, int channel) static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde, 0xad, 0xbe, 0xef }; - /* disable mac filter, so we hear everything */ - wl->rx_config &= ~CFG_BSSID_FILTER_EN; - wl->channel = channel; memcpy(wl->bssid, dummy_bssid, ETH_ALEN); + /* pass through frames from all BSS */ + wl1271_configure_filters(wl, FIF_OTHER_BSS); + /* the dummy join is performed always with STATION BSS type to allow also ad-hoc mode to listen to the surroundings without sending any beacons yet. */ @@ -1186,7 +1162,9 @@ static int wl1271_unjoin_channel(struct wl1271 *wl) clear_bit(WL1271_FLAG_JOINED, &wl->flags); wl->channel = 0; memset(wl->bssid, 0, ETH_ALEN); - wl->rx_config = WL1271_DEFAULT_RX_CONFIG; + + /* stop filterting packets based on bssid */ + wl1271_configure_filters(wl, FIF_OTHER_BSS); out: return ret; @@ -1359,14 +1337,14 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - kfree(fp); - - /* FIXME: We still need to set our filters properly */ - /* determine, whether supported filter values have changed */ if (changed == 0) goto out_sleep; + /* configure filters */ + wl->filters = *total; + wl1271_configure_filters(wl, 0); + /* apply configured filters */ ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter); if (ret < 0) @@ -1377,6 +1355,7 @@ out_sleep: out: mutex_unlock(&wl->mutex); + kfree(fp); } static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -1522,10 +1501,12 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw, goto out; if (wl1271_11a_enabled()) - ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0, + ret = wl1271_cmd_scan(hw->priv, ssid, len, + req->ie, req->ie_len, 1, 0, WL1271_SCAN_BAND_DUAL, 3); else - ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0, + ret = wl1271_cmd_scan(hw->priv, ssid, len, + req->ie, req->ie_len, 1, 0, WL1271_SCAN_BAND_2_4_GHZ, 3); wl1271_ps_elp_sleep(wl); @@ -1638,14 +1619,14 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, * and enable the BSSID filter */ memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { - wl->rx_config |= CFG_BSSID_FILTER_EN; memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); + ret = wl1271_cmd_build_null_data(wl); - if (ret < 0) { - wl1271_warning("cmd buld null data failed %d", - ret); + if (ret < 0) goto out_sleep; - } + + /* filter out all packets not from this BSSID */ + wl1271_configure_filters(wl, 0); /* Need to update the BSSID (for filtering etc) */ do_join = true; @@ -1735,6 +1716,7 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params) { struct wl1271 *wl = hw->priv; + u8 ps_scheme; int ret; mutex_lock(&wl->mutex); @@ -1745,17 +1727,22 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue, if (ret < 0) goto out; + /* the txop is confed in units of 32us by the mac80211, we need us */ ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue), params->cw_min, params->cw_max, - params->aifs, params->txop); + params->aifs, params->txop << 5); if (ret < 0) goto out_sleep; + if (params->uapsd) + ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER; + else + ps_scheme = CONF_PS_SCHEME_LEGACY; + ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue), CONF_CHANNEL_TYPE_EDCF, wl1271_tx_get_queue(queue), - CONF_PS_SCHEME_LEGACY_PSPOLL, - CONF_ACK_POLICY_LEGACY, 0, 0); + ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0); if (ret < 0) goto out_sleep; @@ -1925,7 +1912,6 @@ static const struct ieee80211_ops wl1271_ops = { .add_interface = wl1271_op_add_interface, .remove_interface = wl1271_op_remove_interface, .config = wl1271_op_config, -/* .config_interface = wl1271_op_config_interface, */ .prepare_multicast = wl1271_op_prepare_multicast, .configure_filter = wl1271_op_configure_filter, .tx = wl1271_op_tx, @@ -1937,6 +1923,68 @@ static const struct ieee80211_ops wl1271_ops = { CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; +static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wl1271 *wl = dev_get_drvdata(dev); + ssize_t len; + + /* FIXME: what's the maximum length of buf? page size?*/ + len = 500; + + mutex_lock(&wl->mutex); + len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n", + wl->sg_enabled); + mutex_unlock(&wl->mutex); + + return len; + +} + +static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct wl1271 *wl = dev_get_drvdata(dev); + unsigned long res; + int ret; + + ret = strict_strtoul(buf, 10, &res); + + if (ret < 0) { + wl1271_warning("incorrect value written to bt_coex_mode"); + return count; + } + + mutex_lock(&wl->mutex); + + res = !!res; + + if (res == wl->sg_enabled) + goto out; + + wl->sg_enabled = res; + + if (wl->state == WL1271_STATE_OFF) + goto out; + + ret = wl1271_ps_elp_wakeup(wl, false); + if (ret < 0) + goto out; + + wl1271_acx_sg_enable(wl, wl->sg_enabled); + wl1271_ps_elp_sleep(wl); + + out: + mutex_unlock(&wl->mutex); + return count; +} + +static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR, + wl1271_sysfs_show_bt_coex_state, + wl1271_sysfs_store_bt_coex_state); + int wl1271_register_hw(struct wl1271 *wl) { int ret; @@ -1960,6 +2008,14 @@ int wl1271_register_hw(struct wl1271 *wl) } EXPORT_SYMBOL_GPL(wl1271_register_hw); +void wl1271_unregister_hw(struct wl1271 *wl) +{ + ieee80211_unregister_hw(wl->hw); + wl->mac80211_registered = false; + +} +EXPORT_SYMBOL_GPL(wl1271_unregister_hw); + int wl1271_init_ieee80211(struct wl1271 *wl) { /* The tx descriptor buffer and the TKIP space. */ @@ -1974,6 +2030,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_NOISE_DBM | IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | @@ -1984,6 +2041,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl) if (wl1271_11a_enabled()) wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz; + wl->hw->queues = 4; + SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl)); return 0; @@ -1995,21 +2054,34 @@ EXPORT_SYMBOL_GPL(wl1271_init_ieee80211); struct ieee80211_hw *wl1271_alloc_hw(void) { struct ieee80211_hw *hw; + struct platform_device *plat_dev = NULL; struct wl1271 *wl; - int i; + int i, ret; + static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf}; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); if (!hw) { wl1271_error("could not alloc ieee80211_hw"); - return ERR_PTR(-ENOMEM); + ret = -ENOMEM; + goto err_hw_alloc; } + plat_dev = kmalloc(sizeof(wl1271_device), GFP_KERNEL); + if (!plat_dev) { + wl1271_error("could not allocate platform_device"); + ret = -ENOMEM; + goto err_plat_alloc; + } + + memcpy(plat_dev, &wl1271_device, sizeof(wl1271_device)); + wl = hw->priv; memset(wl, 0, sizeof(*wl)); INIT_LIST_HEAD(&wl->list); wl->hw = hw; + wl->plat_dev = plat_dev; skb_queue_head_init(&wl->tx_queue); @@ -2027,6 +2099,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->band = IEEE80211_BAND_2GHZ; wl->vif = NULL; wl->flags = 0; + wl->sg_enabled = true; for (i = 0; i < ACX_TX_DESCRIPTORS; i++) wl->tx_frames[i] = NULL; @@ -2036,18 +2109,55 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->state = WL1271_STATE_OFF; mutex_init(&wl->mutex); + /* + * FIXME: we should use a zero MAC address here, but for now we + * generate a random Nokia address. + */ + memcpy(wl->mac_addr, nokia_oui, 3); + get_random_bytes(wl->mac_addr + 3, 3); + /* Apply default driver configuration. */ wl1271_conf_init(wl); wl1271_debugfs_init(wl); + /* Register platform device */ + ret = platform_device_register(wl->plat_dev); + if (ret) { + wl1271_error("couldn't register platform device"); + goto err_hw; + } + dev_set_drvdata(&wl->plat_dev->dev, wl); + + /* Create sysfs file to control bt coex state */ + ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state); + if (ret < 0) { + wl1271_error("failed to create sysfs file bt_coex_state"); + goto err_platform; + } + return hw; + +err_platform: + platform_device_unregister(wl->plat_dev); + +err_hw: + wl1271_debugfs_exit(wl); + kfree(plat_dev); + +err_plat_alloc: + ieee80211_free_hw(hw); + +err_hw_alloc: + + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(wl1271_alloc_hw); int wl1271_free_hw(struct wl1271 *wl) { - ieee80211_unregister_hw(wl->hw); + platform_device_unregister(wl->plat_dev); + kfree(wl->plat_dev); wl1271_debugfs_exit(wl); diff --git a/drivers/net/wireless/wl12xx/wl1271_sdio.c b/drivers/net/wireless/wl12xx/wl1271_sdio.c index 1f204db30c27..3c03de74dbfc 100644 --- a/drivers/net/wireless/wl12xx/wl1271_sdio.c +++ b/drivers/net/wireless/wl12xx/wl1271_sdio.c @@ -102,15 +102,14 @@ static void wl1271_sdio_init(struct wl1271 *wl) } static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) + size_t len, bool fixed) { int ret; struct sdio_func *func = wl_to_func(wl); - sdio_claim_host(func); if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { ((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret); - wl1271_debug(DEBUG_SPI, "sdio read 52 addr 0x%x, byte 0x%02x", + wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x", addr, ((u8 *)buf)[0]); } else { if (fixed) @@ -118,32 +117,30 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf, else ret = sdio_memcpy_fromio(func, buf, addr, len); - wl1271_debug(DEBUG_SPI, "sdio read 53 addr 0x%x, %d bytes", + wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %d bytes", addr, len); - wl1271_dump_ascii(DEBUG_SPI, "data: ", buf, len); + wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len); } if (ret) wl1271_error("sdio read failed (%d)", ret); - sdio_release_host(func); } static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, - size_t len, bool fixed) + size_t len, bool fixed) { int ret; struct sdio_func *func = wl_to_func(wl); - sdio_claim_host(func); if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret); - wl1271_debug(DEBUG_SPI, "sdio write 52 addr 0x%x, byte 0x%02x", + wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x", addr, ((u8 *)buf)[0]); } else { - wl1271_debug(DEBUG_SPI, "sdio write 53 addr 0x%x, %d bytes", + wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %d bytes", addr, len); - wl1271_dump_ascii(DEBUG_SPI, "data: ", buf, len); + wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len); if (fixed) ret = sdio_writesb(func, addr, buf, len); @@ -153,7 +150,23 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf, if (ret) wl1271_error("sdio write failed (%d)", ret); - sdio_release_host(func); +} + +static void wl1271_sdio_set_power(struct wl1271 *wl, bool enable) +{ + struct sdio_func *func = wl_to_func(wl); + + /* Let the SDIO stack handle wlan_enable control, so we + * keep host claimed while wlan is in use to keep wl1271 + * alive. + */ + if (enable) { + sdio_claim_host(func); + sdio_enable_func(func); + } else { + sdio_disable_func(func); + sdio_release_host(func); + } } static struct wl1271_if_operations sdio_ops = { @@ -161,15 +174,12 @@ static struct wl1271_if_operations sdio_ops = { .write = wl1271_sdio_raw_write, .reset = wl1271_sdio_reset, .init = wl1271_sdio_init, + .power = wl1271_sdio_set_power, .dev = wl1271_sdio_wl_to_dev, .enable_irq = wl1271_sdio_enable_interrupts, .disable_irq = wl1271_sdio_disable_interrupts }; -static void wl1271_sdio_set_power(bool enable) -{ -} - static int __devinit wl1271_probe(struct sdio_func *func, const struct sdio_device_id *id) { @@ -190,8 +200,6 @@ static int __devinit wl1271_probe(struct sdio_func *func, wl->if_priv = func; wl->if_ops = &sdio_ops; - wl->set_power = wl1271_sdio_set_power; - /* Grab access to FN0 for ELP reg. */ func->card->quirks |= MMC_QUIRK_LENIENT_FN0; @@ -220,28 +228,18 @@ static int __devinit wl1271_probe(struct sdio_func *func, if (ret) goto out_irq; - sdio_claim_host(func); sdio_set_drvdata(func, wl); - ret = sdio_enable_func(func); - if (ret) - goto out_release; - - sdio_release_host(func); - wl1271_notice("initialized"); return 0; - out_release: - sdio_release_host(func); - out_irq: free_irq(wl->irq, wl); out_free: - ieee80211_free_hw(hw); + wl1271_free_hw(wl); return ret; } @@ -250,24 +248,10 @@ static void __devexit wl1271_remove(struct sdio_func *func) { struct wl1271 *wl = sdio_get_drvdata(func); - ieee80211_unregister_hw(wl->hw); - - sdio_claim_host(func); - sdio_disable_func(func); - sdio_release_host(func); - free_irq(wl->irq, wl); - kfree(wl->target_mem_map); - vfree(wl->fw); - wl->fw = NULL; - kfree(wl->nvs); - wl->nvs = NULL; - - kfree(wl->fw_status); - kfree(wl->tx_res_if); - - ieee80211_free_hw(wl->hw); + wl1271_unregister_hw(wl); + wl1271_free_hw(wl); } static struct sdio_driver wl1271_sdio_driver = { diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index ed285fec2a08..f44b05a32b0d 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -23,7 +23,6 @@ #include <linux/irq.h> #include <linux/module.h> -#include <linux/platform_device.h> #include <linux/crc7.h> #include <linux/spi/spi.h> #include <linux/spi/wl12xx.h> @@ -332,26 +331,18 @@ static irqreturn_t wl1271_irq(int irq, void *cookie) return IRQ_HANDLED; } -static void wl1271_device_release(struct device *dev) +static void wl1271_spi_set_power(struct wl1271 *wl, bool enable) { - + if (wl->set_power) + wl->set_power(enable); } -static struct platform_device wl1271_device = { - .name = "wl1271", - .id = -1, - - /* device model insists to have a release function */ - .dev = { - .release = wl1271_device_release, - }, -}; - static struct wl1271_if_operations spi_ops = { .read = wl1271_spi_raw_read, .write = wl1271_spi_raw_write, .reset = wl1271_spi_reset, .init = wl1271_spi_init, + .power = wl1271_spi_set_power, .dev = wl1271_spi_wl_to_dev, .enable_irq = wl1271_spi_enable_interrupts, .disable_irq = wl1271_spi_disable_interrupts @@ -415,33 +406,23 @@ static int __devinit wl1271_probe(struct spi_device *spi) disable_irq(wl->irq); - ret = platform_device_register(&wl1271_device); - if (ret) { - wl1271_error("couldn't register platform device"); - goto out_irq; - } - dev_set_drvdata(&wl1271_device.dev, wl); - ret = wl1271_init_ieee80211(wl); if (ret) - goto out_platform; + goto out_irq; ret = wl1271_register_hw(wl); if (ret) - goto out_platform; + goto out_irq; wl1271_notice("initialized"); return 0; - out_platform: - platform_device_unregister(&wl1271_device); - out_irq: free_irq(wl->irq, wl); out_free: - ieee80211_free_hw(hw); + wl1271_free_hw(wl); return ret; } @@ -450,9 +431,9 @@ static int __devexit wl1271_remove(struct spi_device *spi) { struct wl1271 *wl = dev_get_drvdata(&spi->dev); - platform_device_unregister(&wl1271_device); free_irq(wl->irq, wl); + wl1271_unregister_hw(wl); wl1271_free_hw(wl); return 0; diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.h b/drivers/net/wireless/wl12xx/wl1271_tx.h index 8b9f6b4f5652..5e6c27a57415 100644 --- a/drivers/net/wireless/wl12xx/wl1271_tx.h +++ b/drivers/net/wireless/wl12xx/wl1271_tx.h @@ -125,9 +125,6 @@ struct wl1271_tx_hw_res_if { static inline int wl1271_tx_get_queue(int queue) { - /* FIXME: use best effort until WMM is enabled */ - return CONF_TX_AC_BE; - switch (queue) { case 0: return CONF_TX_AC_VO; diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 7b9621de239f..65dd502eab0d 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -1834,32 +1834,32 @@ out: } static const iw_handler wl3501_handler[] = { - [SIOCGIWNAME - SIOCIWFIRST] = wl3501_get_name, - [SIOCSIWFREQ - SIOCIWFIRST] = wl3501_set_freq, - [SIOCGIWFREQ - SIOCIWFIRST] = wl3501_get_freq, - [SIOCSIWMODE - SIOCIWFIRST] = wl3501_set_mode, - [SIOCGIWMODE - SIOCIWFIRST] = wl3501_get_mode, - [SIOCGIWSENS - SIOCIWFIRST] = wl3501_get_sens, - [SIOCGIWRANGE - SIOCIWFIRST] = wl3501_get_range, - [SIOCSIWSPY - SIOCIWFIRST] = iw_handler_set_spy, - [SIOCGIWSPY - SIOCIWFIRST] = iw_handler_get_spy, - [SIOCSIWTHRSPY - SIOCIWFIRST] = iw_handler_set_thrspy, - [SIOCGIWTHRSPY - SIOCIWFIRST] = iw_handler_get_thrspy, - [SIOCSIWAP - SIOCIWFIRST] = wl3501_set_wap, - [SIOCGIWAP - SIOCIWFIRST] = wl3501_get_wap, - [SIOCSIWSCAN - SIOCIWFIRST] = wl3501_set_scan, - [SIOCGIWSCAN - SIOCIWFIRST] = wl3501_get_scan, - [SIOCSIWESSID - SIOCIWFIRST] = wl3501_set_essid, - [SIOCGIWESSID - SIOCIWFIRST] = wl3501_get_essid, - [SIOCSIWNICKN - SIOCIWFIRST] = wl3501_set_nick, - [SIOCGIWNICKN - SIOCIWFIRST] = wl3501_get_nick, - [SIOCGIWRATE - SIOCIWFIRST] = wl3501_get_rate, - [SIOCGIWRTS - SIOCIWFIRST] = wl3501_get_rts_threshold, - [SIOCGIWFRAG - SIOCIWFIRST] = wl3501_get_frag_threshold, - [SIOCGIWTXPOW - SIOCIWFIRST] = wl3501_get_txpow, - [SIOCGIWRETRY - SIOCIWFIRST] = wl3501_get_retry, - [SIOCGIWENCODE - SIOCIWFIRST] = wl3501_get_encode, - [SIOCGIWPOWER - SIOCIWFIRST] = wl3501_get_power, + IW_HANDLER(SIOCGIWNAME, wl3501_get_name), + IW_HANDLER(SIOCSIWFREQ, wl3501_set_freq), + IW_HANDLER(SIOCGIWFREQ, wl3501_get_freq), + IW_HANDLER(SIOCSIWMODE, wl3501_set_mode), + IW_HANDLER(SIOCGIWMODE, wl3501_get_mode), + IW_HANDLER(SIOCGIWSENS, wl3501_get_sens), + IW_HANDLER(SIOCGIWRANGE, wl3501_get_range), + IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), + IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), + IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), + IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), + IW_HANDLER(SIOCSIWAP, wl3501_set_wap), + IW_HANDLER(SIOCGIWAP, wl3501_get_wap), + IW_HANDLER(SIOCSIWSCAN, wl3501_set_scan), + IW_HANDLER(SIOCGIWSCAN, wl3501_get_scan), + IW_HANDLER(SIOCSIWESSID, wl3501_set_essid), + IW_HANDLER(SIOCGIWESSID, wl3501_get_essid), + IW_HANDLER(SIOCSIWNICKN, wl3501_set_nick), + IW_HANDLER(SIOCGIWNICKN, wl3501_get_nick), + IW_HANDLER(SIOCGIWRATE, wl3501_get_rate), + IW_HANDLER(SIOCGIWRTS, wl3501_get_rts_threshold), + IW_HANDLER(SIOCGIWFRAG, wl3501_get_frag_threshold), + IW_HANDLER(SIOCGIWTXPOW, wl3501_get_txpow), + IW_HANDLER(SIOCGIWRETRY, wl3501_get_retry), + IW_HANDLER(SIOCGIWENCODE, wl3501_get_encode), + IW_HANDLER(SIOCGIWPOWER, wl3501_get_power), }; static const struct iw_handler_def wl3501_handler_def = { diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 28ba20fda3e2..daf6a3432b92 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -323,6 +323,12 @@ * the TX command and %NL80211_ATTR_FRAME includes the contents of the * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged * the frame. + * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command + * is used to configure connection quality monitoring notification trigger + * levels. + * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This + * command is used as an event to indicate the that a trigger level was + * reached. * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use @@ -419,6 +425,9 @@ enum nl80211_commands { NL80211_CMD_SET_POWER_SAVE, NL80211_CMD_GET_POWER_SAVE, + NL80211_CMD_SET_CQM, + NL80211_CMD_NOTIFY_CQM, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -691,6 +700,9 @@ enum nl80211_commands { * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * acknowledged by the recipient. * + * @NL80211_ATTR_CQM: connection quality monitor configuration in a + * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -842,6 +854,8 @@ enum nl80211_attrs { NL80211_ATTR_PS_STATE, + NL80211_ATTR_CQM, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1583,4 +1597,40 @@ enum nl80211_ps_state { NL80211_PS_ENABLED, }; +/** + * enum nl80211_attr_cqm - connection quality monitor attributes + * @__NL80211_ATTR_CQM_INVALID: invalid + * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies + * the threshold for the RSSI level at which an event will be sent. Zero + * to disable. + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies + * the minimum amount the RSSI level must change after an event before a + * new event may be issued (to reduce effects of RSSI oscillation). + * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event + * @__NL80211_ATTR_CQM_AFTER_LAST: internal + * @NL80211_ATTR_CQM_MAX: highest key attribute + */ +enum nl80211_attr_cqm { + __NL80211_ATTR_CQM_INVALID, + NL80211_ATTR_CQM_RSSI_THOLD, + NL80211_ATTR_CQM_RSSI_HYST, + NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + + /* keep last */ + __NL80211_ATTR_CQM_AFTER_LAST, + NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 +}; + +/** + * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the + * configured threshold + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the + * configured threshold + */ +enum nl80211_cqm_rssi_threshold_event { + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/linux/wireless.h b/include/linux/wireless.h index 5b4c6c772a9b..e6827eedf18b 100644 --- a/include/linux/wireless.h +++ b/include/linux/wireless.h @@ -346,6 +346,8 @@ #define SIOCIWFIRST 0x8B00 #define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ #define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) +#define IW_HANDLER(id, func) \ + [IW_IOCTL_IDX(id)] = func /* Odd : get (world access), even : set (root access) */ #define IW_IS_SET(cmd) (!((cmd) & 0x1)) @@ -648,7 +650,7 @@ * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ #define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ (cmd - SIOCIWFIRSTPRIV + 0x60) : \ - (cmd - SIOCSIWCOMMIT)) + (cmd - SIOCIWFIRST)) #define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) #define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) /* Event capability constants - event autogenerated by the kernel diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 3d134a1fb96b..868cfd3b9724 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -1007,6 +1007,7 @@ struct cfg80211_pmksa { * RSN IE. It allows for faster roaming between WPA2 BSSIDs. * @del_pmksa: Delete a cached PMKID. * @flush_pmksa: Flush all cached PMKIDs. + * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold. * */ struct cfg80211_ops { @@ -1152,6 +1153,10 @@ struct cfg80211_ops { int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, bool enabled, int timeout); + + int (*set_cqm_rssi_config)(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst); }; /* @@ -2337,4 +2342,18 @@ bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf, void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, const u8 *buf, size_t len, bool ack, gfp_t gfp); + +/** + * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event + * @dev: network device + * @rssi_event: the triggered RSSI event + * @gfp: context flags + * + * This function is called when a configured connection quality monitoring + * rssi threshold reached event occurs. + */ +void cfg80211_cqm_rssi_notify(struct net_device *dev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + #endif /* __NET_CFG80211_H */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 936bc410d061..1a8f50af49a0 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -144,6 +144,7 @@ struct ieee80211_low_level_stats { * new beacon (beaconing modes) * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be * enabled/disabled (beaconing modes) + * @BSS_CHANGED_CQM: Connection quality monitor config changed */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -156,6 +157,7 @@ enum ieee80211_bss_change { BSS_CHANGED_BSSID = 1<<7, BSS_CHANGED_BEACON = 1<<8, BSS_CHANGED_BEACON_ENABLED = 1<<9, + BSS_CHANGED_CQM = 1<<10, }; /** @@ -185,6 +187,9 @@ enum ieee80211_bss_change { * @enable_beacon: whether beaconing should be enabled or not * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info). * This field is only valid when the channel type is one of the HT types. + * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value + * implies disabled + * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis */ struct ieee80211_bss_conf { const u8 *bssid; @@ -202,6 +207,8 @@ struct ieee80211_bss_conf { u64 timestamp; u32 basic_rates; u16 ht_operation_mode; + s32 cqm_rssi_thold; + u32 cqm_rssi_hyst; }; /** @@ -954,6 +961,17 @@ enum ieee80211_tkip_key_type { * Hardware can provide ack status reports of Tx frames to * the stack. * + * @IEEE80211_HW_CONNECTION_MONITOR: + * The hardware performs its own connection monitoring, including + * periodic keep-alives to the AP and probing the AP on beacon loss. + * When this flag is set, signaling beacon-loss will cause an immediate + * change to disassociated state. + * + * @IEEE80211_HW_SUPPORTS_CQM_RSSI: + * Hardware can do connection quality monitoring - i.e. it can monitor + * connection quality related parameters, such as the RSSI level and + * provide notifications if configured trigger levels are reached. + * */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -975,6 +993,8 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, + IEEE80211_HW_CONNECTION_MONITOR = 1<<19, + IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20, }; /** @@ -2364,12 +2384,42 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, * * @vif: &struct ieee80211_vif pointer from the add_interface callback. * - * When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and - * IEEE80211_CONF_PS is set, the driver needs to inform whenever the + * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING and + * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the * hardware is not receiving beacons with this function. */ void ieee80211_beacon_loss(struct ieee80211_vif *vif); +/** + * ieee80211_connection_loss - inform hardware has lost connection to the AP + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING, and + * %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver + * needs to inform if the connection to the AP has been lost. + * + * This function will cause immediate change to disassociated state, + * without connection recovery attempts. + */ +void ieee80211_connection_loss(struct ieee80211_vif *vif); + +/** + * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring + * rssi threshold triggered + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @rssi_event: the RSSI trigger event type + * @gfp: context flags + * + * When the %IEEE80211_HW_SUPPORTS_CQM_RSSI is set, and a connection quality + * monitoring is configured with an rssi threshold, the driver will inform + * whenever the rssi level reaches the threshold. + */ +void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + /* Rate control API */ /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b7116ef84a3b..c8f520529eec 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1402,6 +1402,32 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_vif *vif = &sdata->vif; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + + if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) + return -EOPNOTSUPP; + + if (rssi_thold == bss_conf->cqm_rssi_thold && + rssi_hyst == bss_conf->cqm_rssi_hyst) + return 0; + + bss_conf->cqm_rssi_thold = rssi_thold; + bss_conf->cqm_rssi_hyst = rssi_hyst; + + /* tell the driver upon association, unless already associated */ + if (sdata->u.mgd.associated) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM); + + return 0; +} + static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, const u8 *addr, @@ -1506,4 +1532,5 @@ struct cfg80211_ops mac80211_config_ops = { .remain_on_channel = ieee80211_remain_on_channel, .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel, .action = ieee80211_action, + .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config, }; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b84126491ab1..ab369e2a5282 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -327,7 +327,7 @@ struct ieee80211_if_managed { struct work_struct work; struct work_struct monitor_work; struct work_struct chswitch_work; - struct work_struct beacon_loss_work; + struct work_struct beacon_connection_loss_work; unsigned long probe_timeout; int probe_send_count; @@ -1156,7 +1156,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, int powersave); void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, struct ieee80211_hdr *hdr); -void ieee80211_beacon_loss_work(struct work_struct *work); +void ieee80211_beacon_connection_loss_work(struct work_struct *work); void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, enum queue_stop_reason reason); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d5571b9420cd..b4ec59a8dc03 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -486,7 +486,7 @@ static int ieee80211_stop(struct net_device *dev) cancel_work_sync(&sdata->u.mgd.work); cancel_work_sync(&sdata->u.mgd.chswitch_work); cancel_work_sync(&sdata->u.mgd.monitor_work); - cancel_work_sync(&sdata->u.mgd.beacon_loss_work); + cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work); /* * When we get here, the interface is marked down. diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index be5f723d643a..34e0650e9ef8 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -753,6 +753,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, /* And the BSSID changed - we're associated now */ bss_info_changed |= BSS_CHANGED_BSSID; + /* Tell the driver to monitor connection quality (if supported) */ + if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) && + sdata->vif.bss_conf.cqm_rssi_thold) + bss_info_changed |= BSS_CHANGED_CQM; + ieee80211_bss_info_change_notify(sdata, bss_info_changed); mutex_lock(&local->iflist_mtx); @@ -854,6 +859,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, if (is_multicast_ether_addr(hdr->addr1)) return; + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) + return; + mod_timer(&sdata->u.mgd.conn_mon_timer, round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); } @@ -931,23 +939,68 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, mutex_unlock(&ifmgd->mtx); } -void ieee80211_beacon_loss_work(struct work_struct *work) +static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + struct ieee80211_local *local = sdata->local; + u8 bssid[ETH_ALEN]; + + mutex_lock(&ifmgd->mtx); + if (!ifmgd->associated) { + mutex_unlock(&ifmgd->mtx); + return; + } + + memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); + + printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid); + + ieee80211_set_disassoc(sdata); + ieee80211_recalc_idle(local); + mutex_unlock(&ifmgd->mtx); + /* + * must be outside lock due to cfg80211, + * but that's not a problem. + */ + ieee80211_send_deauth_disassoc(sdata, bssid, + IEEE80211_STYPE_DEAUTH, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, + NULL); +} + +void ieee80211_beacon_connection_loss_work(struct work_struct *work) { struct ieee80211_sub_if_data *sdata = container_of(work, struct ieee80211_sub_if_data, - u.mgd.beacon_loss_work); + u.mgd.beacon_connection_loss_work); - ieee80211_mgd_probe_ap(sdata, true); + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) + __ieee80211_connection_loss(sdata); + else + ieee80211_mgd_probe_ap(sdata, true); } void ieee80211_beacon_loss(struct ieee80211_vif *vif) { struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_hw *hw = &sdata->local->hw; - ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); + WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR); + ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); } EXPORT_SYMBOL(ieee80211_beacon_loss); +void ieee80211_connection_loss(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_hw *hw = &sdata->local->hw; + + WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR)); + ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work); +} +EXPORT_SYMBOL(ieee80211_connection_loss); + + static enum rx_mgmt_action __must_check ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) @@ -1637,7 +1690,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) if (local->quiescing) return; - ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); + ieee80211_queue_work(&sdata->local->hw, + &sdata->u.mgd.beacon_connection_loss_work); } static void ieee80211_sta_conn_mon_timer(unsigned long data) @@ -1689,7 +1743,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) */ cancel_work_sync(&ifmgd->work); - cancel_work_sync(&ifmgd->beacon_loss_work); + cancel_work_sync(&ifmgd->beacon_connection_loss_work); if (del_timer_sync(&ifmgd->timer)) set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); @@ -1723,7 +1777,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) INIT_WORK(&ifmgd->work, ieee80211_sta_work); INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); - INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work); + INIT_WORK(&ifmgd->beacon_connection_loss_work, + ieee80211_beacon_connection_loss_work); setup_timer(&ifmgd->timer, ieee80211_sta_timer, (unsigned long) sdata); setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, @@ -2135,3 +2190,13 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, *cookie = (unsigned long) skb; return 0; } + +void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); +} +EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index cbe53ed4fb0b..08e1f17a4226 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2010,14 +2010,12 @@ void ieee80211_tx_pending(unsigned long data) while (!skb_queue_empty(&local->pending[i])) { struct sk_buff *skb = __skb_dequeue(&local->pending[i]); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_sub_if_data *sdata; if (WARN_ON(!info->control.vif)) { kfree_skb(skb); continue; } - sdata = vif_to_sdata(info->control.vif); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 62bc8855e123..0855f0d32349 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -894,3 +894,16 @@ void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp); } EXPORT_SYMBOL(cfg80211_action_tx_status); + +void cfg80211_cqm_rssi_notify(struct net_device *dev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + /* Indicate roaming trigger event to user space */ + nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); +} +EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e447db04cf76..a7fc3d83f5f6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -149,6 +149,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, }; /* policy for the attributes */ @@ -4778,6 +4779,84 @@ unlock_rtnl: return err; } +static struct nla_policy +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, +}; + +static int nl80211_set_cqm_rssi(struct genl_info *info, + s32 threshold, u32 hysteresis) +{ + struct cfg80211_registered_device *rdev; + struct wireless_dev *wdev; + struct net_device *dev; + int err; + + if (threshold > 0) + return -EINVAL; + + rtnl_lock(); + + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); + if (err) + goto unlock_rdev; + + wdev = dev->ieee80211_ptr; + + if (!rdev->ops->set_cqm_rssi_config) { + err = -EOPNOTSUPP; + goto unlock_rdev; + } + + if (wdev->iftype != NL80211_IFTYPE_STATION) { + err = -EOPNOTSUPP; + goto unlock_rdev; + } + + err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, + threshold, hysteresis); + +unlock_rdev: + cfg80211_unlock_rdev(rdev); + dev_put(dev); + rtnl_unlock(); + + return err; +} + +static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) +{ + struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; + struct nlattr *cqm; + int err; + + cqm = info->attrs[NL80211_ATTR_CQM]; + if (!cqm) { + err = -EINVAL; + goto out; + } + + err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, + nl80211_attr_cqm_policy); + if (err) + goto out; + + if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && + attrs[NL80211_ATTR_CQM_RSSI_HYST]) { + s32 threshold; + u32 hysteresis; + threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); + hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); + err = nl80211_set_cqm_rssi(info, threshold, hysteresis); + } else + err = -EINVAL; + +out: + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -5082,6 +5161,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, /* can be retrieved by unprivileged users */ }, + { + .cmd = NL80211_CMD_SET_CQM, + .doit = nl80211_set_cqm, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -5832,6 +5917,52 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void +nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *pinfoattr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); + if (!pinfoattr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + rssi_event); + + nla_nest_end(msg, pinfoattr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, void *_notify) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 4ca511102c6c..2ad7fbc7d9f1 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -82,4 +82,10 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, const u8 *buf, size_t len, bool ack, gfp_t gfp); +void +nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + #endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index 5e1656bdf23b..bfcbeee23f9c 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -28,226 +28,226 @@ typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *, * know about. */ static const struct iw_ioctl_description standard_ioctl[] = { - [SIOCSIWCOMMIT - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = { .header_type = IW_HEADER_TYPE_NULL, }, - [SIOCGIWNAME - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWNAME)] = { .header_type = IW_HEADER_TYPE_CHAR, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWNWID - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWNWID)] = { .header_type = IW_HEADER_TYPE_PARAM, .flags = IW_DESCR_FLAG_EVENT, }, - [SIOCGIWNWID - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWNWID)] = { .header_type = IW_HEADER_TYPE_PARAM, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWFREQ - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWFREQ)] = { .header_type = IW_HEADER_TYPE_FREQ, .flags = IW_DESCR_FLAG_EVENT, }, - [SIOCGIWFREQ - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWFREQ)] = { .header_type = IW_HEADER_TYPE_FREQ, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWMODE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWMODE)] = { .header_type = IW_HEADER_TYPE_UINT, .flags = IW_DESCR_FLAG_EVENT, }, - [SIOCGIWMODE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWMODE)] = { .header_type = IW_HEADER_TYPE_UINT, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWSENS - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWSENS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWSENS - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWSENS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWRANGE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWRANGE)] = { .header_type = IW_HEADER_TYPE_NULL, }, - [SIOCGIWRANGE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWRANGE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_range), .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWPRIV - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWPRIV)] = { .header_type = IW_HEADER_TYPE_NULL, }, - [SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */ + [IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */ .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_priv_args), .max_tokens = 16, .flags = IW_DESCR_FLAG_NOMAX, }, - [SIOCSIWSTATS - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWSTATS)] = { .header_type = IW_HEADER_TYPE_NULL, }, - [SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */ + [IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */ .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_statistics), .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWSPY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr), .max_tokens = IW_MAX_SPY, }, - [SIOCGIWSPY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_SPY, }, - [SIOCSIWTHRSPY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_thrspy), .min_tokens = 1, .max_tokens = 1, }, - [SIOCGIWTHRSPY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct iw_thrspy), .min_tokens = 1, .max_tokens = 1, }, - [SIOCSIWAP - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWAP)] = { .header_type = IW_HEADER_TYPE_ADDR, }, - [SIOCGIWAP - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWAP)] = { .header_type = IW_HEADER_TYPE_ADDR, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWMLME - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWMLME)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_mlme), .max_tokens = sizeof(struct iw_mlme), }, - [SIOCGIWAPLIST - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWAPLIST)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = sizeof(struct sockaddr) + sizeof(struct iw_quality), .max_tokens = IW_MAX_AP, .flags = IW_DESCR_FLAG_NOMAX, }, - [SIOCSIWSCAN - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWSCAN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = 0, .max_tokens = sizeof(struct iw_scan_req), }, - [SIOCGIWSCAN - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWSCAN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_SCAN_MAX_DATA, .flags = IW_DESCR_FLAG_NOMAX, }, - [SIOCSIWESSID - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWESSID)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, .flags = IW_DESCR_FLAG_EVENT, }, - [SIOCGIWESSID - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWESSID)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, .flags = IW_DESCR_FLAG_DUMP, }, - [SIOCSIWNICKN - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWNICKN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, }, - [SIOCGIWNICKN - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWNICKN)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ESSID_MAX_SIZE, }, - [SIOCSIWRATE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWRATE)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWRATE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWRATE)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWRTS - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWRTS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWRTS - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWRTS)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWFRAG - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWFRAG)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWFRAG - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWFRAG)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWTXPOW - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWTXPOW)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWTXPOW - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWTXPOW)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWRETRY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWRETRY)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWRETRY - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWRETRY)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWENCODE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWENCODE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ENCODING_TOKEN_MAX, .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, }, - [SIOCGIWENCODE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWENCODE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_ENCODING_TOKEN_MAX, .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, }, - [SIOCSIWPOWER - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWPOWER)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWPOWER - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWPOWER)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWGENIE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWGENIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, - [SIOCGIWGENIE - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWGENIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, - [SIOCSIWAUTH - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWAUTH)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCGIWAUTH - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWAUTH)] = { .header_type = IW_HEADER_TYPE_PARAM, }, - [SIOCSIWENCODEEXT - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_encode_ext), .max_tokens = sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, }, - [SIOCGIWENCODEEXT - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_encode_ext), .max_tokens = sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, }, - [SIOCSIWPMKSA - SIOCIWFIRST] = { + [IW_IOCTL_IDX(SIOCSIWPMKSA)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .min_tokens = sizeof(struct iw_pmksa), @@ -261,44 +261,44 @@ static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl); * we know about. */ static const struct iw_ioctl_description standard_event[] = { - [IWEVTXDROP - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVTXDROP)] = { .header_type = IW_HEADER_TYPE_ADDR, }, - [IWEVQUAL - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVQUAL)] = { .header_type = IW_HEADER_TYPE_QUAL, }, - [IWEVCUSTOM - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVCUSTOM)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_CUSTOM_MAX, }, - [IWEVREGISTERED - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVREGISTERED)] = { .header_type = IW_HEADER_TYPE_ADDR, }, - [IWEVEXPIRED - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVEXPIRED)] = { .header_type = IW_HEADER_TYPE_ADDR, }, - [IWEVGENIE - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVGENIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, - [IWEVMICHAELMICFAILURE - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_michaelmicfailure), }, - [IWEVASSOCREQIE - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVASSOCREQIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, - [IWEVASSOCRESPIE - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVASSOCRESPIE)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = IW_GENERIC_IE_MAX, }, - [IWEVPMKIDCAND - IWEVFIRST] = { + [IW_EVENT_IDX(IWEVPMKIDCAND)] = { .header_type = IW_HEADER_TYPE_POINT, .token_size = 1, .max_tokens = sizeof(struct iw_pmkid_cand), @@ -449,11 +449,11 @@ void wireless_send_event(struct net_device * dev, /* Get the description of the Event */ if (cmd <= SIOCIWLAST) { - cmd_index = cmd - SIOCIWFIRST; + cmd_index = IW_IOCTL_IDX(cmd); if (cmd_index < standard_ioctl_num) descr = &(standard_ioctl[cmd_index]); } else { - cmd_index = cmd - IWEVFIRST; + cmd_index = IW_EVENT_IDX(cmd); if (cmd_index < standard_event_num) descr = &(standard_event[cmd_index]); } @@ -662,7 +662,7 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd) return NULL; /* Try as a standard command */ - index = cmd - SIOCIWFIRST; + index = IW_IOCTL_IDX(cmd); if (index < handlers->num_standard) return handlers->standard[index]; @@ -954,9 +954,9 @@ static int ioctl_standard_call(struct net_device * dev, int ret = -EINVAL; /* Get the description of the IOCTL */ - if ((cmd - SIOCIWFIRST) >= standard_ioctl_num) + if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num) return -EOPNOTSUPP; - descr = &(standard_ioctl[cmd - SIOCIWFIRST]); + descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]); /* Check if we have a pointer to user space data or not */ if (descr->header_type != IW_HEADER_TYPE_POINT) { @@ -1012,7 +1012,7 @@ static int compat_standard_call(struct net_device *dev, struct iw_point iwp; int err; - descr = standard_ioctl + (cmd - SIOCIWFIRST); + descr = standard_ioctl + IW_IOCTL_IDX(cmd); if (descr->header_type != IW_HEADER_TYPE_POINT) return ioctl_standard_call(dev, iwr, cmd, info, handler); |