Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6

This commit is contained in:
David S. Miller 2010-03-29 13:50:10 -07:00
commit 7905e357eb
82 changed files with 9085 additions and 1556 deletions

View file

@ -2279,26 +2279,25 @@ void gelic_wl_interrupt(struct net_device *netdev, u64 status)
/* /*
* driver helpers * driver helpers
*/ */
#define IW_IOCTL(n) [(n) - SIOCSIWCOMMIT]
static const iw_handler gelic_wl_wext_handler[] = static const iw_handler gelic_wl_wext_handler[] =
{ {
IW_IOCTL(SIOCGIWNAME) = gelic_wl_get_name, IW_HANDLER(SIOCGIWNAME, gelic_wl_get_name),
IW_IOCTL(SIOCGIWRANGE) = gelic_wl_get_range, IW_HANDLER(SIOCGIWRANGE, gelic_wl_get_range),
IW_IOCTL(SIOCSIWSCAN) = gelic_wl_set_scan, IW_HANDLER(SIOCSIWSCAN, gelic_wl_set_scan),
IW_IOCTL(SIOCGIWSCAN) = gelic_wl_get_scan, IW_HANDLER(SIOCGIWSCAN, gelic_wl_get_scan),
IW_IOCTL(SIOCSIWAUTH) = gelic_wl_set_auth, IW_HANDLER(SIOCSIWAUTH, gelic_wl_set_auth),
IW_IOCTL(SIOCGIWAUTH) = gelic_wl_get_auth, IW_HANDLER(SIOCGIWAUTH, gelic_wl_get_auth),
IW_IOCTL(SIOCSIWESSID) = gelic_wl_set_essid, IW_HANDLER(SIOCSIWESSID, gelic_wl_set_essid),
IW_IOCTL(SIOCGIWESSID) = gelic_wl_get_essid, IW_HANDLER(SIOCGIWESSID, gelic_wl_get_essid),
IW_IOCTL(SIOCSIWENCODE) = gelic_wl_set_encode, IW_HANDLER(SIOCSIWENCODE, gelic_wl_set_encode),
IW_IOCTL(SIOCGIWENCODE) = gelic_wl_get_encode, IW_HANDLER(SIOCGIWENCODE, gelic_wl_get_encode),
IW_IOCTL(SIOCSIWAP) = gelic_wl_set_ap, IW_HANDLER(SIOCSIWAP, gelic_wl_set_ap),
IW_IOCTL(SIOCGIWAP) = gelic_wl_get_ap, IW_HANDLER(SIOCGIWAP, gelic_wl_get_ap),
IW_IOCTL(SIOCSIWENCODEEXT) = gelic_wl_set_encodeext, IW_HANDLER(SIOCSIWENCODEEXT, gelic_wl_set_encodeext),
IW_IOCTL(SIOCGIWENCODEEXT) = gelic_wl_get_encodeext, IW_HANDLER(SIOCGIWENCODEEXT, gelic_wl_get_encodeext),
IW_IOCTL(SIOCSIWMODE) = gelic_wl_set_mode, IW_HANDLER(SIOCSIWMODE, gelic_wl_set_mode),
IW_IOCTL(SIOCGIWMODE) = gelic_wl_get_mode, IW_HANDLER(SIOCGIWMODE, gelic_wl_get_mode),
IW_IOCTL(SIOCGIWNICKN) = gelic_wl_get_nick, IW_HANDLER(SIOCGIWNICKN, gelic_wl_get_nick),
}; };
static const struct iw_handler_def gelic_wl_wext_handler_def = { static const struct iw_handler_def gelic_wl_wext_handler_def = {

View file

@ -3,7 +3,7 @@ menuconfig ATH_COMMON
depends on CFG80211 depends on CFG80211
---help--- ---help---
This will enable the support for the Atheros wireless drivers. 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. enables the common ath.ko module which shares common helpers.
For more information and documentation on this module you can visit: For more information and documentation on this module you can visit:

View file

@ -32,3 +32,24 @@ config ATH9K_DEBUGFS
Also required for changing debug message flags at run time. 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.

View file

@ -28,3 +28,13 @@ obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o
obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o
ath9k_common-y:= 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

View file

@ -101,9 +101,13 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
nf = 0 - ((nf ^ 0x1ff) + 1); nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE, ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ctl] [chain 0] is %d\n", nf); "NF calibrated [ctl] [chain 0] is %d\n", nf);
if (AR_SREV_9271(ah) && (nf >= -114))
nf = -116;
nfarray[0] = nf; 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)) if (AR_SREV_9280_10_OR_LATER(ah))
nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
AR9280_PHY_CH1_MINCCA_PWR); AR9280_PHY_CH1_MINCCA_PWR);
@ -139,9 +143,13 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
nf = 0 - ((nf ^ 0x1ff) + 1); nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE, ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ext] [chain 0] is %d\n", nf); "NF calibrated [ext] [chain 0] is %d\n", nf);
if (AR_SREV_9271(ah) && (nf >= -114))
nf = -116;
nfarray[3] = nf; 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)) if (AR_SREV_9280_10_OR_LATER(ah))
nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
AR9280_PHY_CH1_EXT_MINCCA_PWR); 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; u8 chainmask, rx_chain_status;
rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK); 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; chainmask = 0x9;
else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) { else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) {
if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4)) 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)) if (AR_SREV_9280(ah))
noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE; 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; noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE;
else if (AR_SREV_9287(ah)) else if (AR_SREV_9287(ah))
noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE; 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 */ /* Do NF cal only at longer intervals */
if (longcal) { if (longcal) {
/* Do periodic PAOffset Cal */ /* Do periodic PAOffset Cal */
if (AR_SREV_9271(ah)) if (AR_SREV_9271(ah)) {
ath9k_hw_9271_pa_cal(ah, false); if (!ah->pacal_info.skipcount)
else if (AR_SREV_9285_11_OR_LATER(ah)) { 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) if (!ah->pacal_info.skipcount)
ath9k_hw_9285_pa_cal(ah, false); ath9k_hw_9285_pa_cal(ah, false);
else else

View file

@ -286,6 +286,427 @@ int ath9k_cmn_padpos(__le16 frame_control)
} }
EXPORT_SYMBOL(ath9k_cmn_padpos); 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) static int __init ath9k_cmn_init(void)
{ {
return 0; return 0;

View file

@ -23,6 +23,8 @@
/* Common header for Atheros 802.11n base driver cores */ /* Common header for Atheros 802.11n base driver cores */
#define IEEE80211_WEP_NKID 4
#define WME_NUM_TID 16 #define WME_NUM_TID 16
#define WME_BA_BMP_SIZE 64 #define WME_BA_BMP_SIZE 64
#define WME_MAX_BA WME_BA_BMP_SIZE #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); bool decrypt_error);
int ath9k_cmn_padpos(__le16 frame_control); 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);

View file

@ -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);
}

View file

@ -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 */

View file

@ -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 */

View file

@ -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;
}
}

View file

@ -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 *) &reg, 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);

File diff suppressed because it is too large Load diff

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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 */

View file

@ -499,8 +499,10 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
{ {
int ecode; int ecode;
if (!ath9k_hw_chip_test(ah)) if (!AR_SREV_9271(ah)) {
return -ENODEV; if (!ath9k_hw_chip_test(ah))
return -ENODEV;
}
ecode = ath9k_hw_rf_claim(ah); ecode = ath9k_hw_rf_claim(ah);
if (ecode != 0) if (ecode != 0)
@ -603,9 +605,23 @@ static void ath9k_hw_init_mode_regs(struct ath_hw *ah)
ARRAY_SIZE(ar9271Modes_9271), 6); ARRAY_SIZE(ar9271Modes_9271), 6);
INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271, INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271,
ARRAY_SIZE(ar9271Common_9271), 2); 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, INIT_INI_ARRAY(&ah->iniModes_9271_1_0_only,
ar9271Modes_9271_1_0_only, ar9271Modes_9271_1_0_only,
ARRAY_SIZE(ar9271Modes_9271_1_0_only), 6); 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; return;
} }
@ -990,22 +1006,6 @@ static void ath9k_hw_init_qos(struct ath_hw *ah)
REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF); 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, static void ath9k_hw_init_pll(struct ath_hw *ah,
struct ath9k_channel *chan) 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 */ /* Switch the core clock for ar9271 to 117Mhz */
if (AR_SREV_9271(ah)) { if (AR_SREV_9271(ah)) {
if ((pll == 0x142c) || (pll == 0x2850) ) { udelay(500);
udelay(500); REG_WRITE(ah, 0x50040, 0x304);
/* 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(RTC_PLL_SETTLE_DELAY); 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); struct ath_common *common = ath9k_hw_common(ah);
if (common->state <= ATH_HW_INITIALIZED) if (common->state < ATH_HW_INITIALIZED)
goto free_hw; goto free_hw;
if (!AR_SREV_9100(ah)) if (!AR_SREV_9100(ah))
@ -1252,8 +1238,6 @@ void ath9k_hw_deinit(struct ath_hw *ah)
free_hw: free_hw:
if (!AR_SREV_9280_10_OR_LATER(ah)) if (!AR_SREV_9280_10_OR_LATER(ah))
ath9k_hw_rf_free_ext_banks(ah); ath9k_hw_rf_free_ext_banks(ah);
kfree(ah);
ah = NULL;
} }
EXPORT_SYMBOL(ath9k_hw_deinit); EXPORT_SYMBOL(ath9k_hw_deinit);
@ -1266,26 +1250,6 @@ static void ath9k_hw_override_ini(struct ath_hw *ah,
{ {
u32 val; 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 * Set the RX_ABORT and RX_DIS and clear if off only after
* RXE is set for MAC. This prevents frames with corrupted * 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)); REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
if (AR_SREV_9280_10_OR_LATER(ah)) { if (AR_SREV_9280_10_OR_LATER(ah)) {
val = REG_READ(ah, AR_PCU_MISC_MODE2) & val = REG_READ(ah, AR_PCU_MISC_MODE2);
(~AR_PCU_MISC_MODE2_HWWAR1);
if (!AR_SREV_9271(ah))
val &= ~AR_PCU_MISC_MODE2_HWWAR1;
if (AR_SREV_9287_10_OR_LATER(ah)) if (AR_SREV_9287_10_OR_LATER(ah))
val = val & (~AR_PCU_MISC_MODE2_HWWAR2); val = val & (~AR_PCU_MISC_MODE2_HWWAR2);
@ -1439,7 +1405,10 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
return -EINVAL; return -EINVAL;
} }
/* Set correct baseband to analog shift setting to access analog chips */
REG_WRITE(ah, AR_PHY(0), 0x00000007); REG_WRITE(ah, AR_PHY(0), 0x00000007);
/* Write ADDAC shifts */
REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO); REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO);
ah->eep_ops->set_addac(ah, chan); 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 * sizeof(u32) * ah->iniAddac.ia_rows *
ah->iniAddac.ia_columns; ah->iniAddac.ia_columns;
/* For AR5416 2.0/2.1 */
memcpy(ah->addac5416_21, memcpy(ah->addac5416_21,
ah->iniAddac.ia_array, addacSize); ah->iniAddac.ia_array, addacSize);
/* override CLKDRV value at [row, column] = [31, 1] */
(ah->addac5416_21)[31 * ah->iniAddac.ia_columns + 1] = 0; (ah->addac5416_21)[31 * ah->iniAddac.ia_columns + 1] = 0;
temp.ia_array = ah->addac5416_21; 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)) AR_SREV_9287_10_OR_LATER(ah))
REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites); 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++) { for (i = 0; i < ah->iniCommon.ia_rows; i++) {
u32 reg = INI_RA(&ah->iniCommon, i, 0); u32 reg = INI_RA(&ah->iniCommon, i, 0);
u32 val = INI_RA(&ah->iniCommon, i, 1); 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); 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)) ath9k_hw_write_regs(ah, freqIndex, regWrites);
REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only,
modesIndex, regWrites);
if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) { if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) {
REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex, 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) if (OLC_FOR_AR9280_20_LATER)
ath9k_olc_init(ah); ath9k_olc_init(ah);
/* Set TX power */
ah->eep_ops->set_txpower(ah, chan, ah->eep_ops->set_txpower(ah, chan,
ath9k_regd_get_ctl(regulatory, chan), ath9k_regd_get_ctl(regulatory, chan),
channel->max_antenna_gain * 2, channel->max_antenna_gain * 2,
@ -1524,6 +1506,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
min((u32) MAX_RATE_POWER, min((u32) MAX_RATE_POWER,
(u32) regulatory->power_limit)); (u32) regulatory->power_limit));
/* Write analog registers */
if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) { if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) {
ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL, ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
"ar5416SetRfRegs failed\n"); "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); ath9k_hw_mark_phy_inactive(ah);
/* Only required on the first reset */
if (AR_SREV_9271(ah) && ah->htc_reset_init) { if (AR_SREV_9271(ah) && ah->htc_reset_init) {
REG_WRITE(ah, REG_WRITE(ah,
AR9271_RESET_POWER_DOWN_CONTROL, AR9271_RESET_POWER_DOWN_CONTROL,
@ -1978,6 +1962,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
return -EINVAL; return -EINVAL;
} }
/* Only required on the first reset */
if (AR_SREV_9271(ah) && ah->htc_reset_init) { if (AR_SREV_9271(ah) && ah->htc_reset_init) {
ah->htc_reset_init = false; ah->htc_reset_init = false;
REG_WRITE(ah, REG_WRITE(ah,
@ -2438,7 +2423,7 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
if (!AR_SREV_9100(ah)) if (!AR_SREV_9100(ah))
REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF); 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), REG_CLR_BIT(ah, (AR_RTC_RESET),
AR_RTC_RESET_EN); AR_RTC_RESET_EN);
} }
@ -3216,7 +3201,9 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
else else
pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD; 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; pCap->num_gpio_pins = AR9285_NUM_GPIO;
else if (AR_SREV_9280_10_OR_LATER(ah)) else if (AR_SREV_9280_10_OR_LATER(ah))
pCap->num_gpio_pins = AR928X_NUM_GPIO; 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) if (gpio >= ah->caps.num_gpio_pins)
return 0xffffffff; 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; return MS_REG_READ(AR9287, gpio) != 0;
else if (AR_SREV_9285_10_OR_LATER(ah)) else if (AR_SREV_9285_10_OR_LATER(ah))
return MS_REG_READ(AR9285, gpio) != 0; 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) 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), REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
AR_GPIO_BIT(gpio)); AR_GPIO_BIT(gpio));
} }
@ -3865,6 +3857,16 @@ void ath_gen_timer_isr(struct ath_hw *ah)
} }
EXPORT_SYMBOL(ath_gen_timer_isr); 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 { static struct {
u32 version; u32 version;
const char * name; const char * name;

View file

@ -599,6 +599,11 @@ struct ath_hw {
struct ar5416IniArray iniModes_9271_1_0_only; struct ar5416IniArray iniModes_9271_1_0_only;
struct ar5416IniArray iniCckfirNormal; struct ar5416IniArray iniCckfirNormal;
struct ar5416IniArray iniCckfirJapan2484; 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_trigger;
u32 intr_gen_timer_thresh; 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); 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_CTRL 0x70
#define ATH_PCIE_CAP_LINK_L0S 1 #define ATH_PCIE_CAP_LINK_L0S 1
#define ATH_PCIE_CAP_LINK_L1 2 #define ATH_PCIE_CAP_LINK_L1 2

View file

@ -758,6 +758,9 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
tasklet_kill(&sc->intr_tq); tasklet_kill(&sc->intr_tq);
tasklet_kill(&sc->bcon_tasklet); tasklet_kill(&sc->bcon_tasklet);
kfree(sc->sc_ah);
sc->sc_ah = NULL;
} }
void ath9k_deinit_device(struct ath_softc *sc) void ath9k_deinit_device(struct ath_softc *sc)

View file

@ -6441,7 +6441,7 @@ static const u_int32_t ar9271Modes_9271[][6] = {
{ 0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 }, { 0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 },
{ 0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 }, { 0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 },
{ 0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 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 }, { 0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 },
{ 0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 }, { 0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 },
{ 0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 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 }, { 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 },
{ 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, { 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 },
{ 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, { 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 },
{ 0x00009a88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, { 0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000 },
{ 0x00009a8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, { 0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 },
{ 0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, { 0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
{ 0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 }, { 0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 },
{ 0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 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 }, { 0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 },
{ 0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 }, { 0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 },
{ 0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 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 }, { 0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 },
{ 0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 }, { 0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 },
{ 0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 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 }, { 0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 },
{ 0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 }, { 0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 },
{ 0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 }, { 0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 },
{ 0x0000aa88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 }, { 0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000 },
{ 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, { 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 },
{ 0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 }, { 0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
{ 0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 }, { 0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 },
{ 0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 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 }, { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a },
{ 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 }, { 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 },
{ 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 }, { 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 }, { 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e },
}; };
@ -6879,7 +6860,7 @@ static const u_int32_t ar9271Common_9271[][2] = {
{ 0x00008258, 0x00000000 }, { 0x00008258, 0x00000000 },
{ 0x0000825c, 0x400000ff }, { 0x0000825c, 0x400000ff },
{ 0x00008260, 0x00080922 }, { 0x00008260, 0x00080922 },
{ 0x00008264, 0x88a00010 }, { 0x00008264, 0xa8a00010 },
{ 0x00008270, 0x00000000 }, { 0x00008270, 0x00000000 },
{ 0x00008274, 0x40000000 }, { 0x00008274, 0x40000000 },
{ 0x00008278, 0x003e4180 }, { 0x00008278, 0x003e4180 },
@ -6910,13 +6891,10 @@ static const u_int32_t ar9271Common_9271[][2] = {
{ 0x00007810, 0x71c0d388 }, { 0x00007810, 0x71c0d388 },
{ 0x00007814, 0x924934a8 }, { 0x00007814, 0x924934a8 },
{ 0x0000781c, 0x00000000 }, { 0x0000781c, 0x00000000 },
{ 0x00007820, 0x00000c04 },
{ 0x00007824, 0x00d8abff },
{ 0x00007828, 0x66964300 }, { 0x00007828, 0x66964300 },
{ 0x0000782c, 0x8db6d961 }, { 0x0000782c, 0x8db6d961 },
{ 0x00007830, 0x8db6d96c }, { 0x00007830, 0x8db6d96c },
{ 0x00007834, 0x6140008b }, { 0x00007834, 0x6140008b },
{ 0x00007838, 0x00000029 },
{ 0x0000783c, 0x72ee0a72 }, { 0x0000783c, 0x72ee0a72 },
{ 0x00007840, 0xbbfffffc }, { 0x00007840, 0xbbfffffc },
{ 0x00007844, 0x000c0db6 }, { 0x00007844, 0x000c0db6 },
@ -6929,7 +6907,6 @@ static const u_int32_t ar9271Common_9271[][2] = {
{ 0x00007860, 0x21084210 }, { 0x00007860, 0x21084210 },
{ 0x00007864, 0xf7d7ffde }, { 0x00007864, 0xf7d7ffde },
{ 0x00007868, 0xc2034080 }, { 0x00007868, 0xc2034080 },
{ 0x0000786c, 0x48609eb4 },
{ 0x00007870, 0x10142c00 }, { 0x00007870, 0x10142c00 },
{ 0x00009808, 0x00000000 }, { 0x00009808, 0x00000000 },
{ 0x0000980c, 0xafe68e30 }, { 0x0000980c, 0xafe68e30 },
@ -6982,9 +6959,6 @@ static const u_int32_t ar9271Common_9271[][2] = {
{ 0x000099e8, 0x3c466478 }, { 0x000099e8, 0x3c466478 },
{ 0x000099ec, 0x0cc80caa }, { 0x000099ec, 0x0cc80caa },
{ 0x000099f0, 0x00000000 }, { 0x000099f0, 0x00000000 },
{ 0x0000a1f4, 0x00000000 },
{ 0x0000a1f8, 0x71733d01 },
{ 0x0000a1fc, 0xd0ad5c12 },
{ 0x0000a208, 0x803e68c8 }, { 0x0000a208, 0x803e68c8 },
{ 0x0000a210, 0x4080a333 }, { 0x0000a210, 0x4080a333 },
{ 0x0000a214, 0x00206c10 }, { 0x0000a214, 0x00206c10 },
@ -7004,13 +6978,9 @@ static const u_int32_t ar9271Common_9271[][2] = {
{ 0x0000a260, 0xdfa90f01 }, { 0x0000a260, 0xdfa90f01 },
{ 0x0000a268, 0x00000000 }, { 0x0000a268, 0x00000000 },
{ 0x0000a26c, 0x0ebae9e6 }, { 0x0000a26c, 0x0ebae9e6 },
{ 0x0000a278, 0x3bdef7bd },
{ 0x0000a27c, 0x050e83bd },
{ 0x0000a388, 0x0c000000 }, { 0x0000a388, 0x0c000000 },
{ 0x0000a38c, 0x20202020 }, { 0x0000a38c, 0x20202020 },
{ 0x0000a390, 0x20202020 }, { 0x0000a390, 0x20202020 },
{ 0x0000a394, 0x3bdef7bd },
{ 0x0000a398, 0x000003bd },
{ 0x0000a39c, 0x00000001 }, { 0x0000a39c, 0x00000001 },
{ 0x0000a3a0, 0x00000000 }, { 0x0000a3a0, 0x00000000 },
{ 0x0000a3a4, 0x00000000 }, { 0x0000a3a4, 0x00000000 },
@ -7025,8 +6995,6 @@ static const u_int32_t ar9271Common_9271[][2] = {
{ 0x0000a3cc, 0x20202020 }, { 0x0000a3cc, 0x20202020 },
{ 0x0000a3d0, 0x20202020 }, { 0x0000a3d0, 0x20202020 },
{ 0x0000a3d4, 0x20202020 }, { 0x0000a3d4, 0x20202020 },
{ 0x0000a3dc, 0x3bdef7bd },
{ 0x0000a3e0, 0x000003bd },
{ 0x0000a3e4, 0x00000000 }, { 0x0000a3e4, 0x00000000 },
{ 0x0000a3e8, 0x18c43433 }, { 0x0000a3e8, 0x18c43433 },
{ 0x0000a3ec, 0x00f70081 }, { 0x0000a3ec, 0x00f70081 },
@ -7046,7 +7014,102 @@ static const u_int32_t ar9271Common_9271[][2] = {
{ 0x0000d384, 0xf3307ff0 }, { 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] = { static const u_int32_t ar9271Modes_9271_1_0_only[][6] = {
{ 0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311 }, { 0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311 },
{ 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 }, { 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 },
};

View file

@ -351,7 +351,7 @@ void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds,
ads->ds_ctl6 = SM(keyType, AR_EncrType); 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_ctl8 = 0;
ads->ds_ctl9 = 0; ads->ds_ctl9 = 0;
ads->ds_ctl10 = 0; ads->ds_ctl10 = 0;

View file

@ -150,6 +150,32 @@ struct ath_rx_status {
u32 evm2; 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_CRC 0x01
#define ATH9K_RXERR_PHY 0x02 #define ATH9K_RXERR_PHY 0x02
#define ATH9K_RXERR_FIFO 0x04 #define ATH9K_RXERR_FIFO 0x04

View file

@ -110,8 +110,8 @@ struct ath_rate_table {
int rate_cnt; int rate_cnt;
int mcs_start; int mcs_start;
struct { struct {
int valid; u8 valid;
int valid_single_stream; u8 valid_single_stream;
u8 phy; u8 phy;
u32 ratekbps; u32 ratekbps;
u32 user_ratekbps; u32 user_ratekbps;

View file

@ -940,6 +940,7 @@ enum {
#define AR928X_NUM_GPIO 10 #define AR928X_NUM_GPIO 10
#define AR9285_NUM_GPIO 12 #define AR9285_NUM_GPIO 12
#define AR9287_NUM_GPIO 11 #define AR9287_NUM_GPIO 11
#define AR9271_NUM_GPIO 16
#define AR_GPIO_IN_OUT 0x4048 #define AR_GPIO_IN_OUT 0x4048
#define AR_GPIO_IN_VAL 0x0FFFC000 #define AR_GPIO_IN_VAL 0x0FFFC000
@ -950,6 +951,8 @@ enum {
#define AR9285_GPIO_IN_VAL_S 12 #define AR9285_GPIO_IN_VAL_S 12
#define AR9287_GPIO_IN_VAL 0x003FF800 #define AR9287_GPIO_IN_VAL 0x003FF800
#define AR9287_GPIO_IN_VAL_S 11 #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 0x404c
#define AR_GPIO_OE_OUT_DRV 0x3 #define AR_GPIO_OE_OUT_DRV 0x3

View file

@ -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;
}

View file

@ -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 */

View file

@ -59,6 +59,7 @@ enum ATH_DEBUG {
ATH_DBG_PS = 0x00000800, ATH_DBG_PS = 0x00000800,
ATH_DBG_HWTIMER = 0x00001000, ATH_DBG_HWTIMER = 0x00001000,
ATH_DBG_BTCOEX = 0x00002000, ATH_DBG_BTCOEX = 0x00002000,
ATH_DBG_WMI = 0x00004000,
ATH_DBG_ANY = 0xffffffff ATH_DBG_ANY = 0xffffffff
}; };

View file

@ -4348,11 +4348,10 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
b43_set_phytxctl_defaults(dev); b43_set_phytxctl_defaults(dev);
/* Minimum Contention Window */ /* 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); 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); b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0xF);
}
/* Maximum Contention Window */ /* Maximum Contention Window */
b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF); b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF);

View file

@ -9995,49 +9995,48 @@ static int ipw_wx_sw_reset(struct net_device *dev,
} }
/* Rebase the WE IOCTLs to zero for the handler array */ /* Rebase the WE IOCTLs to zero for the handler array */
#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
static iw_handler ipw_wx_handlers[] = { static iw_handler ipw_wx_handlers[] = {
IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname, IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname),
IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq, IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq),
IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq, IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq),
IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode, IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode),
IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode, IW_HANDLER(SIOCGIWMODE, ipw_wx_get_mode),
IW_IOCTL(SIOCSIWSENS) = ipw_wx_set_sens, IW_HANDLER(SIOCSIWSENS, ipw_wx_set_sens),
IW_IOCTL(SIOCGIWSENS) = ipw_wx_get_sens, IW_HANDLER(SIOCGIWSENS, ipw_wx_get_sens),
IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range, IW_HANDLER(SIOCGIWRANGE, ipw_wx_get_range),
IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap, IW_HANDLER(SIOCSIWAP, ipw_wx_set_wap),
IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap, IW_HANDLER(SIOCGIWAP, ipw_wx_get_wap),
IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan, IW_HANDLER(SIOCSIWSCAN, ipw_wx_set_scan),
IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan, IW_HANDLER(SIOCGIWSCAN, ipw_wx_get_scan),
IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid, IW_HANDLER(SIOCSIWESSID, ipw_wx_set_essid),
IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid, IW_HANDLER(SIOCGIWESSID, ipw_wx_get_essid),
IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick, IW_HANDLER(SIOCSIWNICKN, ipw_wx_set_nick),
IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick, IW_HANDLER(SIOCGIWNICKN, ipw_wx_get_nick),
IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate, IW_HANDLER(SIOCSIWRATE, ipw_wx_set_rate),
IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate, IW_HANDLER(SIOCGIWRATE, ipw_wx_get_rate),
IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts, IW_HANDLER(SIOCSIWRTS, ipw_wx_set_rts),
IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts, IW_HANDLER(SIOCGIWRTS, ipw_wx_get_rts),
IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag, IW_HANDLER(SIOCSIWFRAG, ipw_wx_set_frag),
IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag, IW_HANDLER(SIOCGIWFRAG, ipw_wx_get_frag),
IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow, IW_HANDLER(SIOCSIWTXPOW, ipw_wx_set_txpow),
IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow, IW_HANDLER(SIOCGIWTXPOW, ipw_wx_get_txpow),
IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry, IW_HANDLER(SIOCSIWRETRY, ipw_wx_set_retry),
IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry, IW_HANDLER(SIOCGIWRETRY, ipw_wx_get_retry),
IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode, IW_HANDLER(SIOCSIWENCODE, ipw_wx_set_encode),
IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode, IW_HANDLER(SIOCGIWENCODE, ipw_wx_get_encode),
IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power, IW_HANDLER(SIOCSIWPOWER, ipw_wx_set_power),
IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power, IW_HANDLER(SIOCGIWPOWER, ipw_wx_get_power),
IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy, IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy, IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy, IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy, IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie, IW_HANDLER(SIOCSIWGENIE, ipw_wx_set_genie),
IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie, IW_HANDLER(SIOCGIWGENIE, ipw_wx_get_genie),
IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme, IW_HANDLER(SIOCSIWMLME, ipw_wx_set_mlme),
IW_IOCTL(SIOCSIWAUTH) = ipw_wx_set_auth, IW_HANDLER(SIOCSIWAUTH, ipw_wx_set_auth),
IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth, IW_HANDLER(SIOCGIWAUTH, ipw_wx_get_auth),
IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext, IW_HANDLER(SIOCSIWENCODEEXT, ipw_wx_set_encodeext),
IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext, IW_HANDLER(SIOCGIWENCODEEXT, ipw_wx_get_encodeext),
}; };
enum { enum {

View file

@ -212,6 +212,9 @@ static struct iwl_lib_ops iwl1000_lib = {
.set_ct_kill = iwl1000_set_ct_threshold, .set_ct_kill = iwl1000_set_ct_threshold,
}, },
.add_bcast_station = iwl_add_bcast_station, .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 = { static const struct iwl_ops iwl1000_ops = {
@ -223,7 +226,7 @@ static const struct iwl_ops iwl1000_ops = {
}; };
struct iwl_cfg iwl1000_bgn_cfg = { 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, .fw_name_pre = IWL1000_FW_PRE,
.ucode_api_max = IWL1000_UCODE_API_MAX, .ucode_api_max = IWL1000_UCODE_API_MAX,
.ucode_api_min = IWL1000_UCODE_API_MIN, .ucode_api_min = IWL1000_UCODE_API_MIN,
@ -249,10 +252,11 @@ struct iwl_cfg iwl1000_bgn_cfg = {
.support_ct_kill_exit = true, .support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl1000_bg_cfg = { 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, .fw_name_pre = IWL1000_FW_PRE,
.ucode_api_max = IWL1000_UCODE_API_MAX, .ucode_api_max = IWL1000_UCODE_API_MAX,
.ucode_api_min = IWL1000_UCODE_API_MIN, .ucode_api_min = IWL1000_UCODE_API_MIN,
@ -277,6 +281,7 @@ struct iwl_cfg iwl1000_bg_cfg = {
.support_ct_kill_exit = true, .support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX));

View file

@ -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 ieee80211_hw *hw = priv->hw;
struct iwl_priv *priv = (struct iwl_priv *)priv_r; 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; 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; 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->last_flush = jiffies;
rs_sta->flush_time = IWL_RATE_FLUSH; rs_sta->flush_time = IWL_RATE_FLUSH;
rs_sta->last_tx_packets = 0; 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.data = (unsigned long)rs_sta;
rs_sta->rate_scale_flush.function = iwl3945_bg_rate_scale_flush; 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; 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) 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; rs_sta = &psta->rs_sta;
spin_lock_init(&rs_sta->lock);
init_timer(&rs_sta->rate_scale_flush);
IWL_DEBUG_RATE(priv, "leave\n"); IWL_DEBUG_RATE(priv, "leave\n");
return rs_sta; 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, static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta,
void *priv_sta) void *priv_sta)
{ {
struct iwl3945_sta_priv *psta = (void *) sta->drv_priv; struct iwl3945_rs_sta *rs_sta = priv_sta;
struct iwl3945_rs_sta *rs_sta = &psta->rs_sta;
struct iwl_priv *priv __maybe_unused = rs_sta->priv;
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); 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; 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++; rs_sta->tx_packets++;
scale_rate_index = first_index; scale_rate_index = first_index;
@ -625,7 +645,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
u32 fail_count; u32 fail_count;
s8 scale_action = 0; s8 scale_action = 0;
unsigned long flags; unsigned long flags;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0; u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0;
s8 max_rate_idx = -1; s8 max_rate_idx = -1;
struct iwl_priv *priv = (struct iwl_priv *)priv_r; 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"); 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)) if (rate_control_send_low(sta, priv_sta, txrc))
return; return;
@ -650,20 +675,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
if (sband->band == IEEE80211_BAND_5GHZ) if (sband->band == IEEE80211_BAND_5GHZ)
rate_mask = rate_mask << IWL_FIRST_OFDM_RATE; 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); spin_lock_irqsave(&rs_sta->lock, flags);
/* for recent assoc, choose best rate regarding /* for recent assoc, choose best rate regarding
@ -883,12 +894,22 @@ static void iwl3945_remove_debugfs(void *priv, void *priv_sta)
} }
#endif #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 = { static struct rate_control_ops rs_ops = {
.module = NULL, .module = NULL,
.name = RS_NAME, .name = RS_NAME,
.tx_status = rs_tx_status, .tx_status = rs_tx_status,
.get_rate = rs_get_rate, .get_rate = rs_get_rate,
.rate_init = rs_rate_init, .rate_init = rs_rate_init_stub,
.alloc = rs_alloc, .alloc = rs_alloc,
.free = rs_free, .free = rs_free,
.alloc_sta = rs_alloc_sta, .alloc_sta = rs_alloc_sta,
@ -899,7 +920,6 @@ static struct rate_control_ops rs_ops = {
#endif #endif
}; };
void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id) void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
{ {
struct iwl_priv *priv = hw->priv; 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, sta = ieee80211_find_sta(priv->vif,
priv->stations[sta_id].sta.sta.addr); priv->stations[sta_id].sta.sta.addr);
if (!sta) { if (!sta) {
IWL_DEBUG_RATE(priv, "Unable to find station to initialize rate scaling.\n");
rcu_read_unlock(); rcu_read_unlock();
return; return;
} }

View file

@ -1911,6 +1911,8 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv)
"configuration (%d).\n", rc); "configuration (%d).\n", rc);
return rc; return rc;
} }
iwl_clear_ucode_stations(priv, false);
iwl_restore_stations(priv);
} }
IWL_DEBUG_INFO(priv, "Sending RXON\n" 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)); 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 /* 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 */ * 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; 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 */ /* Init the hardware's rate fallback order based on the band */
rc = iwl3945_init_hw_rate_table(priv); rc = iwl3945_init_hw_rate_table(priv);
if (rc) { if (rc) {
@ -2828,6 +2820,7 @@ static struct iwl_cfg iwl3945_bg_cfg = {
.led_compensation = 64, .led_compensation = 64,
.broken_powersave = true, .broken_powersave = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
static struct iwl_cfg iwl3945_abg_cfg = { static struct iwl_cfg iwl3945_abg_cfg = {
@ -2846,6 +2839,7 @@ static struct iwl_cfg iwl3945_abg_cfg = {
.led_compensation = 64, .led_compensation = 64,
.broken_powersave = true, .broken_powersave = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
DEFINE_PCI_DEVICE_TABLE(iwl3945_hw_card_ids) = { DEFINE_PCI_DEVICE_TABLE(iwl3945_hw_card_ids) = {

View file

@ -95,7 +95,6 @@ struct iwl3945_rs_sta {
u8 tgg; u8 tgg;
u8 flush_pending; u8 flush_pending;
u8 start_rate; u8 start_rate;
u8 ibss_sta_added;
struct timer_list rate_scale_flush; struct timer_list rate_scale_flush;
struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945]; struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945];
#ifdef CONFIG_MAC80211_DEBUGFS #ifdef CONFIG_MAC80211_DEBUGFS

View file

@ -2187,6 +2187,7 @@ static struct iwl_lib_ops iwl4965_lib = {
.load_ucode = iwl4965_load_bsm, .load_ucode = iwl4965_load_bsm,
.dump_nic_event_log = iwl_dump_nic_event_log, .dump_nic_event_log = iwl_dump_nic_event_log,
.dump_nic_error_log = iwl_dump_nic_error_log, .dump_nic_error_log = iwl_dump_nic_error_log,
.dump_fh = iwl_dump_fh,
.set_channel_switch = iwl4965_hw_channel_switch, .set_channel_switch = iwl4965_hw_channel_switch,
.apm_ops = { .apm_ops = {
.init = iwl_apm_init, .init = iwl_apm_init,
@ -2220,6 +2221,7 @@ static struct iwl_lib_ops iwl4965_lib = {
.set_ct_kill = iwl4965_set_ct_threshold, .set_ct_kill = iwl4965_set_ct_threshold,
}, },
.add_bcast_station = iwl_add_bcast_station, .add_bcast_station = iwl_add_bcast_station,
.check_plcp_health = iwl_good_plcp_health,
}; };
static const struct iwl_ops iwl4965_ops = { static const struct iwl_ops iwl4965_ops = {
@ -2231,7 +2233,7 @@ static const struct iwl_ops iwl4965_ops = {
}; };
struct iwl_cfg iwl4965_agn_cfg = { struct iwl_cfg iwl4965_agn_cfg = {
.name = "4965AGN", .name = "Intel(R) Wireless WiFi Link 4965AGN",
.fw_name_pre = IWL4965_FW_PRE, .fw_name_pre = IWL4965_FW_PRE,
.ucode_api_max = IWL4965_UCODE_API_MAX, .ucode_api_max = IWL4965_UCODE_API_MAX,
.ucode_api_min = IWL4965_UCODE_API_MIN, .ucode_api_min = IWL4965_UCODE_API_MIN,
@ -2254,6 +2256,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
.led_compensation = 61, .led_compensation = 61,
.chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS, .chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
/* Module firmware */ /* Module firmware */

View file

@ -544,7 +544,6 @@ void iwl5000_init_alive_start(struct iwl_priv *priv)
goto restart; goto restart;
} }
iwl_clear_stations_table(priv);
ret = priv->cfg->ops->lib->alive_notify(priv); ret = priv->cfg->ops->lib->alive_notify(priv);
if (ret) { if (ret) {
IWL_WARN(priv, IWL_WARN(priv,
@ -1500,6 +1499,9 @@ struct iwl_lib_ops iwl5000_lib = {
.set_ct_kill = iwl5000_set_ct_threshold, .set_ct_kill = iwl5000_set_ct_threshold,
}, },
.add_bcast_station = iwl_add_bcast_station, .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 = { 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, .set_ct_kill = iwl5150_set_ct_threshold,
}, },
.add_bcast_station = iwl_add_bcast_station, .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 = { static const struct iwl_ops iwl5000_ops = {
@ -1580,7 +1585,7 @@ struct iwl_mod_params iwl50_mod_params = {
struct iwl_cfg iwl5300_agn_cfg = { struct iwl_cfg iwl5300_agn_cfg = {
.name = "5300AGN", .name = "Intel(R) Ultimate N WiFi Link 5300 AGN",
.fw_name_pre = IWL5000_FW_PRE, .fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN, .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, .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl5100_bgn_cfg = { struct iwl_cfg iwl5100_bgn_cfg = {
.name = "5100BGN", .name = "Intel(R) WiFi Link 5100 BGN",
.fw_name_pre = IWL5000_FW_PRE, .fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN, .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, .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl5100_abg_cfg = { struct iwl_cfg iwl5100_abg_cfg = {
.name = "5100ABG", .name = "Intel(R) WiFi Link 5100 ABG",
.fw_name_pre = IWL5000_FW_PRE, .fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN, .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, .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl5100_agn_cfg = { struct iwl_cfg iwl5100_agn_cfg = {
.name = "5100AGN", .name = "Intel(R) WiFi Link 5100 AGN",
.fw_name_pre = IWL5000_FW_PRE, .fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN, .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, .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl5350_agn_cfg = { struct iwl_cfg iwl5350_agn_cfg = {
.name = "5350AGN", .name = "Intel(R) WiMAX/WiFi Link 5350 AGN",
.fw_name_pre = IWL5000_FW_PRE, .fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN, .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, .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl5150_agn_cfg = { struct iwl_cfg iwl5150_agn_cfg = {
.name = "5150AGN", .name = "Intel(R) WiMAX/WiFi Link 5150 AGN",
.fw_name_pre = IWL5150_FW_PRE, .fw_name_pre = IWL5150_FW_PRE,
.ucode_api_max = IWL5150_UCODE_API_MAX, .ucode_api_max = IWL5150_UCODE_API_MAX,
.ucode_api_min = IWL5150_UCODE_API_MIN, .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, .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl5150_abg_cfg = { struct iwl_cfg iwl5150_abg_cfg = {
.name = "5150ABG", .name = "Intel(R) WiMAX/WiFi Link 5150 ABG",
.fw_name_pre = IWL5150_FW_PRE, .fw_name_pre = IWL5150_FW_PRE,
.ucode_api_max = IWL5150_UCODE_API_MAX, .ucode_api_max = IWL5150_UCODE_API_MAX,
.ucode_api_min = IWL5150_UCODE_API_MIN, .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, .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));

View file

@ -278,6 +278,9 @@ static struct iwl_lib_ops iwl6000_lib = {
.set_ct_kill = iwl6000_set_ct_threshold, .set_ct_kill = iwl6000_set_ct_threshold,
}, },
.add_bcast_station = iwl_add_bcast_station, .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 = { 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, .set_calib_version = iwl6050_set_calib_version,
}, },
.add_bcast_station = iwl_add_bcast_station, .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 = { 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 * "i": Internal configuration, use internal Power Amplifier
*/ */
struct iwl_cfg iwl6000i_2agn_cfg = { 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, .fw_name_pre = IWL6000_FW_PRE,
.ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_max = IWL6000_UCODE_API_MAX,
.ucode_api_min = IWL6000_UCODE_API_MIN, .ucode_api_min = IWL6000_UCODE_API_MIN,
@ -386,10 +392,11 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
.support_ct_kill_exit = true, .support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl6000i_2abg_cfg = { 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, .fw_name_pre = IWL6000_FW_PRE,
.ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_max = IWL6000_UCODE_API_MAX,
.ucode_api_min = IWL6000_UCODE_API_MIN, .ucode_api_min = IWL6000_UCODE_API_MIN,
@ -417,10 +424,11 @@ struct iwl_cfg iwl6000i_2abg_cfg = {
.support_ct_kill_exit = true, .support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl6000i_2bg_cfg = { 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, .fw_name_pre = IWL6000_FW_PRE,
.ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_max = IWL6000_UCODE_API_MAX,
.ucode_api_min = IWL6000_UCODE_API_MIN, .ucode_api_min = IWL6000_UCODE_API_MIN,
@ -448,10 +456,11 @@ struct iwl_cfg iwl6000i_2bg_cfg = {
.support_ct_kill_exit = true, .support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl6050_2agn_cfg = { 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, .fw_name_pre = IWL6050_FW_PRE,
.ucode_api_max = IWL6050_UCODE_API_MAX, .ucode_api_max = IWL6050_UCODE_API_MAX,
.ucode_api_min = IWL6050_UCODE_API_MIN, .ucode_api_min = IWL6050_UCODE_API_MIN,
@ -480,10 +489,11 @@ struct iwl_cfg iwl6050_2agn_cfg = {
.support_ct_kill_exit = true, .support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1500, .chain_noise_scale = 1500,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl6050_2abg_cfg = { 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, .fw_name_pre = IWL6050_FW_PRE,
.ucode_api_max = IWL6050_UCODE_API_MAX, .ucode_api_max = IWL6050_UCODE_API_MAX,
.ucode_api_min = IWL6050_UCODE_API_MIN, .ucode_api_min = IWL6050_UCODE_API_MIN,
@ -511,10 +521,11 @@ struct iwl_cfg iwl6050_2abg_cfg = {
.support_ct_kill_exit = true, .support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1500, .chain_noise_scale = 1500,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
struct iwl_cfg iwl6000_3agn_cfg = { 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, .fw_name_pre = IWL6000_FW_PRE,
.ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_max = IWL6000_UCODE_API_MAX,
.ucode_api_min = IWL6000_UCODE_API_MIN, .ucode_api_min = IWL6000_UCODE_API_MIN,
@ -543,6 +554,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
.support_ct_kill_exit = true, .support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1000, .chain_noise_scale = 1000,
.monitor_recover_period = IWL_MONITORING_PERIOD,
}; };
MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));

View file

@ -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"); 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) || if (!ieee80211_is_data(hdr->frame_control) ||
info->flags & IEEE80211_TX_CTL_NO_ACK) info->flags & IEEE80211_TX_CTL_NO_ACK)
return; return;
@ -778,10 +787,6 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
!(info->flags & IEEE80211_TX_STAT_AMPDU)) !(info->flags & IEEE80211_TX_STAT_AMPDU))
return; 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 * Ignore this Tx frame response if its initial rate doesn't match
* that of latest Link Quality command. There may be stragglers * 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++; lq_sta->missed_rate_counter++;
if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
lq_sta->missed_rate_counter = 0; 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 */ /* Regardless, ignore this status info for outdated rate */
return; return;
@ -1915,7 +1920,7 @@ static u32 rs_update_rate_tbl(struct iwl_priv *priv,
/* Update uCode's rate table. */ /* Update uCode's rate table. */
rate = rate_n_flags_from_tbl(priv, tbl, index, is_green); rate = rate_n_flags_from_tbl(priv, tbl, index, is_green);
rs_fill_link_cmd(priv, lq_sta, rate); 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; return rate;
} }
@ -2291,7 +2296,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n", IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n",
tbl->current_rate, index); tbl->current_rate, index);
rs_fill_link_cmd(priv, lq_sta, tbl->current_rate); 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 } else
done_search = 1; done_search = 1;
} }
@ -2340,7 +2345,20 @@ static void rs_rate_scale_perform(struct iwl_priv *priv,
return; 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, static void rs_initialize_lq(struct iwl_priv *priv,
struct ieee80211_conf *conf, struct ieee80211_conf *conf,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
@ -2359,10 +2377,6 @@ static void rs_initialize_lq(struct iwl_priv *priv,
i = lq_sta->last_txrate_idx; 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; valid_tx_ant = priv->hw_params.valid_tx_ant;
if (!lq_sta->search_better_tbl) if (!lq_sta->search_better_tbl)
@ -2390,7 +2404,8 @@ static void rs_initialize_lq(struct iwl_priv *priv,
tbl->current_rate = rate; tbl->current_rate = rate;
rs_set_expected_tpt_table(lq_sta, tbl); rs_set_expected_tpt_table(lq_sta, tbl);
rs_fill_link_cmd(NULL, lq_sta, rate); 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: out:
return; 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 sk_buff *skb = txrc->skb;
struct ieee80211_supported_band *sband = txrc->sband; struct ieee80211_supported_band *sband = txrc->sband;
struct iwl_priv *priv = (struct iwl_priv *)priv_r; 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 ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct iwl_lq_sta *lq_sta = priv_sta; struct iwl_lq_sta *lq_sta = priv_sta;
int rate_idx; 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; 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. */ /* Send management frames and NO_ACK data using lowest rate. */
if (rate_control_send_low(sta, priv_sta, txrc)) if (rate_control_send_low(sta, priv_sta, txrc))
return; return;
rate_idx = lq_sta->last_txrate_idx; 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) { if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
rate_idx -= IWL_FIRST_OFDM_RATE; rate_idx -= IWL_FIRST_OFDM_RATE;
/* 6M and 9M shared same MCS index */ /* 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; 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; 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_conf *conf = &priv->hw->conf;
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; 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;
lq_sta->lq.sta_id = 0xff; 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 = sta_id;
for (j = 0; j < LQ_SIZE; j++) for (j = 0; j < LQ_SIZE; j++)
for (i = 0; i < IWL_RATE_COUNT; i++) 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++) for (i = 0; i < IWL_RATE_COUNT; i++)
rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[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 /* TODO: what is a good starting rate for STA? About middle? Maybe not
* the lowest or the highest rate.. Could consider using RSSI from * the lowest or the highest rate.. Could consider using RSSI from
* previous packets? Need to have IEEE 802.1X auth succeed immediately * previous packets? Need to have IEEE 802.1X auth succeed immediately
* after assoc.. */ * 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->is_dup = 0;
lq_sta->max_rate_idx = -1; lq_sta->max_rate_idx = -1;
lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; 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) { if (lq_sta->dbg_fixed_rate) {
rs_fill_link_cmd(NULL, lq_sta, 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; return count;
@ -2992,12 +2981,21 @@ static void rs_remove_debugfs(void *priv, void *priv_sta)
} }
#endif #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 = { static struct rate_control_ops rs_ops = {
.module = NULL, .module = NULL,
.name = RS_NAME, .name = RS_NAME,
.tx_status = rs_tx_status, .tx_status = rs_tx_status,
.get_rate = rs_get_rate, .get_rate = rs_get_rate,
.rate_init = rs_rate_init, .rate_init = rs_rate_init_stub,
.alloc = rs_alloc, .alloc = rs_alloc,
.free = rs_free, .free = rs_free,
.alloc_sta = rs_alloc_sta, .alloc_sta = rs_alloc_sta,

View file

@ -403,7 +403,6 @@ struct iwl_lq_sta {
u8 is_green; u8 is_green;
u8 is_dup; u8 is_dup;
enum ieee80211_band band; enum ieee80211_band band;
u8 ibss_sta_added;
/* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
u32 supp_rates; 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); 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 * iwl_rate_control_register - Register the rate control algorithm callbacks
* *

View file

@ -144,9 +144,6 @@ int iwl_commit_rxon(struct iwl_priv *priv)
return 0; return 0;
} }
/* station table will be cleared */
priv->assoc_station_added = 0;
/* If we are currently associated and the new config requires /* If we are currently associated and the new config requires
* an RXON_ASSOC and the new config wants the associated mask enabled, * an RXON_ASSOC and the new config wants the associated mask enabled,
* we must clear the associated from the active configuration * 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); IWL_ERR(priv, "Error clearing ASSOC_MSK (%d)\n", ret);
return ret; return ret;
} }
iwl_clear_ucode_stations(priv, false);
iwl_restore_stations(priv);
} }
IWL_DEBUG_INFO(priv, "Sending RXON\n" 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); iwl_set_rxon_hwcrypto(priv, !priv->cfg->mod_params->sw_crypto);
/* Apply the new configuration /* Apply the new configuration
* RXON unassoc clears the station table in uCode, send it before * RXON unassoc clears the station table in uCode so restoration of
* we add the bcast station. If assoc bit is set, we will send RXON * stations is needed after it (the RXON command) completes
* after having added the bcast and bssid station.
*/ */
if (!new_assoc) { if (!new_assoc) {
ret = iwl_send_cmd_pdu(priv, REPLY_RXON, 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); IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
return ret; return ret;
} }
IWL_DEBUG_INFO(priv, "Return from !new_assoc RXON. \n");
memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon)); 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; 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 (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. * allow CTS-to-self if possible for new association.
* this is relevant only for 5000 series and up, * this is relevant only for 5000 series and up,
@ -2087,7 +2064,6 @@ static void iwl_alive_start(struct iwl_priv *priv)
goto restart; goto restart;
} }
iwl_clear_stations_table(priv);
ret = priv->cfg->ops->lib->alive_notify(priv); ret = priv->cfg->ops->lib->alive_notify(priv);
if (ret) { if (ret) {
IWL_WARN(priv, 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 */ /* After the ALIVE response, we can send host commands to the uCode */
set_bit(STATUS_ALIVE, &priv->status); 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)) if (iwl_is_rfkill(priv))
return; return;
@ -2143,6 +2126,8 @@ static void iwl_alive_start(struct iwl_priv *priv)
wake_up_interruptible(&priv->wait_command_queue); wake_up_interruptible(&priv->wait_command_queue);
iwl_power_update_mode(priv, true); iwl_power_update_mode(priv, true);
IWL_DEBUG_INFO(priv, "Updated power mode\n");
return; return;
@ -2162,7 +2147,7 @@ static void __iwl_down(struct iwl_priv *priv)
if (!exit_pending) if (!exit_pending)
set_bit(STATUS_EXIT_PENDING, &priv->status); set_bit(STATUS_EXIT_PENDING, &priv->status);
iwl_clear_stations_table(priv); iwl_clear_ucode_stations(priv, true);
/* Unblock any waiting calls */ /* Unblock any waiting calls */
wake_up_interruptible_all(&priv->wait_command_queue); 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++) { for (i = 0; i < MAX_HW_RESTARTS; i++) {
iwl_clear_stations_table(priv);
/* load bootstrap state machine, /* load bootstrap state machine,
* load bootstrap program into processor's memory, * load bootstrap program into processor's memory,
* prepare to load the "initialize" uCode */ * prepare to load the "initialize" uCode */
@ -2501,10 +2484,6 @@ void iwl_post_associate(struct iwl_priv *priv)
return; 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)) if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return; return;
@ -2556,6 +2535,9 @@ void iwl_post_associate(struct iwl_priv *priv)
iwlcore_commit_rxon(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) { switch (priv->iw_mode) {
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
break; break;
@ -2565,7 +2547,7 @@ void iwl_post_associate(struct iwl_priv *priv)
/* assume default assoc id */ /* assume default assoc id */
priv->assoc_id = 1; 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); iwl_send_beacon_cmd(priv);
break; break;
@ -2576,9 +2558,6 @@ void iwl_post_associate(struct iwl_priv *priv)
break; break;
} }
if (priv->iw_mode == NL80211_IFTYPE_ADHOC)
priv->assoc_station_added = 1;
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);
iwl_activate_qos(priv, 0); iwl_activate_qos(priv, 0);
spin_unlock_irqrestore(&priv->lock, flags); spin_unlock_irqrestore(&priv->lock, flags);
@ -2937,10 +2916,21 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
return ret; return ret;
case IEEE80211_AMPDU_TX_START: case IEEE80211_AMPDU_TX_START:
IWL_DEBUG_HT(priv, "start Tx\n"); 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: case IEEE80211_AMPDU_TX_STOP:
IWL_DEBUG_HT(priv, "stop Tx\n"); IWL_DEBUG_HT(priv, "stop Tx\n");
ret = iwl_tx_agg_stop(priv, sta->addr, tid); 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)) if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return 0; return 0;
else 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; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
int sta_id; int sta_id;
/*
* TODO: We really should use this callback to
* actually maintain the station table in
* the device.
*/
switch (cmd) { 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: case STA_NOTIFY_SLEEP:
WARN_ON(!sta_priv->client); WARN_ON(!sta_priv->client);
sta_priv->asleep = true; 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 * 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.data = (unsigned long)priv;
priv->ucode_trace.function = iwl_bg_ucode_trace; 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) if (!priv->cfg->use_isr_legacy)
tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
iwl_irq_tasklet, (unsigned long)priv); 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); cancel_work_sync(&priv->beacon_update);
del_timer_sync(&priv->statistics_periodic); del_timer_sync(&priv->statistics_periodic);
del_timer_sync(&priv->ucode_trace); 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, 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->mutex);
mutex_init(&priv->sync_cmd_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_channels = NULL;
priv->ieee_rates = NULL; priv->ieee_rates = NULL;
priv->band = IEEE80211_BAND_2GHZ; 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->iw_mode = NL80211_IFTYPE_STATION;
priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
priv->_agn.agg_tids_count = 0;
/* initialize force reset */ /* initialize force reset */
priv->force_reset[IWL_RF_RESET].reset_duration = 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, .ampdu_action = iwl_mac_ampdu_action,
.hw_scan = iwl_mac_hw_scan, .hw_scan = iwl_mac_hw_scan,
.sta_notify = iwl_mac_sta_notify, .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) 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_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
iwl_hw_detect(priv); 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); priv->cfg->name, priv->hw_rev);
/* We disable the RETRY_TIMEOUT register (0x41) to keep /* 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_rx_queue_free(priv, &priv->rxq);
iwl_hw_txq_ctx_free(priv); iwl_hw_txq_ctx_free(priv);
iwl_clear_stations_table(priv);
iwl_eeprom_free(priv); iwl_eeprom_free(priv);

View file

@ -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); memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
iwl_clear_stations_table(priv);
return iwlcore_commit_rxon(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); err = iwl_set_mode(priv, vif);
if (err) if (err)
goto out_err; goto out_err;
/* Add the broadcast address so we can send broadcast frames */
priv->cfg->ops->lib->add_bcast_station(priv);
goto out; goto out;
out_err: out_err:
@ -2339,6 +2341,8 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
mutex_lock(&priv->mutex); mutex_lock(&priv->mutex);
iwl_clear_ucode_stations(priv, true);
if (iwl_is_ready_rf(priv)) { if (iwl_is_ready_rf(priv)) {
iwl_scan_cancel_timeout(priv, 100); iwl_scan_cancel_timeout(priv, 100);
priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; 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); spin_lock_irqsave(&priv->lock, flags);
priv->assoc_id = 0; priv->assoc_id = 0;
priv->assoc_capability = 0; priv->assoc_capability = 0;
priv->assoc_station_added = 0;
/* new association get rid of ibss beacon skb */ /* new association get rid of ibss beacon skb */
if (priv->ibss_beacon) if (priv->ibss_beacon)
@ -3048,6 +3051,99 @@ int iwl_force_reset(struct iwl_priv *priv, int mode)
} }
return 0; 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 #ifdef CONFIG_PM

View file

@ -191,6 +191,14 @@ struct iwl_lib_ops {
struct iwl_temp_ops temp_ops; struct iwl_temp_ops temp_ops;
/* station management */ /* station management */
void (*add_bcast_station)(struct iwl_priv *priv); 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 { struct iwl_led_ops {
@ -295,6 +303,8 @@ struct iwl_cfg {
const bool support_wimax_coexist; const bool support_wimax_coexist;
u8 plcp_delta_threshold; u8 plcp_delta_threshold;
s32 chain_noise_scale; 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); struct iwl_rx_mem_buffer *rxb);
void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb); 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, void iwl_rx_statistics(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb); struct iwl_rx_mem_buffer *rxb);
void iwl_reply_statistics(struct iwl_priv *priv, 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); pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
return pci_lnk_ctl; return pci_lnk_ctl;
} }
void iwl_bg_monitor_recover(unsigned long data);
#ifdef CONFIG_PM #ifdef CONFIG_PM
int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state); int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state);
int iwl_pci_resume(struct pci_dev *pdev); 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); u8 flags, bool clear);
extern int iwl_verify_ucode(struct iwl_priv *priv); extern int iwl_verify_ucode(struct iwl_priv *priv);
extern int iwl_send_lq_cmd(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, extern void iwl_rx_reply_rx(struct iwl_priv *priv,
struct iwl_rx_mem_buffer *rxb); struct iwl_rx_mem_buffer *rxb);
extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv, extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv,

View file

@ -183,6 +183,10 @@ struct iwl_queue {
int n_bd; /* number of BDs in this queue */ int n_bd; /* number of BDs in this queue */
int write_ptr; /* 1-st empty entry (index) host_w*/ int write_ptr; /* 1-st empty entry (index) host_w*/
int read_ptr; /* last used entry (index) host_r*/ 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 */ dma_addr_t dma_addr; /* physical addr for BD's */
int n_window; /* safe queue window */ int n_window; /* safe queue window */
u32 id; u32 id;
@ -544,11 +548,18 @@ struct iwl_qos_info {
struct iwl_qosparam_cmd def_qos_parm; 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_station_entry {
struct iwl_addsta_cmd sta; struct iwl_addsta_cmd sta;
struct iwl_tid_data tid[MAX_TID_COUNT]; struct iwl_tid_data tid[MAX_TID_COUNT];
u8 used; u8 used;
struct iwl_hw_key keyinfo; 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_RF_RESET (HZ*3)
#define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) #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 { enum iwl_reset {
IWL_RF_RESET = 0, IWL_RF_RESET = 0,
IWL_FW_RESET, IWL_FW_RESET,
@ -1163,7 +1179,6 @@ struct iwl_priv {
u16 active_rate; u16 active_rate;
u8 assoc_station_added;
u8 start_calib; u8 start_calib;
struct iwl_sensitivity_data sensitivity_data; struct iwl_sensitivity_data sensitivity_data;
struct iwl_chain_noise_data chain_noise_data; struct iwl_chain_noise_data chain_noise_data;
@ -1285,6 +1300,11 @@ struct iwl_priv {
int ict_index; int ict_index;
u32 inta; u32 inta;
bool use_ict; bool use_ict;
/*
* reporting the number of tids has AGG on. 0 means
* no AGGREGATION
*/
u8 agg_tids_count;
} _agn; } _agn;
#endif #endif
}; };
@ -1348,6 +1368,7 @@ struct iwl_priv {
struct work_struct run_time_calib_work; struct work_struct run_time_calib_work;
struct timer_list statistics_periodic; struct timer_list statistics_periodic;
struct timer_list ucode_trace; struct timer_list ucode_trace;
struct timer_list monitor_recover;
bool hw_ready; bool hw_ready;
struct iwl_event_log event_log; struct iwl_event_log event_log;

View file

@ -616,29 +616,77 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv,
#define REG_RECALIB_PERIOD (60) #define REG_RECALIB_PERIOD (60)
#define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n" /* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */
void iwl_rx_statistics(struct iwl_priv *priv, #define ACK_CNT_RATIO (50)
struct iwl_rx_mem_buffer *rxb) #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; bool rc = true;
struct iwl_rx_packet *pkt = rxb_addr(rxb); 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; int combined_plcp_delta;
unsigned int plcp_msec; unsigned int plcp_msec;
unsigned long plcp_received_jiffies; 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 * check for plcp_err and trigger radio reset if it exceeds
* the plcp error threshold plcp_delta. * 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)); le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err));
if ((combined_plcp_delta > 0) && if ((combined_plcp_delta > 0) &&
((combined_plcp_delta * 100) / plcp_msec) > ((combined_plcp_delta * 100) / plcp_msec) >
priv->cfg->plcp_delta_threshold) { priv->cfg->plcp_delta_threshold) {
/* /*
* if plcp_err exceed the threshold, the following * if plcp_err exceed the threshold,
* data is printed in csv format: * the following data is printed in csv format:
* Text: plcp_err exceeded %d, * Text: plcp_err exceeded %d,
* Received ofdm.plcp_err, * Received ofdm.plcp_err,
* Current ofdm.plcp_err, * Current ofdm.plcp_err,
@ -672,22 +720,73 @@ void iwl_rx_statistics(struct iwl_priv *priv,
* combined_plcp_delta, * combined_plcp_delta,
* plcp_msec * 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, priv->cfg->plcp_delta_threshold,
le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err), le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err),
le32_to_cpu(priv->statistics.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(pkt->u.stats.rx.ofdm_ht.plcp_err),
le32_to_cpu( le32_to_cpu(
priv->statistics.rx.ofdm_ht.plcp_err), priv->statistics.rx.ofdm_ht.plcp_err),
combined_plcp_delta, plcp_msec); combined_plcp_delta, plcp_msec);
rc = false;
/*
* Reset the RF radio due to the high plcp
* error rate
*/
iwl_force_reset(priv, IWL_RF_RESET);
} }
} }
return rc;
}
EXPORT_SYMBOL(iwl_good_plcp_health);
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)); memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));

View file

@ -29,14 +29,12 @@
#include <net/mac80211.h> #include <net/mac80211.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/sched.h>
#include "iwl-dev.h" #include "iwl-dev.h"
#include "iwl-core.h" #include "iwl-core.h"
#include "iwl-sta.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) u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr)
{ {
int i; int i;
@ -64,6 +62,19 @@ u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr)
addr, priv->num_stations); addr, priv->num_stations);
out: 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); spin_unlock_irqrestore(&priv->sta_lock, flags);
return ret; return ret;
} }
@ -158,13 +169,6 @@ static void iwl_process_add_sta_resp(struct iwl_priv *priv,
priv->stations[sta_id].sta.mode == priv->stations[sta_id].sta.mode ==
STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
addsta->sta.addr); 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); spin_unlock_irqrestore(&priv->sta_lock, flags);
} }
@ -190,6 +194,10 @@ int iwl_send_add_sta(struct iwl_priv *priv,
.flags = flags, .flags = flags,
.data = data, .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) if (flags & CMD_ASYNC)
cmd.callback = iwl_add_sta_callback; 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, static u8 iwl_prep_station(struct iwl_priv *priv, const u8 *addr,
struct ieee80211_sta_ht_cap *ht_info) bool is_ap,
struct ieee80211_sta_ht_cap *ht_info)
{ {
struct iwl_station_entry *station; struct iwl_station_entry *station;
unsigned long flags_spin;
int i; int i;
int sta_id = IWL_INVALID_STATION; u8 sta_id = IWL_INVALID_STATION;
u16 rate; u16 rate;
spin_lock_irqsave(&priv->sta_lock, flags_spin);
if (is_ap) if (is_ap)
sta_id = IWL_AP_ID; sta_id = IWL_AP_ID;
else if (is_broadcast_ether_addr(addr)) 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; sta_id = i;
} }
/* These two conditions have the same outcome, but keep them separate /*
since they have different meanings */ * These two conditions have the same outcome, but keep them
if (unlikely(sta_id == IWL_INVALID_STATION)) { * separate
spin_unlock_irqrestore(&priv->sta_lock, flags_spin); */
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; 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)) { !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; return sta_id;
} }
station = &priv->stations[sta_id]; station = &priv->stations[sta_id];
station->used = IWL_STA_DRIVER_ACTIVE; station->used = IWL_STA_DRIVER_ACTIVE;
IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", 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... */ /* Turn on both antennas for the station... */
station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK);
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; return sta_id;
} }
EXPORT_SYMBOL(iwl_add_station);
static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, const u8 *addr) #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)
{ {
unsigned long flags; struct iwl_station_entry *station;
u8 sta_id = iwl_find_station(priv, addr); unsigned long flags_spin;
int ret = 0;
u8 sta_id;
BUG_ON(sta_id == IWL_INVALID_STATION); *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;
}
IWL_DEBUG_ASSOC(priv, "Removed STA from Ucode: %pM\n", addr); /*
* 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;
}
spin_lock_irqsave(&priv->sta_lock, flags); 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 */
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_common);
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(priv, REPLY_TX_LINK_QUALITY_CMD,
sizeof(link_cmd), &link_cmd);
}
/*
* 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)
{
int ret;
u8 sta_id;
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;
}
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 */ /* Ucode must be active and driver must be non active */
if (priv->stations[sta_id].used != IWL_STA_UCODE_ACTIVE) if (priv->stations[sta_id].used != IWL_STA_UCODE_ACTIVE)
IWL_ERR(priv, "removed non active STA %d\n", sta_id); IWL_ERR(priv, "removed non active STA %u\n", sta_id);
priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE;
memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry));
spin_unlock_irqrestore(&priv->sta_lock, flags); IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id);
} }
static void iwl_remove_sta_callback(struct iwl_priv *priv, static int iwl_send_remove_station(struct iwl_priv *priv,
struct iwl_device_cmd *cmd, struct iwl_station_entry *station)
struct iwl_rx_packet *pkt)
{
struct iwl_rem_sta_cmd *rm_sta =
(struct iwl_rem_sta_cmd *)cmd->cmd.payload;
const u8 *addr = rm_sta->addr;
if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) {
IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n",
pkt->hdr.flags);
return;
}
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;
}
}
static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr,
u8 flags)
{ {
struct iwl_rx_packet *pkt; struct iwl_rx_packet *pkt;
int ret; int ret;
unsigned long flags_spin;
struct iwl_rem_sta_cmd rm_sta_cmd; struct iwl_rem_sta_cmd rm_sta_cmd;
struct iwl_host_cmd cmd = { struct iwl_host_cmd cmd = {
.id = REPLY_REMOVE_STA, .id = REPLY_REMOVE_STA,
.len = sizeof(struct iwl_rem_sta_cmd), .len = sizeof(struct iwl_rem_sta_cmd),
.flags = flags, .flags = CMD_SYNC,
.data = &rm_sta_cmd, .data = &rm_sta_cmd,
}; };
memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
rm_sta_cmd.num_sta = 1; 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); ret = iwl_send_cmd(priv, &cmd);
if (ret || (flags & CMD_ASYNC)) if (ret)
return ret; return ret;
pkt = (struct iwl_rx_packet *)cmd.reply_page; 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) { if (!ret) {
switch (pkt->u.rem_sta.status) { switch (pkt->u.rem_sta.status) {
case REM_STA_SUCCESS_MSK: 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"); IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
break; break;
default: 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. * 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 sta_id = IWL_INVALID_STATION;
int i, ret = -EINVAL; int i, ret = -EINVAL;
unsigned long flags; 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); spin_lock_irqsave(&priv->sta_lock, flags);
if (is_ap) if (is_ap)
sta_id = IWL_AP_ID; sta_id = IWL_AP_ID;
else if (is_broadcast_ether_addr(addr))
sta_id = priv->hw_params.bcast_sta_id;
else else
for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++) for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++)
if (priv->stations[i].used && if (priv->stations[i].used &&
!compare_ether_addr(priv->stations[i].sta.sta.addr, !compare_ether_addr(priv->stations[i].sta.sta.addr,
addr)) { sta->addr)) {
sta_id = i; sta_id = i;
break; break;
} }
@ -464,17 +598,17 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
goto out; goto out;
IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", 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)) { if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
IWL_ERR(priv, "Removing %pM but non DRIVER active\n", IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n",
addr); sta->addr);
goto out; goto out;
} }
if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
IWL_ERR(priv, "Removing %pM but non UCODE active\n", IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n",
addr); sta->addr);
goto out; 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); BUG_ON(priv->num_stations < 0);
station = &priv->stations[sta_id];
spin_unlock_irqrestore(&priv->sta_lock, flags); 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; return ret;
out: out:
spin_unlock_irqrestore(&priv->sta_lock, flags); spin_unlock_irqrestore(&priv->sta_lock, flags);
@ -495,37 +630,122 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
} }
/** /**
* iwl_clear_stations_table - Clear the driver's station table * iwl_clear_ucode_stations() - clear entire station table driver and/or ucode
* * @priv:
* NOTE: This does not clear or otherwise alter the device's station table. * @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; int i;
unsigned long flags_spin;
bool cleared = false;
spin_lock_irqsave(&priv->sta_lock, flags); IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver%s\n",
force ? " and ucode" : "");
if (iwl_is_alive(priv) && if (force) {
!test_bit(STATUS_EXIT_PENDING, &priv->status) && if (!iwl_is_ready(priv)) {
iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL)) /*
IWL_ERR(priv, "Couldn't clear the station table\n"); * If device is not ready at this point the station
* table is likely already empty (uCode not ready
priv->num_stations = 0; * to receive station requests) or will soon be
memset(priv->stations, 0, sizeof(priv->stations)); * due to interface going down.
*/
/* clean ucode key table bit map */ IWL_DEBUG_INFO(priv, "Unable to remove stations from device - device not ready\n");
priv->ucode_key_table = 0; } else {
iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL);
/* 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);
} }
spin_unlock_irqrestore(&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 (!cleared)
IWL_DEBUG_INFO(priv, "No active stations found to be cleared\n");
} }
EXPORT_SYMBOL(iwl_clear_stations_table); EXPORT_SYMBOL(iwl_clear_ucode_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;
if (!iwl_is_ready(priv)) {
IWL_DEBUG_INFO(priv, "Not ready yet, not restoring any stations.\n");
return;
}
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;
}
}
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_restore_stations);
int iwl_get_free_ucode_key_index(struct iwl_priv *priv) 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 #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, 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 = { struct iwl_host_cmd cmd = {
.id = REPLY_TX_LINK_QUALITY_CMD, .id = REPLY_TX_LINK_QUALITY_CMD,
.len = sizeof(struct iwl_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; lq->sta_id = IWL_AP_ID;
iwl_dump_lq_cmd(priv, lq); iwl_dump_lq_cmd(priv, lq);
BUG_ON(init && (cmd.flags & CMD_ASYNC));
if (iwl_is_associated(priv) && priv->assoc_station_added) iwl_dump_lq_cmd(priv, lq);
return iwl_send_cmd(priv, &cmd); 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; return 0;
} }
EXPORT_SYMBOL(iwl_send_lq_cmd); 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. * iwl_add_bcast_station - add broadcast station into station table.
*/ */
void iwl_add_bcast_station(struct iwl_priv *priv) void iwl_add_bcast_station(struct iwl_priv *priv)
{ {
IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n"); 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, true);
/* Set up default rate scaling table in device's station table */
iwl_sta_init_bcast_lq(priv);
} }
EXPORT_SYMBOL(iwl_add_bcast_station); 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) void iwl3945_add_bcast_station(struct iwl_priv *priv)
{ {
IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n"); 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); 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 /* If we are a client station in a BSS network, use the special
* AP station entry (that's the only station we communicate with) */ * AP station entry (that's the only station we communicate with) */
case NL80211_IFTYPE_STATION: 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; return IWL_AP_ID;
/* If we are an AP, then find the station, or use BCAST */ /* 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) if (sta_id != IWL_INVALID_STATION)
return sta_id; 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. " IWL_DEBUG_DROP(priv, "Station %pM not in station map. "
"Defaulting to broadcast...\n", "Defaulting to broadcast...\n",
hdr->addr1); 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); 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);

View file

@ -32,6 +32,12 @@
#define HW_KEY_DYNAMIC 0 #define HW_KEY_DYNAMIC 0
#define HW_KEY_DEFAULT 1 #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 * iwl_find_station - Find station id for a given BSSID
* @bssid: MAC address of station ID to find * @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, struct ieee80211_key_conf *keyconf,
const u8 *addr, u32 iv32, u16 *phase1key); 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 iwl_add_bcast_station(struct iwl_priv *priv);
void iwl3945_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_restore_stations(struct iwl_priv *priv);
void iwl_clear_stations_table(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_free_ucode_key_index(struct iwl_priv *priv);
int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr); 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_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
int iwl_send_add_sta(struct iwl_priv *priv, int iwl_send_add_sta(struct iwl_priv *priv,
struct iwl_addsta_cmd *sta, u8 flags); struct iwl_addsta_cmd *sta, u8 flags);
u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags, int iwl_add_local_station(struct iwl_priv *priv, const u8 *addr, bool init_rs);
struct ieee80211_sta_ht_cap *ht_info); 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); 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, int iwl_sta_rx_agg_start(struct iwl_priv *priv,
const u8 *addr, int tid, u16 ssn); const u8 *addr, int tid, u16 ssn);

View file

@ -322,6 +322,8 @@ static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
q->high_mark = 2; q->high_mark = 2;
q->write_ptr = q->read_ptr = 0; q->write_ptr = q->read_ptr = 0;
q->last_read_ptr = 0;
q->repeat_same_read_ptr = 0;
return 0; return 0;
} }

View file

@ -2480,8 +2480,6 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
goto restart; goto restart;
} }
iwl_clear_stations_table(priv);
rfkill = iwl_read_prph(priv, APMG_RFKILL_REG); rfkill = iwl_read_prph(priv, APMG_RFKILL_REG);
IWL_DEBUG_INFO(priv, "RFKILL status: 0x%x\n", rfkill); 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 */ /* After the ALIVE response, we can send commands to 3945 uCode */
set_bit(STATUS_ALIVE, &priv->status); 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)) if (iwl_is_rfkill(priv))
return; return;
@ -2558,7 +2563,8 @@ static void __iwl3945_down(struct iwl_priv *priv)
if (!exit_pending) if (!exit_pending)
set_bit(STATUS_EXIT_PENDING, &priv->status); 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 */ /* Unblock any waiting calls */
wake_up_interruptible_all(&priv->wait_command_queue); 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++) { for (i = 0; i < MAX_HW_RESTARTS; i++) {
iwl_clear_stations_table(priv);
/* load bootstrap state machine, /* load bootstrap state machine,
* load bootstrap program into processor's memory, * load bootstrap program into processor's memory,
* prepare to load the "initialize" uCode */ * prepare to load the "initialize" uCode */
@ -3119,12 +3123,13 @@ void iwl3945_post_associate(struct iwl_priv *priv)
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
priv->assoc_id = 1; 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, iwl3945_sync_sta(priv, IWL_STA_ID,
(priv->band == IEEE80211_BAND_5GHZ) ? (priv->band == IEEE80211_BAND_5GHZ) ?
IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP, IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP,
CMD_ASYNC); CMD_ASYNC);
iwl3945_rate_scale_init(priv->hw, IWL_STA_ID); iwl3945_rate_scale_init(priv->hw, IWL_STA_ID);
iwl3945_send_beacon_cmd(priv); iwl3945_send_beacon_cmd(priv);
break; break;
@ -3309,7 +3314,7 @@ void iwl3945_config_ap(struct iwl_priv *priv)
/* restore RXON assoc */ /* restore RXON assoc */
priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK; priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
iwlcore_commit_rxon(priv); 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); 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; 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 * sysfs attributes
@ -3766,6 +3803,13 @@ static void iwl3945_setup_deferred_work(struct iwl_priv *priv)
iwl3945_hw_setup_deferred_work(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)) tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
iwl3945_irq_tasklet, (unsigned long)priv); 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->scan_check);
cancel_delayed_work(&priv->alive_start); cancel_delayed_work(&priv->alive_start);
cancel_work_sync(&priv->beacon_update); 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[] = { static struct attribute *iwl3945_sysfs_entries[] = {
@ -3815,7 +3861,9 @@ static struct ieee80211_ops iwl3945_hw_ops = {
.conf_tx = iwl_mac_conf_tx, .conf_tx = iwl_mac_conf_tx,
.reset_tsf = iwl_mac_reset_tsf, .reset_tsf = iwl_mac_reset_tsf,
.bss_info_changed = iwl_bss_info_changed, .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) 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->mutex);
mutex_init(&priv->sync_cmd_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_channels = NULL;
priv->ieee_rates = NULL; priv->ieee_rates = NULL;
priv->band = IEEE80211_BAND_2GHZ; 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_hw_txq_ctx_free(priv);
iwl3945_unset_hw_params(priv); iwl3945_unset_hw_params(priv);
iwl_clear_stations_table(priv);
/*netif_stop_queue(dev); */ /*netif_stop_queue(dev); */
flush_workqueue(priv->workqueue); flush_workqueue(priv->workqueue);

View file

@ -31,6 +31,9 @@ u8 lbs_bg_rates[MAX_RATES] =
0x00, 0x00 }; 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. * @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) { if (status_code) {
lbs_mac_event_disconnected(priv); lbs_mac_event_disconnected(priv);
ret = -1; ret = status_code;
goto done; goto done;
} }
@ -813,7 +816,24 @@ static int lbs_try_associate(struct lbs_private *priv,
goto out; goto out;
ret = lbs_associate(priv, assoc_req, CMD_802_11_ASSOCIATE); 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: out:
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret); lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
return ret; return ret;

View file

@ -133,6 +133,7 @@ struct lbs_private {
u8 wpa_ie_len; u8 wpa_ie_len;
u16 wep_tx_keyidx; u16 wep_tx_keyidx;
struct enc_key wep_keys[4]; struct enc_key wep_keys[4];
u8 authtype_auto;
/* Wake On LAN */ /* Wake On LAN */
uint32_t wol_criteria; uint32_t wol_criteria;

View file

@ -835,6 +835,7 @@ static int lbs_init_adapter(struct lbs_private *priv)
priv->is_auto_deep_sleep_enabled = 0; priv->is_auto_deep_sleep_enabled = 0;
priv->wakeup_dev_required = 0; priv->wakeup_dev_required = 0;
init_waitqueue_head(&priv->ds_awake_q); init_waitqueue_head(&priv->ds_awake_q);
priv->authtype_auto = 1;
mutex_init(&priv->lock); mutex_init(&priv->lock);

View file

@ -1440,8 +1440,10 @@ static int lbs_set_encode(struct net_device *dev,
set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
if (dwrq->flags & IW_ENCODE_RESTRICTED) { if (dwrq->flags & IW_ENCODE_RESTRICTED) {
priv->authtype_auto = 0;
assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
} else if (dwrq->flags & IW_ENCODE_OPEN) { } else if (dwrq->flags & IW_ENCODE_OPEN) {
priv->authtype_auto = 0;
assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; 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; goto out;
if (dwrq->flags & IW_ENCODE_RESTRICTED) { if (dwrq->flags & IW_ENCODE_RESTRICTED) {
priv->authtype_auto = 0;
assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
} else if (dwrq->flags & IW_ENCODE_OPEN) { } else if (dwrq->flags & IW_ENCODE_OPEN) {
priv->authtype_auto = 0;
assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM; assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
} }

View file

@ -1505,46 +1505,44 @@ static const struct iw_priv_args orinoco_privtab[] = {
* Structures to export the Wireless Handlers * 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[] = { static const iw_handler orinoco_handler[] = {
STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit), IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)orinoco_ioctl_commit),
STD_IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname), IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname),
STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq), IW_HANDLER(SIOCSIWFREQ, (iw_handler)orinoco_ioctl_setfreq),
STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq), IW_HANDLER(SIOCGIWFREQ, (iw_handler)orinoco_ioctl_getfreq),
STD_IW_HANDLER(SIOCSIWMODE, cfg80211_wext_siwmode), IW_HANDLER(SIOCSIWMODE, (iw_handler)cfg80211_wext_siwmode),
STD_IW_HANDLER(SIOCGIWMODE, cfg80211_wext_giwmode), IW_HANDLER(SIOCGIWMODE, (iw_handler)cfg80211_wext_giwmode),
STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens), IW_HANDLER(SIOCSIWSENS, (iw_handler)orinoco_ioctl_setsens),
STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens), IW_HANDLER(SIOCGIWSENS, (iw_handler)orinoco_ioctl_getsens),
STD_IW_HANDLER(SIOCGIWRANGE, cfg80211_wext_giwrange), IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange),
STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy), IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy), IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy), IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy), IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap), IW_HANDLER(SIOCSIWAP, (iw_handler)orinoco_ioctl_setwap),
STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap), IW_HANDLER(SIOCGIWAP, (iw_handler)orinoco_ioctl_getwap),
STD_IW_HANDLER(SIOCSIWSCAN, cfg80211_wext_siwscan), IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan),
STD_IW_HANDLER(SIOCGIWSCAN, cfg80211_wext_giwscan), IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan),
STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid), IW_HANDLER(SIOCSIWESSID, (iw_handler)orinoco_ioctl_setessid),
STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid), IW_HANDLER(SIOCGIWESSID, (iw_handler)orinoco_ioctl_getessid),
STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate), IW_HANDLER(SIOCSIWRATE, (iw_handler)orinoco_ioctl_setrate),
STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate), IW_HANDLER(SIOCGIWRATE, (iw_handler)orinoco_ioctl_getrate),
STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts), IW_HANDLER(SIOCSIWRTS, (iw_handler)orinoco_ioctl_setrts),
STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts), IW_HANDLER(SIOCGIWRTS, (iw_handler)orinoco_ioctl_getrts),
STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag), IW_HANDLER(SIOCSIWFRAG, (iw_handler)orinoco_ioctl_setfrag),
STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag), IW_HANDLER(SIOCGIWFRAG, (iw_handler)orinoco_ioctl_getfrag),
STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry), IW_HANDLER(SIOCGIWRETRY, (iw_handler)orinoco_ioctl_getretry),
STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode), IW_HANDLER(SIOCSIWENCODE, (iw_handler)orinoco_ioctl_setiwencode),
STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode), IW_HANDLER(SIOCGIWENCODE, (iw_handler)orinoco_ioctl_getiwencode),
STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower), IW_HANDLER(SIOCSIWPOWER, (iw_handler)orinoco_ioctl_setpower),
STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower), IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower),
STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie), IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie),
STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie), IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie),
STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme), IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme),
STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth), IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth),
STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth), IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth),
STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext), IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_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 Added typecasting since we no longer use iwreq_data -- Moustafa
*/ */
static const iw_handler orinoco_private_handler[] = { static const iw_handler orinoco_private_handler[] = {
[0] = (iw_handler) orinoco_ioctl_reset, [0] = (iw_handler)orinoco_ioctl_reset,
[1] = (iw_handler) orinoco_ioctl_reset, [1] = (iw_handler)orinoco_ioctl_reset,
[2] = (iw_handler) orinoco_ioctl_setport3, [2] = (iw_handler)orinoco_ioctl_setport3,
[3] = (iw_handler) orinoco_ioctl_getport3, [3] = (iw_handler)orinoco_ioctl_getport3,
[4] = (iw_handler) orinoco_ioctl_setpreamble, [4] = (iw_handler)orinoco_ioctl_setpreamble,
[5] = (iw_handler) orinoco_ioctl_getpreamble, [5] = (iw_handler)orinoco_ioctl_getpreamble,
[6] = (iw_handler) orinoco_ioctl_setibssport, [6] = (iw_handler)orinoco_ioctl_setibssport,
[7] = (iw_handler) orinoco_ioctl_getibssport, [7] = (iw_handler)orinoco_ioctl_getibssport,
[9] = (iw_handler) orinoco_ioctl_getrid, [9] = (iw_handler)orinoco_ioctl_getrid,
}; };
const struct iw_handler_def orinoco_handler_def = { const struct iw_handler_def orinoco_handler_def = {

View file

@ -1113,10 +1113,10 @@ static const struct ethtool_ops netdev_ethtool_ops = {
/* /*
* Wireless Handler : get protocol name * Wireless Handler : get protocol name
*/ */
static int ray_get_name(struct net_device *dev, static int ray_get_name(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, char *cwrq, char *extra) union iwreq_data *wrqu, char *extra)
{ {
strcpy(cwrq, "IEEE 802.11-FH"); strcpy(wrqu->name, "IEEE 802.11-FH");
return 0; return 0;
} }
@ -1124,9 +1124,8 @@ static int ray_get_name(struct net_device *dev,
/* /*
* Wireless Handler : set frequency * Wireless Handler : set frequency
*/ */
static int ray_set_freq(struct net_device *dev, static int ray_set_freq(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_freq *fwrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); ray_dev_t *local = netdev_priv(dev);
int err = -EINPROGRESS; /* Call commit handler */ int err = -EINPROGRESS; /* Call commit handler */
@ -1136,10 +1135,10 @@ static int ray_set_freq(struct net_device *dev,
return -EBUSY; return -EBUSY;
/* Setting by channel number */ /* 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; err = -EOPNOTSUPP;
else else
local->sparm.b5.a_hop_pattern = fwrq->m; local->sparm.b5.a_hop_pattern = wrqu->freq.m;
return err; return err;
} }
@ -1148,14 +1147,13 @@ static int ray_set_freq(struct net_device *dev,
/* /*
* Wireless Handler : get frequency * Wireless Handler : get frequency
*/ */
static int ray_get_freq(struct net_device *dev, static int ray_get_freq(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_freq *fwrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); ray_dev_t *local = netdev_priv(dev);
fwrq->m = local->sparm.b5.a_hop_pattern; wrqu->freq.m = local->sparm.b5.a_hop_pattern;
fwrq->e = 0; wrqu->freq.e = 0;
return 0; return 0;
} }
@ -1163,9 +1161,8 @@ static int ray_get_freq(struct net_device *dev,
/* /*
* Wireless Handler : set ESSID * Wireless Handler : set ESSID
*/ */
static int ray_set_essid(struct net_device *dev, static int ray_set_essid(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_point *dwrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); ray_dev_t *local = netdev_priv(dev);
@ -1174,19 +1171,17 @@ static int ray_set_essid(struct net_device *dev,
return -EBUSY; return -EBUSY;
/* Check if we asked for `any' */ /* Check if we asked for `any' */
if (dwrq->flags == 0) { if (wrqu->essid.flags == 0)
/* Corey : can you do that ? */ /* Corey : can you do that ? */
return -EOPNOTSUPP; return -EOPNOTSUPP;
} else {
/* Check the size of the string */
if (dwrq->length > IW_ESSID_MAX_SIZE) {
return -E2BIG;
}
/* Set the ESSID in the card */ /* Check the size of the string */
memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE); if (wrqu->essid.length > IW_ESSID_MAX_SIZE)
memcpy(local->sparm.b5.a_current_ess_id, extra, dwrq->length); 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 */ return -EINPROGRESS; /* Call commit handler */
} }
@ -1195,9 +1190,8 @@ static int ray_set_essid(struct net_device *dev,
/* /*
* Wireless Handler : get ESSID * Wireless Handler : get ESSID
*/ */
static int ray_get_essid(struct net_device *dev, static int ray_get_essid(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_point *dwrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); 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); memcpy(extra, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE);
/* Push it out ! */ /* Push it out ! */
dwrq->length = strlen(extra); wrqu->essid.length = strlen(extra);
dwrq->flags = 1; /* active */ wrqu->essid.flags = 1; /* active */
return 0; return 0;
} }
@ -1215,14 +1209,13 @@ static int ray_get_essid(struct net_device *dev,
/* /*
* Wireless Handler : get AP address * Wireless Handler : get AP address
*/ */
static int ray_get_wap(struct net_device *dev, static int ray_get_wap(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct sockaddr *awrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); ray_dev_t *local = netdev_priv(dev);
memcpy(awrq->sa_data, local->bss_id, ETH_ALEN); memcpy(wrqu->ap_addr.sa_data, local->bss_id, ETH_ALEN);
awrq->sa_family = ARPHRD_ETHER; wrqu->ap_addr.sa_family = ARPHRD_ETHER;
return 0; return 0;
} }
@ -1231,9 +1224,8 @@ static int ray_get_wap(struct net_device *dev,
/* /*
* Wireless Handler : set Bit-Rate * Wireless Handler : set Bit-Rate
*/ */
static int ray_set_rate(struct net_device *dev, static int ray_set_rate(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_param *vwrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); ray_dev_t *local = netdev_priv(dev);
@ -1242,15 +1234,15 @@ static int ray_set_rate(struct net_device *dev,
return -EBUSY; return -EBUSY;
/* Check if rate is in range */ /* Check if rate is in range */
if ((vwrq->value != 1000000) && (vwrq->value != 2000000)) if ((wrqu->bitrate.value != 1000000) && (wrqu->bitrate.value != 2000000))
return -EINVAL; return -EINVAL;
/* Hack for 1.5 Mb/s instead of 2 Mb/s */ /* Hack for 1.5 Mb/s instead of 2 Mb/s */
if ((local->fw_ver == 0x55) && /* Please check */ if ((local->fw_ver == 0x55) && /* Please check */
(vwrq->value == 2000000)) (wrqu->bitrate.value == 2000000))
local->net_default_tx_rate = 3; local->net_default_tx_rate = 3;
else else
local->net_default_tx_rate = vwrq->value / 500000; local->net_default_tx_rate = wrqu->bitrate.value / 500000;
return 0; return 0;
} }
@ -1259,17 +1251,16 @@ static int ray_set_rate(struct net_device *dev,
/* /*
* Wireless Handler : get Bit-Rate * Wireless Handler : get Bit-Rate
*/ */
static int ray_get_rate(struct net_device *dev, static int ray_get_rate(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_param *vwrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); ray_dev_t *local = netdev_priv(dev);
if (local->net_default_tx_rate == 3) if (local->net_default_tx_rate == 3)
vwrq->value = 2000000; /* Hum... */ wrqu->bitrate.value = 2000000; /* Hum... */
else else
vwrq->value = local->net_default_tx_rate * 500000; wrqu->bitrate.value = local->net_default_tx_rate * 500000;
vwrq->fixed = 0; /* We are in auto mode */ wrqu->bitrate.fixed = 0; /* We are in auto mode */
return 0; return 0;
} }
@ -1278,19 +1269,18 @@ static int ray_get_rate(struct net_device *dev,
/* /*
* Wireless Handler : set RTS threshold * Wireless Handler : set RTS threshold
*/ */
static int ray_set_rts(struct net_device *dev, static int ray_set_rts(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_param *vwrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); ray_dev_t *local = netdev_priv(dev);
int rthr = vwrq->value; int rthr = wrqu->rts.value;
/* Reject if card is already initialised */ /* Reject if card is already initialised */
if (local->card_status != CARD_AWAITING_PARAM) if (local->card_status != CARD_AWAITING_PARAM)
return -EBUSY; return -EBUSY;
/* if(wrq->u.rts.fixed == 0) we should complain */ /* if(wrq->u.rts.fixed == 0) we should complain */
if (vwrq->disabled) if (wrqu->rts.disabled)
rthr = 32767; rthr = 32767;
else { else {
if ((rthr < 0) || (rthr > 2347)) /* What's the max packet size ??? */ 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 * Wireless Handler : get RTS threshold
*/ */
static int ray_get_rts(struct net_device *dev, static int ray_get_rts(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_param *vwrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); 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]; + local->sparm.b5.a_rts_threshold[1];
vwrq->disabled = (vwrq->value == 32767); wrqu->rts.disabled = (wrqu->rts.value == 32767);
vwrq->fixed = 1; wrqu->rts.fixed = 1;
return 0; return 0;
} }
@ -1324,19 +1313,18 @@ static int ray_get_rts(struct net_device *dev,
/* /*
* Wireless Handler : set Fragmentation threshold * Wireless Handler : set Fragmentation threshold
*/ */
static int ray_set_frag(struct net_device *dev, static int ray_set_frag(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_param *vwrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); ray_dev_t *local = netdev_priv(dev);
int fthr = vwrq->value; int fthr = wrqu->frag.value;
/* Reject if card is already initialised */ /* Reject if card is already initialised */
if (local->card_status != CARD_AWAITING_PARAM) if (local->card_status != CARD_AWAITING_PARAM)
return -EBUSY; return -EBUSY;
/* if(wrq->u.frag.fixed == 0) should complain */ /* if(wrq->u.frag.fixed == 0) should complain */
if (vwrq->disabled) if (wrqu->frag.disabled)
fthr = 32767; fthr = 32767;
else { else {
if ((fthr < 256) || (fthr > 2347)) /* To check out ! */ 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 * Wireless Handler : get Fragmentation threshold
*/ */
static int ray_get_frag(struct net_device *dev, static int ray_get_frag(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_param *vwrq, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); 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]; + local->sparm.b5.a_frag_threshold[1];
vwrq->disabled = (vwrq->value == 32767); wrqu->frag.disabled = (wrqu->frag.value == 32767);
vwrq->fixed = 1; wrqu->frag.fixed = 1;
return 0; return 0;
} }
@ -1370,8 +1357,8 @@ static int ray_get_frag(struct net_device *dev,
/* /*
* Wireless Handler : set Mode of Operation * Wireless Handler : set Mode of Operation
*/ */
static int ray_set_mode(struct net_device *dev, static int ray_set_mode(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, __u32 *uwrq, char *extra) union iwreq_data *wrqu, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); ray_dev_t *local = netdev_priv(dev);
int err = -EINPROGRESS; /* Call commit handler */ 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) if (local->card_status != CARD_AWAITING_PARAM)
return -EBUSY; return -EBUSY;
switch (*uwrq) { switch (wrqu->mode) {
case IW_MODE_ADHOC: case IW_MODE_ADHOC:
card_mode = 0; card_mode = 0;
/* Fall through */ /* Fall through */
@ -1399,15 +1386,15 @@ static int ray_set_mode(struct net_device *dev,
/* /*
* Wireless Handler : get Mode of Operation * Wireless Handler : get Mode of Operation
*/ */
static int ray_get_mode(struct net_device *dev, static int ray_get_mode(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, __u32 *uwrq, char *extra) union iwreq_data *wrqu, char *extra)
{ {
ray_dev_t *local = netdev_priv(dev); ray_dev_t *local = netdev_priv(dev);
if (local->sparm.b5.a_network_type) if (local->sparm.b5.a_network_type)
*uwrq = IW_MODE_INFRA; wrqu->mode = IW_MODE_INFRA;
else else
*uwrq = IW_MODE_ADHOC; wrqu->mode = IW_MODE_ADHOC;
return 0; return 0;
} }
@ -1416,16 +1403,15 @@ static int ray_get_mode(struct net_device *dev,
/* /*
* Wireless Handler : get range info * Wireless Handler : get range info
*/ */
static int ray_get_range(struct net_device *dev, static int ray_get_range(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
struct iw_point *dwrq, char *extra)
{ {
struct iw_range *range = (struct iw_range *)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) */ /* 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 */ /* Set the Wireless Extension versions */
range->we_version_compiled = WIRELESS_EXT; 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 * Wireless Private Handler : set framing mode
*/ */
static int ray_set_framing(struct net_device *dev, static int ray_set_framing(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra) union iwreq_data *wrqu, char *extra)
{ {
translate = *(extra); /* Set framing mode */ translate = *(extra); /* Set framing mode */
@ -1461,8 +1446,7 @@ static int ray_set_framing(struct net_device *dev,
/* /*
* Wireless Private Handler : get framing mode * Wireless Private Handler : get framing mode
*/ */
static int ray_get_framing(struct net_device *dev, static int ray_get_framing(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra) union iwreq_data *wrqu, char *extra)
{ {
*(extra) = translate; *(extra) = translate;
@ -1474,8 +1458,7 @@ static int ray_get_framing(struct net_device *dev,
/* /*
* Wireless Private Handler : get country * Wireless Private Handler : get country
*/ */
static int ray_get_country(struct net_device *dev, static int ray_get_country(struct net_device *dev, struct iw_request_info *info,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra) union iwreq_data *wrqu, char *extra)
{ {
*(extra) = country; *(extra) = country;
@ -1487,10 +1470,9 @@ static int ray_get_country(struct net_device *dev,
/* /*
* Commit handler : called after a bunch of SET operations * Commit handler : called after a bunch of SET operations
*/ */
static int ray_commit(struct net_device *dev, struct iw_request_info *info, /* NULL */ static int ray_commit(struct net_device *dev, struct iw_request_info *info,
void *zwrq, /* NULL */ union iwreq_data *wrqu, char *extra)
char *extra) {
{ /* NULL */
return 0; return 0;
} }
@ -1531,28 +1513,28 @@ static iw_stats *ray_get_wireless_stats(struct net_device *dev)
*/ */
static const iw_handler ray_handler[] = { static const iw_handler ray_handler[] = {
[SIOCSIWCOMMIT - SIOCIWFIRST] = (iw_handler) ray_commit, IW_HANDLER(SIOCSIWCOMMIT, ray_commit),
[SIOCGIWNAME - SIOCIWFIRST] = (iw_handler) ray_get_name, IW_HANDLER(SIOCGIWNAME, ray_get_name),
[SIOCSIWFREQ - SIOCIWFIRST] = (iw_handler) ray_set_freq, IW_HANDLER(SIOCSIWFREQ, ray_set_freq),
[SIOCGIWFREQ - SIOCIWFIRST] = (iw_handler) ray_get_freq, IW_HANDLER(SIOCGIWFREQ, ray_get_freq),
[SIOCSIWMODE - SIOCIWFIRST] = (iw_handler) ray_set_mode, IW_HANDLER(SIOCSIWMODE, ray_set_mode),
[SIOCGIWMODE - SIOCIWFIRST] = (iw_handler) ray_get_mode, IW_HANDLER(SIOCGIWMODE, ray_get_mode),
[SIOCGIWRANGE - SIOCIWFIRST] = (iw_handler) ray_get_range, IW_HANDLER(SIOCGIWRANGE, ray_get_range),
#ifdef WIRELESS_SPY #ifdef WIRELESS_SPY
[SIOCSIWSPY - SIOCIWFIRST] = (iw_handler) iw_handler_set_spy, IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
[SIOCGIWSPY - SIOCIWFIRST] = (iw_handler) iw_handler_get_spy, IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
[SIOCSIWTHRSPY - SIOCIWFIRST] = (iw_handler) iw_handler_set_thrspy, IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
[SIOCGIWTHRSPY - SIOCIWFIRST] = (iw_handler) iw_handler_get_thrspy, IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
#endif /* WIRELESS_SPY */ #endif /* WIRELESS_SPY */
[SIOCGIWAP - SIOCIWFIRST] = (iw_handler) ray_get_wap, IW_HANDLER(SIOCGIWAP, ray_get_wap),
[SIOCSIWESSID - SIOCIWFIRST] = (iw_handler) ray_set_essid, IW_HANDLER(SIOCSIWESSID, ray_set_essid),
[SIOCGIWESSID - SIOCIWFIRST] = (iw_handler) ray_get_essid, IW_HANDLER(SIOCGIWESSID, ray_get_essid),
[SIOCSIWRATE - SIOCIWFIRST] = (iw_handler) ray_set_rate, IW_HANDLER(SIOCSIWRATE, ray_set_rate),
[SIOCGIWRATE - SIOCIWFIRST] = (iw_handler) ray_get_rate, IW_HANDLER(SIOCGIWRATE, ray_get_rate),
[SIOCSIWRTS - SIOCIWFIRST] = (iw_handler) ray_set_rts, IW_HANDLER(SIOCSIWRTS, ray_set_rts),
[SIOCGIWRTS - SIOCIWFIRST] = (iw_handler) ray_get_rts, IW_HANDLER(SIOCGIWRTS, ray_get_rts),
[SIOCSIWFRAG - SIOCIWFIRST] = (iw_handler) ray_set_frag, IW_HANDLER(SIOCSIWFRAG, ray_set_frag),
[SIOCGIWFRAG - SIOCIWFIRST] = (iw_handler) ray_get_frag, IW_HANDLER(SIOCGIWFRAG, ray_get_frag),
}; };
#define SIOCSIPFRAMING SIOCIWFIRSTPRIV /* Set framing mode */ #define SIOCSIPFRAMING SIOCIWFIRSTPRIV /* Set framing mode */
@ -1560,9 +1542,9 @@ static const iw_handler ray_handler[] = {
#define SIOCGIPCOUNTRY SIOCIWFIRSTPRIV + 3 /* Get country code */ #define SIOCGIPCOUNTRY SIOCIWFIRSTPRIV + 3 /* Get country code */
static const iw_handler ray_private_handler[] = { static const iw_handler ray_private_handler[] = {
[0] = (iw_handler) ray_set_framing, [0] = ray_set_framing,
[1] = (iw_handler) ray_get_framing, [1] = ray_get_framing,
[3] = (iw_handler) ray_get_country, [3] = ray_get_country,
}; };
static const struct iw_priv_args ray_private_args[] = { static const struct iw_priv_args ray_private_args[] = {

View file

@ -53,6 +53,8 @@ enum {
DEBUG_MAC80211 = BIT(11), DEBUG_MAC80211 = BIT(11),
DEBUG_CMD = BIT(12), DEBUG_CMD = BIT(12),
DEBUG_ACX = BIT(13), DEBUG_ACX = BIT(13),
DEBUG_SDIO = BIT(14),
DEBUG_FILTERS = BIT(15),
DEBUG_ALL = ~0, DEBUG_ALL = ~0,
}; };
@ -344,12 +346,14 @@ struct wl1271_if_operations {
bool fixed); bool fixed);
void (*reset)(struct wl1271 *wl); void (*reset)(struct wl1271 *wl);
void (*init)(struct wl1271 *wl); void (*init)(struct wl1271 *wl);
void (*power)(struct wl1271 *wl, bool enable);
struct device* (*dev)(struct wl1271 *wl); struct device* (*dev)(struct wl1271 *wl);
void (*enable_irq)(struct wl1271 *wl); void (*enable_irq)(struct wl1271 *wl);
void (*disable_irq)(struct wl1271 *wl); void (*disable_irq)(struct wl1271 *wl);
}; };
struct wl1271 { struct wl1271 {
struct platform_device *plat_dev;
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
bool mac80211_registered; bool mac80211_registered;
@ -456,6 +460,7 @@ struct wl1271 {
/* Default key (for WEP) */ /* Default key (for WEP) */
u32 default_key; u32 default_key;
unsigned int filters;
unsigned int rx_config; unsigned int rx_config;
unsigned int rx_filter; unsigned int rx_filter;
@ -483,6 +488,8 @@ struct wl1271 {
/* Current chipset configuration */ /* Current chipset configuration */
struct conf_drv_settings conf; struct conf_drv_settings conf;
bool sg_enabled;
struct list_head list; struct list_head list;
}; };

View file

@ -534,7 +534,7 @@ 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)
{ {
struct acx_bt_wlan_coex *pta; struct acx_bt_wlan_coex *pta;
int ret; int ret;
@ -547,7 +547,10 @@ int wl1271_acx_sg_enable(struct wl1271 *wl)
goto out; 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)); ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta));
if (ret < 0) { if (ret < 0) {
@ -564,7 +567,7 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl)
{ {
struct acx_bt_wlan_coex_param *param; struct acx_bt_wlan_coex_param *param;
struct conf_sg_settings *c = &wl->conf.sg; struct conf_sg_settings *c = &wl->conf.sg;
int ret; int i, ret;
wl1271_debug(DEBUG_ACX, "acx sg cfg"); wl1271_debug(DEBUG_ACX, "acx sg cfg");
@ -575,19 +578,9 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl)
} }
/* BT-WLAN coext parameters */ /* BT-WLAN coext parameters */
param->per_threshold = cpu_to_le32(c->per_threshold); for (i = 0; i < CONF_SG_PARAMS_MAX; i++)
param->max_scan_compensation_time = param->params[i] = c->params[i];
cpu_to_le32(c->max_scan_compensation_time); param->param_idx = CONF_SG_PARAMS_ALL;
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;
ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param)); ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param));
if (ret < 0) { if (ret < 0) {

View file

@ -392,29 +392,21 @@ struct acx_conn_monit_params {
__le32 bss_lose_timeout; /* number of TU's from synch fail */ __le32 bss_lose_timeout; /* number of TU's from synch fail */
} __attribute__ ((packed)); } __attribute__ ((packed));
enum {
SG_ENABLE = 0,
SG_DISABLE,
SG_SENSE_NO_ACTIVITY,
SG_SENSE_ACTIVE
};
struct acx_bt_wlan_coex { struct acx_bt_wlan_coex {
struct acx_header header; 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 enable;
u8 pad[3]; u8 pad[3];
} __attribute__ ((packed)); } __attribute__ ((packed));
struct acx_bt_wlan_coex_param {
struct acx_header header;
__le32 params[CONF_SG_PARAMS_MAX];
u8 param_idx;
u8 padding[3];
} __attribute__ ((packed));
struct acx_dco_itrim_params { struct acx_dco_itrim_params {
struct acx_header header; struct acx_header header;
@ -423,52 +415,6 @@ struct acx_dco_itrim_params {
__le32 timeout; __le32 timeout;
} __attribute__ ((packed)); } __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_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 padding[3];
} __attribute__ ((packed));
struct acx_energy_detection { struct acx_energy_detection {
struct acx_header header; struct acx_header header;
@ -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_opt(struct wl1271 *wl, bool enable_filter);
int wl1271_acx_beacon_filter_table(struct wl1271 *wl); int wl1271_acx_beacon_filter_table(struct wl1271 *wl);
int wl1271_acx_conn_monit_params(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_sg_cfg(struct wl1271 *wl);
int wl1271_acx_cca_threshold(struct wl1271 *wl); int wl1271_acx_cca_threshold(struct wl1271 *wl);
int wl1271_acx_bcn_dtim_options(struct wl1271 *wl); int wl1271_acx_bcn_dtim_options(struct wl1271 *wl);

View file

@ -228,6 +228,14 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
nvs_len = sizeof(wl->nvs->nvs); nvs_len = sizeof(wl->nvs->nvs);
nvs_ptr = (u8 *)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: * Layout before the actual NVS tables:
* 1 byte : burst length. * 1 byte : burst length.

View file

@ -26,6 +26,7 @@
#include <linux/crc7.h> #include <linux/crc7.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/ieee80211.h>
#include "wl1271.h" #include "wl1271.h"
#include "wl1271_reg.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->rx_filter_options = cpu_to_le32(wl->rx_filter);
join->bss_type = bss_type; 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) if (wl->band == IEEE80211_BAND_2GHZ)
join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS | join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS |
CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_2MBPS |
@ -546,9 +538,9 @@ int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
return ret; return ret;
} }
int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
u8 active_scan, u8 high_prio, u8 band, const u8 *ie, size_t ie_len, u8 active_scan,
u8 probe_requests) u8 high_prio, u8 band, u8 probe_requests)
{ {
struct wl1271_cmd_trigger_scan_to *trigger = NULL; 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; params->params.num_channels = j;
if (len && ssid) { if (ssid_len && ssid) {
params->params.ssid_len = len; params->params.ssid_len = ssid_len;
memcpy(params->params.ssid, 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) { if (ret < 0) {
wl1271_error("PROBE request template failed"); wl1271_error("PROBE request template failed");
goto out; 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.active = active_scan;
wl->scan.high_prio = high_prio; wl->scan.high_prio = high_prio;
wl->scan.probe_requests = probe_requests; wl->scan.probe_requests = probe_requests;
if (len && ssid) { if (ssid_len && ssid) {
wl->scan.ssid_len = len; wl->scan.ssid_len = ssid_len;
memcpy(wl->scan.ssid, ssid, len); memcpy(wl->scan.ssid, ssid, ssid_len);
} else } else
wl->scan.ssid_len = 0; wl->scan.ssid_len = 0;
} }
@ -714,155 +707,102 @@ int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
return ret; return ret;
} }
static int wl1271_build_basic_rates(u8 *rates, u8 band)
{
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);
}
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;
} else {
wl1271_error("build_basic_rates invalid band: %d", band);
}
return index;
}
int wl1271_cmd_build_null_data(struct wl1271 *wl) int wl1271_cmd_build_null_data(struct wl1271 *wl)
{ {
struct wl12xx_null_data_template template; struct sk_buff *skb = NULL;
int size;
void *ptr;
int ret = -ENOMEM;
if (!is_zero_ether_addr(wl->bssid)) {
memcpy(template.header.da, wl->bssid, ETH_ALEN); if (wl->bss_type == BSS_TYPE_IBSS) {
memcpy(template.header.bssid, wl->bssid, ETH_ALEN); size = sizeof(struct wl12xx_null_data_template);
ptr = NULL;
} else { } else {
memset(template.header.da, 0xff, ETH_ALEN); skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
memset(template.header.bssid, 0xff, ETH_ALEN); if (!skb)
goto out;
size = skb->len;
ptr = skb->data;
} }
memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size);
template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA |
IEEE80211_STYPE_NULLFUNC |
IEEE80211_FCTL_TODS);
return wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, &template, out:
sizeof(template)); dev_kfree_skb(skb);
if (ret)
wl1271_warning("cmd buld null data failed %d", ret);
return ret;
} }
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid) int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
{ {
struct wl12xx_ps_poll_template template; struct sk_buff *skb;
int ret = 0;
memcpy(template.bssid, wl->bssid, ETH_ALEN); skb = ieee80211_pspoll_get(wl->hw, wl->vif);
memcpy(template.ta, wl->mac_addr, ETH_ALEN); if (!skb)
goto out;
/* aid in PS-Poll has its two MSBs each set to 1 */ ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data,
template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); skb->len);
template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
return wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, &template,
sizeof(template));
out:
dev_kfree_skb(skb);
return ret;
} }
int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len, int wl1271_cmd_build_probe_req(struct wl1271 *wl,
u8 band) const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len, u8 band)
{ {
struct wl12xx_probe_req_template template; struct sk_buff *skb;
struct wl12xx_ie_rates *rates;
char *ptr;
u16 size;
int ret; int ret;
ptr = (char *)&template; skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
size = sizeof(struct ieee80211_header); ie, ie_len);
if (!skb) {
ret = -ENOMEM;
goto out;
}
memset(template.header.da, 0xff, ETH_ALEN); wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
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);
if (band == IEEE80211_BAND_2GHZ) if (band == IEEE80211_BAND_2GHZ)
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
&template, size); skb->data, skb->len);
else else
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, 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; 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) int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
{ {
struct wl1271_cmd_set_keys *cmd; struct wl1271_cmd_set_keys *cmd;

View file

@ -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_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send);
int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
size_t len); size_t len);
int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len, int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
u8 active_scan, u8 high_prio, u8 band, const u8 *ie, size_t ie_len, u8 active_scan,
u8 probe_requests); u8 high_prio, u8 band, u8 probe_requests);
int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
void *buf, size_t buf_len); void *buf, size_t buf_len);
int wl1271_cmd_build_null_data(struct wl1271 *wl); int wl1271_cmd_build_null_data(struct wl1271 *wl);
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid); 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, int wl1271_cmd_build_probe_req(struct wl1271 *wl,
u8 band); 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_default_wep_key(struct wl1271 *wl, u8 id);
int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type, int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
u8 key_size, const u8 *key, const u8 *addr, u8 key_size, const u8 *key, const u8 *addr,

View file

@ -65,110 +65,318 @@ enum {
CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS, CONF_HW_RATE_INDEX_MAX = CONF_HW_RATE_INDEX_54MBPS,
}; };
enum {
CONF_SG_DISABLE = 0,
CONF_SG_PROTECTIVE,
CONF_SG_OPPORTUNISTIC
};
enum {
/*
* PER threshold in PPM of the BT voice
*
* Range: 0 - 10000000
*/
CONF_SG_BT_PER_THRESHOLD = 0,
/*
* 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)
*/
CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR,
CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR,
/*
* The time after it expires no new WLAN trigger frame is trasmitted
* in WLAN PSM / BT slave basic rate
*
* Range: 0 - 255 (ms)
*/
CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR,
/*
* Configure the min and max time BT gains the antenna
* in WLAN PSM / BT master EDR
*
* Range: 0 - 255 (ms)
*/
CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR,
CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR,
/*
* The time after it expires no new WLAN trigger frame is trasmitted
* in WLAN PSM / BT master EDR
*
* Range: 0 - 255 (ms)
*/
CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR,
/*
* Configure the min and max time BT gains the antenna
* in WLAN PSM / BT slave EDR
*
* 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)
*/
CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR,
/*
* RX guard time before the beginning of a new BT voice frame during
* which no new WLAN trigger frame is transmitted.
*
* Range: 0 - 100000 (us)
*/
CONF_SG_RXT,
/*
* TX guard time before the beginning of a new BT voice frame during
* which no new WLAN frame is transmitted.
*
* Range: 0 - 100000 (us)
*/
CONF_SG_TXT,
/*
* Enable adaptive RXT/TXT algorithm. If disabled, the host values
* will be utilized.
*
* Range: 0 - disable, 1 - enable
*/
CONF_SG_ADAPTIVE_RXT_TXT,
/*
* The used WLAN legacy service period during active BT ACL link
*
* Range: 0 - 255 (ms)
*/
CONF_SG_PS_POLL_TIMEOUT,
/*
* The used WLAN UPSD service period during active BT ACL link
*
* Range: 0 - 255 (ms)
*/
CONF_SG_UPSD_TIMEOUT,
/*
* Configure the min and max time BT gains the antenna
* in WLAN Active / BT master EDR
*
* Range: 0 - 255 (ms)
*/
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 { struct conf_sg_settings {
/* __le32 params[CONF_SG_PARAMS_MAX];
* Defines the PER threshold in PPM of the BT voice of which reaching u8 state;
* 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.
*
* Unit: PER value in PPM (parts per million)
* #Error_packets / #Total_packets
* Range: u32
*/
u32 per_threshold;
/*
* This value is an absolute time in micro-seconds to limit the
* maximum scan duration compensation while in SG
*/
u32 max_scan_compensation_time;
/* 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.
*
* Unit: msec
* Range: 1-65000
*/
u16 nfs_sample_interval;
/*
* Defines the load ratio for the BT.
* The WLAN ratio is: 100 - load_ratio
*
* Unit: Percent
* Range: 0-100
*/
u8 load_ratio;
/*
* true - Co-ex is allowed to enter/exit P.S automatically and
* transparently to the host
*
* 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
*
*/
u8 auto_ps_mode;
/*
* This parameter defines the compensation percentage of num of probe
* requests in case scan is initiated during BT voice/BT ACL
* guaranteed link.
*
* Unit: Percent
* Range: 0-255 (0 - No compensation)
*/
u8 probe_req_compensation;
/*
* This parameter defines the compensation percentage of scan window
* size in case scan is initiated during BT voice/BT ACL Guaranteed
* link.
*
* Unit: Percent
* Range: 0-255 (0 - No compensation)
*/
u8 scan_window_compensation;
/*
* Defines the antenna configuration.
*
* Range: 0 - Single Antenna; 1 - Dual Antenna
*/
u8 antenna_config;
/*
* The percent out of the Max consecutive beacon miss roaming trigger
* which is the threshold for raising the priority of beacon
* reception.
*
* Range: 1-100
* N = MaxConsecutiveBeaconMiss
* P = coexMaxConsecutiveBeaconMissPrecent
* Threshold = MIN( N-1, round(N * P / 100))
*/
u8 beacon_miss_threshold;
/*
* 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.
*
* Range: HW_BIT_RATE_*
*/
u32 rate_adaptation_threshold;
/*
* The SNR above which the RX rate threshold indicating AP rate
* adaptation is valid
*
* Range: -128 - 127
*/
s8 rate_adaptation_snr;
}; };
enum conf_rx_queue_type { enum conf_rx_queue_type {

View file

@ -28,6 +28,7 @@
#include "wl1271.h" #include "wl1271.h"
#include "wl1271_acx.h" #include "wl1271_acx.h"
#include "wl1271_ps.h" #include "wl1271_ps.h"
#include "wl1271_io.h"
/* ms */ /* ms */
#define WL1271_DEBUGFS_STATS_LIFETIME 1000 #define WL1271_DEBUGFS_STATS_LIFETIME 1000
@ -276,13 +277,10 @@ static ssize_t gpio_power_write(struct file *file,
goto out; goto out;
} }
if (value) { if (value)
wl->set_power(true); wl1271_power_on(wl);
set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags); else
} else { wl1271_power_off(wl);
wl->set_power(false);
clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
}
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);

View file

@ -44,7 +44,9 @@ static int wl1271_event_scan_complete(struct wl1271 *wl,
* scanning as it checks that. * scanning as it checks that.
*/ */
clear_bit(WL1271_FLAG_SCANNING, &wl->flags); clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
/* FIXME: ie missing! */
wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len, wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len,
NULL, 0,
wl->scan.active, wl->scan.active,
wl->scan.high_prio, wl->scan.high_prio,
WL1271_SCAN_BAND_5_GHZ, WL1271_SCAN_BAND_5_GHZ,

View file

@ -160,11 +160,11 @@ int wl1271_init_pta(struct wl1271 *wl)
{ {
int ret; int ret;
ret = wl1271_acx_sg_enable(wl); ret = wl1271_acx_sg_cfg(wl);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = wl1271_acx_sg_cfg(wl); ret = wl1271_acx_sg_enable(wl, wl->sg_enabled);
if (ret < 0) if (ret < 0)
return ret; return ret;

View file

@ -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); 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 */ /* Top Register IO */
void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val); 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 */ /* Functions from wl1271_main.c */
int wl1271_register_hw(struct wl1271 *wl); int wl1271_register_hw(struct wl1271 *wl);
void wl1271_unregister_hw(struct wl1271 *wl);
int wl1271_init_ieee80211(struct wl1271 *wl); int wl1271_init_ieee80211(struct wl1271 *wl);
struct ieee80211_hw *wl1271_alloc_hw(void); struct ieee80211_hw *wl1271_alloc_hw(void);
int wl1271_free_hw(struct wl1271 *wl); int wl1271_free_hw(struct wl1271 *wl);

View file

@ -29,6 +29,7 @@
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/platform_device.h>
#include "wl1271.h" #include "wl1271.h"
#include "wl12xx_80211.h" #include "wl12xx_80211.h"
@ -48,17 +49,57 @@
static struct conf_drv_settings default_conf = { static struct conf_drv_settings default_conf = {
.sg = { .sg = {
.per_threshold = 7500, .params = {
.max_scan_compensation_time = 120000, [CONF_SG_BT_PER_THRESHOLD] = 7500,
.nfs_sample_interval = 400, [CONF_SG_HV3_MAX_OVERRIDE] = 0,
.load_ratio = 50, [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
.auto_ps_mode = 0, [CONF_SG_BT_LOAD_RATIO] = 50,
.probe_req_compensation = 170, [CONF_SG_AUTO_PS_MODE] = 0,
.scan_window_compensation = 50, [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
.antenna_config = 0, [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
.beacon_miss_threshold = 60, [CONF_SG_ANTENNA_CONFIGURATION] = 0,
.rate_adaptation_threshold = CONF_HW_BIT_RATE_12MBPS, [CONF_SG_BEACON_MISS_PERCENT] = 60,
.rate_adaptation_snr = 0 [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 = {
.rx_msdu_life_time = 512000, .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 LIST_HEAD(wl_list);
static void wl1271_conf_init(struct wl1271 *wl) static void wl1271_conf_init(struct wl1271 *wl)
@ -359,18 +415,6 @@ static int wl1271_plt_init(struct wl1271 *wl)
return ret; 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, static void wl1271_fw_status(struct wl1271 *wl,
struct wl1271_fw_status *status) struct wl1271_fw_status *status)
{ {
@ -526,40 +570,6 @@ static int wl1271_fetch_firmware(struct wl1271 *wl)
return ret; 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) static int wl1271_fetch_nvs(struct wl1271 *wl)
{ {
const struct firmware *fw; 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)); memcpy(wl->nvs, fw->data, sizeof(struct wl1271_nvs_file));
ret = wl1271_update_mac_addr(wl);
out: out:
release_firmware(fw); release_firmware(fw);
@ -907,14 +915,59 @@ static struct notifier_block wl1271_dev_notifier = {
static int wl1271_op_start(struct ieee80211_hw *hw) 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; struct wl1271 *wl = hw->priv;
int retries = WL1271_BOOT_RETRIES; int retries = WL1271_BOOT_RETRIES;
int ret = 0; 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); 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) { if (wl->state != WL1271_STATE_OFF) {
wl1271_error("cannot start because not in off state: %d", wl1271_error("cannot start because not in off state: %d",
@ -970,19 +1023,20 @@ static int wl1271_op_start(struct ieee80211_hw *hw)
return ret; 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; struct wl1271 *wl = hw->priv;
int i; int i;
wl1271_info("down");
wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
unregister_inetaddr_notifier(&wl1271_dev_notifier); unregister_inetaddr_notifier(&wl1271_dev_notifier);
list_del(&wl->list);
mutex_lock(&wl->mutex); 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); 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->rate_set = CONF_TX_RATE_MASK_BASIC;
wl->sta_rate_set = 0; wl->sta_rate_set = 0;
wl->flags = 0; wl->flags = 0;
wl->vif = NULL;
wl->filters = 0;
for (i = 0; i < NUM_TX_QUEUES; i++) for (i = 0; i < NUM_TX_QUEUES; i++)
wl->tx_blocks_freed[i] = 0; wl->tx_blocks_freed[i] = 0;
@ -1034,120 +1090,40 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
} }
static int wl1271_op_add_interface(struct ieee80211_hw *hw, static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
struct ieee80211_vif *vif)
{ {
struct wl1271 *wl = hw->priv; wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
int ret = 0; wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", /* combine requested filters with current filter config */
vif->type, vif->addr); filters = wl->filters | filters;
mutex_lock(&wl->mutex); wl1271_debug(DEBUG_FILTERS, "RX filters set: ");
if (wl->vif) {
ret = -EBUSY; if (filters & FIF_PROMISC_IN_BSS) {
goto out; wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS");
wl->rx_config &= ~CFG_UNI_FILTER_EN;
wl->rx_config |= CFG_BSSID_FILTER_EN;
} }
if (filters & FIF_BCN_PRBRESP_PROMISC) {
wl->vif = vif; wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC");
wl->rx_config &= ~CFG_BSSID_FILTER_EN;
switch (vif->type) { wl->rx_config &= ~CFG_SSID_FILTER_EN;
case NL80211_IFTYPE_STATION: }
wl->bss_type = BSS_TYPE_STA_BSS; if (filters & FIF_OTHER_BSS) {
break; wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS");
case NL80211_IFTYPE_ADHOC: wl->rx_config &= ~CFG_BSSID_FILTER_EN;
wl->bss_type = BSS_TYPE_IBSS; }
break; if (filters & FIF_CONTROL) {
default: wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL");
ret = -EOPNOTSUPP; wl->rx_filter |= CFG_RX_CTL_EN;
goto out; }
if (filters & FIF_FCSFAIL) {
wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL");
wl->rx_filter |= CFG_RX_FCS_ERROR;
} }
/* 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;
}
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;
}
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) static int wl1271_join_channel(struct wl1271 *wl, int channel)
{ {
int ret = 0; int ret = 0;
@ -1155,12 +1131,12 @@ static int wl1271_join_channel(struct wl1271 *wl, int channel)
static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde, static const u8 dummy_bssid[ETH_ALEN] = { 0x0b, 0xad, 0xde,
0xad, 0xbe, 0xef }; 0xad, 0xbe, 0xef };
/* disable mac filter, so we hear everything */
wl->rx_config &= ~CFG_BSSID_FILTER_EN;
wl->channel = channel; wl->channel = channel;
memcpy(wl->bssid, dummy_bssid, ETH_ALEN); 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 /* the dummy join is performed always with STATION BSS type to allow
also ad-hoc mode to listen to the surroundings without sending any also ad-hoc mode to listen to the surroundings without sending any
beacons yet. */ beacons yet. */
@ -1186,7 +1162,9 @@ static int wl1271_unjoin_channel(struct wl1271 *wl)
clear_bit(WL1271_FLAG_JOINED, &wl->flags); clear_bit(WL1271_FLAG_JOINED, &wl->flags);
wl->channel = 0; wl->channel = 0;
memset(wl->bssid, 0, ETH_ALEN); 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: out:
return ret; return ret;
@ -1359,14 +1337,14 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
if (ret < 0) if (ret < 0)
goto out_sleep; goto out_sleep;
kfree(fp);
/* FIXME: We still need to set our filters properly */
/* determine, whether supported filter values have changed */ /* determine, whether supported filter values have changed */
if (changed == 0) if (changed == 0)
goto out_sleep; goto out_sleep;
/* configure filters */
wl->filters = *total;
wl1271_configure_filters(wl, 0);
/* apply configured filters */ /* apply configured filters */
ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter); ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
if (ret < 0) if (ret < 0)
@ -1377,6 +1355,7 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
kfree(fp);
} }
static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, 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; goto out;
if (wl1271_11a_enabled()) 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); WL1271_SCAN_BAND_DUAL, 3);
else 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_SCAN_BAND_2_4_GHZ, 3);
wl1271_ps_elp_sleep(wl); 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 * and enable the BSSID filter
*/ */
memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) { memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
wl->rx_config |= CFG_BSSID_FILTER_EN;
memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
ret = wl1271_cmd_build_null_data(wl); ret = wl1271_cmd_build_null_data(wl);
if (ret < 0) { if (ret < 0)
wl1271_warning("cmd buld null data failed %d",
ret);
goto out_sleep; goto out_sleep;
}
/* filter out all packets not from this BSSID */
wl1271_configure_filters(wl, 0);
/* Need to update the BSSID (for filtering etc) */ /* Need to update the BSSID (for filtering etc) */
do_join = true; 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) const struct ieee80211_tx_queue_params *params)
{ {
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
u8 ps_scheme;
int ret; int ret;
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
@ -1745,17 +1727,22 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
if (ret < 0) if (ret < 0)
goto out; 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), ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
params->cw_min, params->cw_max, params->cw_min, params->cw_max,
params->aifs, params->txop); params->aifs, params->txop << 5);
if (ret < 0) if (ret < 0)
goto out_sleep; 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), ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
CONF_CHANNEL_TYPE_EDCF, CONF_CHANNEL_TYPE_EDCF,
wl1271_tx_get_queue(queue), wl1271_tx_get_queue(queue),
CONF_PS_SCHEME_LEGACY_PSPOLL, ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0);
CONF_ACK_POLICY_LEGACY, 0, 0);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out_sleep;
@ -1925,7 +1912,6 @@ static const struct ieee80211_ops wl1271_ops = {
.add_interface = wl1271_op_add_interface, .add_interface = wl1271_op_add_interface,
.remove_interface = wl1271_op_remove_interface, .remove_interface = wl1271_op_remove_interface,
.config = wl1271_op_config, .config = wl1271_op_config,
/* .config_interface = wl1271_op_config_interface, */
.prepare_multicast = wl1271_op_prepare_multicast, .prepare_multicast = wl1271_op_prepare_multicast,
.configure_filter = wl1271_op_configure_filter, .configure_filter = wl1271_op_configure_filter,
.tx = wl1271_op_tx, .tx = wl1271_op_tx,
@ -1937,6 +1923,68 @@ static const struct ieee80211_ops wl1271_ops = {
CFG80211_TESTMODE_CMD(wl1271_tm_cmd) 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 wl1271_register_hw(struct wl1271 *wl)
{ {
int ret; int ret;
@ -1960,6 +2008,14 @@ int wl1271_register_hw(struct wl1271 *wl)
} }
EXPORT_SYMBOL_GPL(wl1271_register_hw); 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) int wl1271_init_ieee80211(struct wl1271 *wl)
{ {
/* The tx descriptor buffer and the TKIP space. */ /* 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_NOISE_DBM |
IEEE80211_HW_BEACON_FILTER | IEEE80211_HW_BEACON_FILTER |
IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_PS |
IEEE80211_HW_SUPPORTS_UAPSD |
IEEE80211_HW_HAS_RATE_CONTROL; IEEE80211_HW_HAS_RATE_CONTROL;
wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
@ -1984,6 +2041,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
if (wl1271_11a_enabled()) if (wl1271_11a_enabled())
wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz; wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
wl->hw->queues = 4;
SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl)); SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
return 0; return 0;
@ -1995,21 +2054,34 @@ EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
struct ieee80211_hw *wl1271_alloc_hw(void) struct ieee80211_hw *wl1271_alloc_hw(void)
{ {
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct platform_device *plat_dev = NULL;
struct wl1271 *wl; 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); hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
if (!hw) { if (!hw) {
wl1271_error("could not alloc ieee80211_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; wl = hw->priv;
memset(wl, 0, sizeof(*wl)); memset(wl, 0, sizeof(*wl));
INIT_LIST_HEAD(&wl->list); INIT_LIST_HEAD(&wl->list);
wl->hw = hw; wl->hw = hw;
wl->plat_dev = plat_dev;
skb_queue_head_init(&wl->tx_queue); skb_queue_head_init(&wl->tx_queue);
@ -2027,6 +2099,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->band = IEEE80211_BAND_2GHZ; wl->band = IEEE80211_BAND_2GHZ;
wl->vif = NULL; wl->vif = NULL;
wl->flags = 0; wl->flags = 0;
wl->sg_enabled = true;
for (i = 0; i < ACX_TX_DESCRIPTORS; i++) for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
wl->tx_frames[i] = NULL; wl->tx_frames[i] = NULL;
@ -2036,18 +2109,55 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
wl->state = WL1271_STATE_OFF; wl->state = WL1271_STATE_OFF;
mutex_init(&wl->mutex); 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. */ /* Apply default driver configuration. */
wl1271_conf_init(wl); wl1271_conf_init(wl);
wl1271_debugfs_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; 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); EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
int wl1271_free_hw(struct wl1271 *wl) 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); wl1271_debugfs_exit(wl);

View file

@ -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, 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; int ret;
struct sdio_func *func = wl_to_func(wl); struct sdio_func *func = wl_to_func(wl);
sdio_claim_host(func);
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret); ((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]); addr, ((u8 *)buf)[0]);
} else { } else {
if (fixed) if (fixed)
@ -118,32 +117,30 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
else else
ret = sdio_memcpy_fromio(func, buf, addr, len); 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); addr, len);
wl1271_dump_ascii(DEBUG_SPI, "data: ", buf, len); wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
} }
if (ret) if (ret)
wl1271_error("sdio read failed (%d)", 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, 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; int ret;
struct sdio_func *func = wl_to_func(wl); struct sdio_func *func = wl_to_func(wl);
sdio_claim_host(func);
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) { if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret); 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]); addr, ((u8 *)buf)[0]);
} else { } 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); addr, len);
wl1271_dump_ascii(DEBUG_SPI, "data: ", buf, len); wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
if (fixed) if (fixed)
ret = sdio_writesb(func, addr, buf, len); 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) if (ret)
wl1271_error("sdio write failed (%d)", 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 = { static struct wl1271_if_operations sdio_ops = {
@ -161,15 +174,12 @@ static struct wl1271_if_operations sdio_ops = {
.write = wl1271_sdio_raw_write, .write = wl1271_sdio_raw_write,
.reset = wl1271_sdio_reset, .reset = wl1271_sdio_reset,
.init = wl1271_sdio_init, .init = wl1271_sdio_init,
.power = wl1271_sdio_set_power,
.dev = wl1271_sdio_wl_to_dev, .dev = wl1271_sdio_wl_to_dev,
.enable_irq = wl1271_sdio_enable_interrupts, .enable_irq = wl1271_sdio_enable_interrupts,
.disable_irq = wl1271_sdio_disable_interrupts .disable_irq = wl1271_sdio_disable_interrupts
}; };
static void wl1271_sdio_set_power(bool enable)
{
}
static int __devinit wl1271_probe(struct sdio_func *func, static int __devinit wl1271_probe(struct sdio_func *func,
const struct sdio_device_id *id) 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_priv = func;
wl->if_ops = &sdio_ops; wl->if_ops = &sdio_ops;
wl->set_power = wl1271_sdio_set_power;
/* Grab access to FN0 for ELP reg. */ /* Grab access to FN0 for ELP reg. */
func->card->quirks |= MMC_QUIRK_LENIENT_FN0; func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
@ -220,28 +228,18 @@ static int __devinit wl1271_probe(struct sdio_func *func,
if (ret) if (ret)
goto out_irq; goto out_irq;
sdio_claim_host(func);
sdio_set_drvdata(func, wl); sdio_set_drvdata(func, wl);
ret = sdio_enable_func(func);
if (ret)
goto out_release;
sdio_release_host(func);
wl1271_notice("initialized"); wl1271_notice("initialized");
return 0; return 0;
out_release:
sdio_release_host(func);
out_irq: out_irq:
free_irq(wl->irq, wl); free_irq(wl->irq, wl);
out_free: out_free:
ieee80211_free_hw(hw); wl1271_free_hw(wl);
return ret; return ret;
} }
@ -250,24 +248,10 @@ static void __devexit wl1271_remove(struct sdio_func *func)
{ {
struct wl1271 *wl = sdio_get_drvdata(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); free_irq(wl->irq, wl);
kfree(wl->target_mem_map); wl1271_unregister_hw(wl);
vfree(wl->fw); wl1271_free_hw(wl);
wl->fw = NULL;
kfree(wl->nvs);
wl->nvs = NULL;
kfree(wl->fw_status);
kfree(wl->tx_res_if);
ieee80211_free_hw(wl->hw);
} }
static struct sdio_driver wl1271_sdio_driver = { static struct sdio_driver wl1271_sdio_driver = {

View file

@ -23,7 +23,6 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/crc7.h> #include <linux/crc7.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/wl12xx.h> #include <linux/spi/wl12xx.h>
@ -332,26 +331,18 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
return IRQ_HANDLED; 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 = { static struct wl1271_if_operations spi_ops = {
.read = wl1271_spi_raw_read, .read = wl1271_spi_raw_read,
.write = wl1271_spi_raw_write, .write = wl1271_spi_raw_write,
.reset = wl1271_spi_reset, .reset = wl1271_spi_reset,
.init = wl1271_spi_init, .init = wl1271_spi_init,
.power = wl1271_spi_set_power,
.dev = wl1271_spi_wl_to_dev, .dev = wl1271_spi_wl_to_dev,
.enable_irq = wl1271_spi_enable_interrupts, .enable_irq = wl1271_spi_enable_interrupts,
.disable_irq = wl1271_spi_disable_interrupts .disable_irq = wl1271_spi_disable_interrupts
@ -415,33 +406,23 @@ static int __devinit wl1271_probe(struct spi_device *spi)
disable_irq(wl->irq); 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); ret = wl1271_init_ieee80211(wl);
if (ret) if (ret)
goto out_platform; goto out_irq;
ret = wl1271_register_hw(wl); ret = wl1271_register_hw(wl);
if (ret) if (ret)
goto out_platform; goto out_irq;
wl1271_notice("initialized"); wl1271_notice("initialized");
return 0; return 0;
out_platform:
platform_device_unregister(&wl1271_device);
out_irq: out_irq:
free_irq(wl->irq, wl); free_irq(wl->irq, wl);
out_free: out_free:
ieee80211_free_hw(hw); wl1271_free_hw(wl);
return ret; return ret;
} }
@ -450,9 +431,9 @@ static int __devexit wl1271_remove(struct spi_device *spi)
{ {
struct wl1271 *wl = dev_get_drvdata(&spi->dev); struct wl1271 *wl = dev_get_drvdata(&spi->dev);
platform_device_unregister(&wl1271_device);
free_irq(wl->irq, wl); free_irq(wl->irq, wl);
wl1271_unregister_hw(wl);
wl1271_free_hw(wl); wl1271_free_hw(wl);
return 0; return 0;

View file

@ -125,9 +125,6 @@ struct wl1271_tx_hw_res_if {
static inline int wl1271_tx_get_queue(int queue) static inline int wl1271_tx_get_queue(int queue)
{ {
/* FIXME: use best effort until WMM is enabled */
return CONF_TX_AC_BE;
switch (queue) { switch (queue) {
case 0: case 0:
return CONF_TX_AC_VO; return CONF_TX_AC_VO;

View file

@ -1834,32 +1834,32 @@ static int wl3501_get_power(struct net_device *dev,
} }
static const iw_handler wl3501_handler[] = { static const iw_handler wl3501_handler[] = {
[SIOCGIWNAME - SIOCIWFIRST] = wl3501_get_name, IW_HANDLER(SIOCGIWNAME, wl3501_get_name),
[SIOCSIWFREQ - SIOCIWFIRST] = wl3501_set_freq, IW_HANDLER(SIOCSIWFREQ, wl3501_set_freq),
[SIOCGIWFREQ - SIOCIWFIRST] = wl3501_get_freq, IW_HANDLER(SIOCGIWFREQ, wl3501_get_freq),
[SIOCSIWMODE - SIOCIWFIRST] = wl3501_set_mode, IW_HANDLER(SIOCSIWMODE, wl3501_set_mode),
[SIOCGIWMODE - SIOCIWFIRST] = wl3501_get_mode, IW_HANDLER(SIOCGIWMODE, wl3501_get_mode),
[SIOCGIWSENS - SIOCIWFIRST] = wl3501_get_sens, IW_HANDLER(SIOCGIWSENS, wl3501_get_sens),
[SIOCGIWRANGE - SIOCIWFIRST] = wl3501_get_range, IW_HANDLER(SIOCGIWRANGE, wl3501_get_range),
[SIOCSIWSPY - SIOCIWFIRST] = iw_handler_set_spy, IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
[SIOCGIWSPY - SIOCIWFIRST] = iw_handler_get_spy, IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
[SIOCSIWTHRSPY - SIOCIWFIRST] = iw_handler_set_thrspy, IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
[SIOCGIWTHRSPY - SIOCIWFIRST] = iw_handler_get_thrspy, IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
[SIOCSIWAP - SIOCIWFIRST] = wl3501_set_wap, IW_HANDLER(SIOCSIWAP, wl3501_set_wap),
[SIOCGIWAP - SIOCIWFIRST] = wl3501_get_wap, IW_HANDLER(SIOCGIWAP, wl3501_get_wap),
[SIOCSIWSCAN - SIOCIWFIRST] = wl3501_set_scan, IW_HANDLER(SIOCSIWSCAN, wl3501_set_scan),
[SIOCGIWSCAN - SIOCIWFIRST] = wl3501_get_scan, IW_HANDLER(SIOCGIWSCAN, wl3501_get_scan),
[SIOCSIWESSID - SIOCIWFIRST] = wl3501_set_essid, IW_HANDLER(SIOCSIWESSID, wl3501_set_essid),
[SIOCGIWESSID - SIOCIWFIRST] = wl3501_get_essid, IW_HANDLER(SIOCGIWESSID, wl3501_get_essid),
[SIOCSIWNICKN - SIOCIWFIRST] = wl3501_set_nick, IW_HANDLER(SIOCSIWNICKN, wl3501_set_nick),
[SIOCGIWNICKN - SIOCIWFIRST] = wl3501_get_nick, IW_HANDLER(SIOCGIWNICKN, wl3501_get_nick),
[SIOCGIWRATE - SIOCIWFIRST] = wl3501_get_rate, IW_HANDLER(SIOCGIWRATE, wl3501_get_rate),
[SIOCGIWRTS - SIOCIWFIRST] = wl3501_get_rts_threshold, IW_HANDLER(SIOCGIWRTS, wl3501_get_rts_threshold),
[SIOCGIWFRAG - SIOCIWFIRST] = wl3501_get_frag_threshold, IW_HANDLER(SIOCGIWFRAG, wl3501_get_frag_threshold),
[SIOCGIWTXPOW - SIOCIWFIRST] = wl3501_get_txpow, IW_HANDLER(SIOCGIWTXPOW, wl3501_get_txpow),
[SIOCGIWRETRY - SIOCIWFIRST] = wl3501_get_retry, IW_HANDLER(SIOCGIWRETRY, wl3501_get_retry),
[SIOCGIWENCODE - SIOCIWFIRST] = wl3501_get_encode, IW_HANDLER(SIOCGIWENCODE, wl3501_get_encode),
[SIOCGIWPOWER - SIOCIWFIRST] = wl3501_get_power, IW_HANDLER(SIOCGIWPOWER, wl3501_get_power),
}; };
static const struct iw_handler_def wl3501_handler_def = { static const struct iw_handler_def wl3501_handler_def = {

View file

@ -323,6 +323,12 @@
* the TX command and %NL80211_ATTR_FRAME includes the contents of the * the TX command and %NL80211_ATTR_FRAME includes the contents of the
* frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
* the frame. * 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_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use * @__NL80211_CMD_AFTER_LAST: internal use
@ -419,6 +425,9 @@ enum nl80211_commands {
NL80211_CMD_SET_POWER_SAVE, NL80211_CMD_SET_POWER_SAVE,
NL80211_CMD_GET_POWER_SAVE, NL80211_CMD_GET_POWER_SAVE,
NL80211_CMD_SET_CQM,
NL80211_CMD_NOTIFY_CQM,
/* add new commands above here */ /* add new commands above here */
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
@ -691,6 +700,9 @@ enum nl80211_commands {
* @NL80211_ATTR_ACK: Flag attribute indicating that the frame was * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
* acknowledged by the recipient. * 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_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
@ -842,6 +854,8 @@ enum nl80211_attrs {
NL80211_ATTR_PS_STATE, NL80211_ATTR_PS_STATE,
NL80211_ATTR_CQM,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
@ -1583,4 +1597,40 @@ enum nl80211_ps_state {
NL80211_PS_ENABLED, 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 */ #endif /* __LINUX_NL80211_H */

View file

@ -346,6 +346,8 @@
#define SIOCIWFIRST 0x8B00 #define SIOCIWFIRST 0x8B00
#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */ #define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */
#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST) #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) */ /* Odd : get (world access), even : set (root access) */
#define IW_IS_SET(cmd) (!((cmd) & 0x1)) #define IW_IS_SET(cmd) (!((cmd) & 0x1))
@ -648,7 +650,7 @@
* 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ #define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \
(cmd - SIOCIWFIRSTPRIV + 0x60) : \ (cmd - SIOCIWFIRSTPRIV + 0x60) : \
(cmd - SIOCSIWCOMMIT)) (cmd - SIOCIWFIRST))
#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) #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)) #define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F))
/* Event capability constants - event autogenerated by the kernel /* Event capability constants - event autogenerated by the kernel

View file

@ -1007,6 +1007,7 @@ struct cfg80211_pmksa {
* RSN IE. It allows for faster roaming between WPA2 BSSIDs. * RSN IE. It allows for faster roaming between WPA2 BSSIDs.
* @del_pmksa: Delete a cached PMKID. * @del_pmksa: Delete a cached PMKID.
* @flush_pmksa: Flush all cached PMKIDs. * @flush_pmksa: Flush all cached PMKIDs.
* @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
* *
*/ */
struct cfg80211_ops { struct cfg80211_ops {
@ -1152,6 +1153,10 @@ struct cfg80211_ops {
int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout); 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, void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
const u8 *buf, size_t len, bool ack, gfp_t gfp); 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 */ #endif /* __NET_CFG80211_H */

View file

@ -144,6 +144,7 @@ struct ieee80211_low_level_stats {
* new beacon (beaconing modes) * new beacon (beaconing modes)
* @BSS_CHANGED_BEACON_ENABLED: Beaconing should be * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be
* enabled/disabled (beaconing modes) * enabled/disabled (beaconing modes)
* @BSS_CHANGED_CQM: Connection quality monitor config changed
*/ */
enum ieee80211_bss_change { enum ieee80211_bss_change {
BSS_CHANGED_ASSOC = 1<<0, BSS_CHANGED_ASSOC = 1<<0,
@ -156,6 +157,7 @@ enum ieee80211_bss_change {
BSS_CHANGED_BSSID = 1<<7, BSS_CHANGED_BSSID = 1<<7,
BSS_CHANGED_BEACON = 1<<8, BSS_CHANGED_BEACON = 1<<8,
BSS_CHANGED_BEACON_ENABLED = 1<<9, 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 * @enable_beacon: whether beaconing should be enabled or not
* @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info). * @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. * 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 { struct ieee80211_bss_conf {
const u8 *bssid; const u8 *bssid;
@ -202,6 +207,8 @@ struct ieee80211_bss_conf {
u64 timestamp; u64 timestamp;
u32 basic_rates; u32 basic_rates;
u16 ht_operation_mode; 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 * Hardware can provide ack status reports of Tx frames to
* the stack. * 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 { enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, 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_DYNAMIC_SMPS = 1<<16,
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, 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. * @vif: &struct ieee80211_vif pointer from the add_interface callback.
* *
* When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING and
* IEEE80211_CONF_PS is set, the driver needs to inform whenever the * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the
* hardware is not receiving beacons with this function. * hardware is not receiving beacons with this function.
*/ */
void ieee80211_beacon_loss(struct ieee80211_vif *vif); 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 */ /* Rate control API */
/** /**

View file

@ -1402,6 +1402,32 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
return 0; 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, static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
struct net_device *dev, struct net_device *dev,
const u8 *addr, const u8 *addr,
@ -1506,4 +1532,5 @@ struct cfg80211_ops mac80211_config_ops = {
.remain_on_channel = ieee80211_remain_on_channel, .remain_on_channel = ieee80211_remain_on_channel,
.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel, .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
.action = ieee80211_action, .action = ieee80211_action,
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
}; };

View file

@ -327,7 +327,7 @@ struct ieee80211_if_managed {
struct work_struct work; struct work_struct work;
struct work_struct monitor_work; struct work_struct monitor_work;
struct work_struct chswitch_work; struct work_struct chswitch_work;
struct work_struct beacon_loss_work; struct work_struct beacon_connection_loss_work;
unsigned long probe_timeout; unsigned long probe_timeout;
int probe_send_count; int probe_send_count;
@ -1156,7 +1156,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
int powersave); int powersave);
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
struct ieee80211_hdr *hdr); 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, void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
enum queue_stop_reason reason); enum queue_stop_reason reason);

View file

@ -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.work);
cancel_work_sync(&sdata->u.mgd.chswitch_work); cancel_work_sync(&sdata->u.mgd.chswitch_work);
cancel_work_sync(&sdata->u.mgd.monitor_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. * When we get here, the interface is marked down.

View file

@ -753,6 +753,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
/* And the BSSID changed - we're associated now */ /* And the BSSID changed - we're associated now */
bss_info_changed |= BSS_CHANGED_BSSID; 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); ieee80211_bss_info_change_notify(sdata, bss_info_changed);
mutex_lock(&local->iflist_mtx); 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)) if (is_multicast_ether_addr(hdr->addr1))
return; return;
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
return;
mod_timer(&sdata->u.mgd.conn_mon_timer, mod_timer(&sdata->u.mgd.conn_mon_timer,
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); 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); 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 = struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data, 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) void ieee80211_beacon_loss(struct ieee80211_vif *vif)
{ {
struct ieee80211_sub_if_data *sdata = vif_to_sdata(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); 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 static enum rx_mgmt_action __must_check
ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len) struct ieee80211_mgmt *mgmt, size_t len)
@ -1637,7 +1690,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
if (local->quiescing) if (local->quiescing)
return; 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) 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->work);
cancel_work_sync(&ifmgd->beacon_loss_work); cancel_work_sync(&ifmgd->beacon_connection_loss_work);
if (del_timer_sync(&ifmgd->timer)) if (del_timer_sync(&ifmgd->timer))
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); 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->work, ieee80211_sta_work);
INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_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, setup_timer(&ifmgd->timer, ieee80211_sta_timer,
(unsigned long) sdata); (unsigned long) sdata);
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, 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; *cookie = (unsigned long) skb;
return 0; 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);

View file

@ -2010,14 +2010,12 @@ void ieee80211_tx_pending(unsigned long data)
while (!skb_queue_empty(&local->pending[i])) { while (!skb_queue_empty(&local->pending[i])) {
struct sk_buff *skb = __skb_dequeue(&local->pending[i]); struct sk_buff *skb = __skb_dequeue(&local->pending[i]);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_sub_if_data *sdata;
if (WARN_ON(!info->control.vif)) { if (WARN_ON(!info->control.vif)) {
kfree_skb(skb); kfree_skb(skb);
continue; continue;
} }
sdata = vif_to_sdata(info->control.vif);
spin_unlock_irqrestore(&local->queue_stop_reason_lock, spin_unlock_irqrestore(&local->queue_stop_reason_lock,
flags); flags);

View file

@ -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); nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
} }
EXPORT_SYMBOL(cfg80211_action_tx_status); 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);

View file

@ -149,6 +149,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
.len = IEEE80211_MAX_DATA_LEN }, .len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
[NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
[NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
}; };
/* policy for the attributes */ /* policy for the attributes */
@ -4778,6 +4779,84 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
return err; 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[] = { static struct genl_ops nl80211_ops[] = {
{ {
.cmd = NL80211_CMD_GET_WIPHY, .cmd = NL80211_CMD_GET_WIPHY,
@ -5082,6 +5161,12 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy, .policy = nl80211_policy,
/* can be retrieved by unprivileged users */ /* 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 = { 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); 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, static int nl80211_netlink_notify(struct notifier_block * nb,
unsigned long state, unsigned long state,
void *_notify) void *_notify)

View file

@ -82,4 +82,10 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
const u8 *buf, size_t len, bool ack, const u8 *buf, size_t len, bool ack,
gfp_t gfp); 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 */ #endif /* __NET_WIRELESS_NL80211_H */

View file

@ -28,226 +28,226 @@ typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
* know about. * know about.
*/ */
static const struct iw_ioctl_description standard_ioctl[] = { static const struct iw_ioctl_description standard_ioctl[] = {
[SIOCSIWCOMMIT - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWCOMMIT)] = {
.header_type = IW_HEADER_TYPE_NULL, .header_type = IW_HEADER_TYPE_NULL,
}, },
[SIOCGIWNAME - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWNAME)] = {
.header_type = IW_HEADER_TYPE_CHAR, .header_type = IW_HEADER_TYPE_CHAR,
.flags = IW_DESCR_FLAG_DUMP, .flags = IW_DESCR_FLAG_DUMP,
}, },
[SIOCSIWNWID - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWNWID)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
.flags = IW_DESCR_FLAG_EVENT, .flags = IW_DESCR_FLAG_EVENT,
}, },
[SIOCGIWNWID - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWNWID)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
.flags = IW_DESCR_FLAG_DUMP, .flags = IW_DESCR_FLAG_DUMP,
}, },
[SIOCSIWFREQ - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWFREQ)] = {
.header_type = IW_HEADER_TYPE_FREQ, .header_type = IW_HEADER_TYPE_FREQ,
.flags = IW_DESCR_FLAG_EVENT, .flags = IW_DESCR_FLAG_EVENT,
}, },
[SIOCGIWFREQ - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWFREQ)] = {
.header_type = IW_HEADER_TYPE_FREQ, .header_type = IW_HEADER_TYPE_FREQ,
.flags = IW_DESCR_FLAG_DUMP, .flags = IW_DESCR_FLAG_DUMP,
}, },
[SIOCSIWMODE - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWMODE)] = {
.header_type = IW_HEADER_TYPE_UINT, .header_type = IW_HEADER_TYPE_UINT,
.flags = IW_DESCR_FLAG_EVENT, .flags = IW_DESCR_FLAG_EVENT,
}, },
[SIOCGIWMODE - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWMODE)] = {
.header_type = IW_HEADER_TYPE_UINT, .header_type = IW_HEADER_TYPE_UINT,
.flags = IW_DESCR_FLAG_DUMP, .flags = IW_DESCR_FLAG_DUMP,
}, },
[SIOCSIWSENS - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWSENS)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCGIWSENS - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWSENS)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCSIWRANGE - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWRANGE)] = {
.header_type = IW_HEADER_TYPE_NULL, .header_type = IW_HEADER_TYPE_NULL,
}, },
[SIOCGIWRANGE - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWRANGE)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = sizeof(struct iw_range), .max_tokens = sizeof(struct iw_range),
.flags = IW_DESCR_FLAG_DUMP, .flags = IW_DESCR_FLAG_DUMP,
}, },
[SIOCSIWPRIV - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWPRIV)] = {
.header_type = IW_HEADER_TYPE_NULL, .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, .header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct iw_priv_args), .token_size = sizeof(struct iw_priv_args),
.max_tokens = 16, .max_tokens = 16,
.flags = IW_DESCR_FLAG_NOMAX, .flags = IW_DESCR_FLAG_NOMAX,
}, },
[SIOCSIWSTATS - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWSTATS)] = {
.header_type = IW_HEADER_TYPE_NULL, .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, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = sizeof(struct iw_statistics), .max_tokens = sizeof(struct iw_statistics),
.flags = IW_DESCR_FLAG_DUMP, .flags = IW_DESCR_FLAG_DUMP,
}, },
[SIOCSIWSPY - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWSPY)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct sockaddr), .token_size = sizeof(struct sockaddr),
.max_tokens = IW_MAX_SPY, .max_tokens = IW_MAX_SPY,
}, },
[SIOCGIWSPY - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWSPY)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct sockaddr) + .token_size = sizeof(struct sockaddr) +
sizeof(struct iw_quality), sizeof(struct iw_quality),
.max_tokens = IW_MAX_SPY, .max_tokens = IW_MAX_SPY,
}, },
[SIOCSIWTHRSPY - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWTHRSPY)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct iw_thrspy), .token_size = sizeof(struct iw_thrspy),
.min_tokens = 1, .min_tokens = 1,
.max_tokens = 1, .max_tokens = 1,
}, },
[SIOCGIWTHRSPY - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWTHRSPY)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct iw_thrspy), .token_size = sizeof(struct iw_thrspy),
.min_tokens = 1, .min_tokens = 1,
.max_tokens = 1, .max_tokens = 1,
}, },
[SIOCSIWAP - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWAP)] = {
.header_type = IW_HEADER_TYPE_ADDR, .header_type = IW_HEADER_TYPE_ADDR,
}, },
[SIOCGIWAP - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWAP)] = {
.header_type = IW_HEADER_TYPE_ADDR, .header_type = IW_HEADER_TYPE_ADDR,
.flags = IW_DESCR_FLAG_DUMP, .flags = IW_DESCR_FLAG_DUMP,
}, },
[SIOCSIWMLME - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWMLME)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.min_tokens = sizeof(struct iw_mlme), .min_tokens = sizeof(struct iw_mlme),
.max_tokens = sizeof(struct iw_mlme), .max_tokens = sizeof(struct iw_mlme),
}, },
[SIOCGIWAPLIST - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWAPLIST)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = sizeof(struct sockaddr) + .token_size = sizeof(struct sockaddr) +
sizeof(struct iw_quality), sizeof(struct iw_quality),
.max_tokens = IW_MAX_AP, .max_tokens = IW_MAX_AP,
.flags = IW_DESCR_FLAG_NOMAX, .flags = IW_DESCR_FLAG_NOMAX,
}, },
[SIOCSIWSCAN - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWSCAN)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.min_tokens = 0, .min_tokens = 0,
.max_tokens = sizeof(struct iw_scan_req), .max_tokens = sizeof(struct iw_scan_req),
}, },
[SIOCGIWSCAN - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWSCAN)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_SCAN_MAX_DATA, .max_tokens = IW_SCAN_MAX_DATA,
.flags = IW_DESCR_FLAG_NOMAX, .flags = IW_DESCR_FLAG_NOMAX,
}, },
[SIOCSIWESSID - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWESSID)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_ESSID_MAX_SIZE, .max_tokens = IW_ESSID_MAX_SIZE,
.flags = IW_DESCR_FLAG_EVENT, .flags = IW_DESCR_FLAG_EVENT,
}, },
[SIOCGIWESSID - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWESSID)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_ESSID_MAX_SIZE, .max_tokens = IW_ESSID_MAX_SIZE,
.flags = IW_DESCR_FLAG_DUMP, .flags = IW_DESCR_FLAG_DUMP,
}, },
[SIOCSIWNICKN - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWNICKN)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_ESSID_MAX_SIZE, .max_tokens = IW_ESSID_MAX_SIZE,
}, },
[SIOCGIWNICKN - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWNICKN)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_ESSID_MAX_SIZE, .max_tokens = IW_ESSID_MAX_SIZE,
}, },
[SIOCSIWRATE - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWRATE)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCGIWRATE - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWRATE)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCSIWRTS - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWRTS)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCGIWRTS - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWRTS)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCSIWFRAG - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWFRAG)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCGIWFRAG - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWFRAG)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCSIWTXPOW - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWTXPOW)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCGIWTXPOW - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWTXPOW)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCSIWRETRY - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWRETRY)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCGIWRETRY - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWRETRY)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCSIWENCODE - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWENCODE)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_ENCODING_TOKEN_MAX, .max_tokens = IW_ENCODING_TOKEN_MAX,
.flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT, .flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
}, },
[SIOCGIWENCODE - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWENCODE)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_ENCODING_TOKEN_MAX, .max_tokens = IW_ENCODING_TOKEN_MAX,
.flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT, .flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
}, },
[SIOCSIWPOWER - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWPOWER)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCGIWPOWER - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWPOWER)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCSIWGENIE - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWGENIE)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_GENERIC_IE_MAX, .max_tokens = IW_GENERIC_IE_MAX,
}, },
[SIOCGIWGENIE - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWGENIE)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_GENERIC_IE_MAX, .max_tokens = IW_GENERIC_IE_MAX,
}, },
[SIOCSIWAUTH - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWAUTH)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCGIWAUTH - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWAUTH)] = {
.header_type = IW_HEADER_TYPE_PARAM, .header_type = IW_HEADER_TYPE_PARAM,
}, },
[SIOCSIWENCODEEXT - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.min_tokens = sizeof(struct iw_encode_ext), .min_tokens = sizeof(struct iw_encode_ext),
.max_tokens = sizeof(struct iw_encode_ext) + .max_tokens = sizeof(struct iw_encode_ext) +
IW_ENCODING_TOKEN_MAX, IW_ENCODING_TOKEN_MAX,
}, },
[SIOCGIWENCODEEXT - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.min_tokens = sizeof(struct iw_encode_ext), .min_tokens = sizeof(struct iw_encode_ext),
.max_tokens = sizeof(struct iw_encode_ext) + .max_tokens = sizeof(struct iw_encode_ext) +
IW_ENCODING_TOKEN_MAX, IW_ENCODING_TOKEN_MAX,
}, },
[SIOCSIWPMKSA - SIOCIWFIRST] = { [IW_IOCTL_IDX(SIOCSIWPMKSA)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.min_tokens = sizeof(struct iw_pmksa), .min_tokens = sizeof(struct iw_pmksa),
@ -261,44 +261,44 @@ static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
* we know about. * we know about.
*/ */
static const struct iw_ioctl_description standard_event[] = { static const struct iw_ioctl_description standard_event[] = {
[IWEVTXDROP - IWEVFIRST] = { [IW_EVENT_IDX(IWEVTXDROP)] = {
.header_type = IW_HEADER_TYPE_ADDR, .header_type = IW_HEADER_TYPE_ADDR,
}, },
[IWEVQUAL - IWEVFIRST] = { [IW_EVENT_IDX(IWEVQUAL)] = {
.header_type = IW_HEADER_TYPE_QUAL, .header_type = IW_HEADER_TYPE_QUAL,
}, },
[IWEVCUSTOM - IWEVFIRST] = { [IW_EVENT_IDX(IWEVCUSTOM)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_CUSTOM_MAX, .max_tokens = IW_CUSTOM_MAX,
}, },
[IWEVREGISTERED - IWEVFIRST] = { [IW_EVENT_IDX(IWEVREGISTERED)] = {
.header_type = IW_HEADER_TYPE_ADDR, .header_type = IW_HEADER_TYPE_ADDR,
}, },
[IWEVEXPIRED - IWEVFIRST] = { [IW_EVENT_IDX(IWEVEXPIRED)] = {
.header_type = IW_HEADER_TYPE_ADDR, .header_type = IW_HEADER_TYPE_ADDR,
}, },
[IWEVGENIE - IWEVFIRST] = { [IW_EVENT_IDX(IWEVGENIE)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_GENERIC_IE_MAX, .max_tokens = IW_GENERIC_IE_MAX,
}, },
[IWEVMICHAELMICFAILURE - IWEVFIRST] = { [IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = sizeof(struct iw_michaelmicfailure), .max_tokens = sizeof(struct iw_michaelmicfailure),
}, },
[IWEVASSOCREQIE - IWEVFIRST] = { [IW_EVENT_IDX(IWEVASSOCREQIE)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_GENERIC_IE_MAX, .max_tokens = IW_GENERIC_IE_MAX,
}, },
[IWEVASSOCRESPIE - IWEVFIRST] = { [IW_EVENT_IDX(IWEVASSOCRESPIE)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = IW_GENERIC_IE_MAX, .max_tokens = IW_GENERIC_IE_MAX,
}, },
[IWEVPMKIDCAND - IWEVFIRST] = { [IW_EVENT_IDX(IWEVPMKIDCAND)] = {
.header_type = IW_HEADER_TYPE_POINT, .header_type = IW_HEADER_TYPE_POINT,
.token_size = 1, .token_size = 1,
.max_tokens = sizeof(struct iw_pmkid_cand), .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 */ /* Get the description of the Event */
if (cmd <= SIOCIWLAST) { if (cmd <= SIOCIWLAST) {
cmd_index = cmd - SIOCIWFIRST; cmd_index = IW_IOCTL_IDX(cmd);
if (cmd_index < standard_ioctl_num) if (cmd_index < standard_ioctl_num)
descr = &(standard_ioctl[cmd_index]); descr = &(standard_ioctl[cmd_index]);
} else { } else {
cmd_index = cmd - IWEVFIRST; cmd_index = IW_EVENT_IDX(cmd);
if (cmd_index < standard_event_num) if (cmd_index < standard_event_num)
descr = &(standard_event[cmd_index]); descr = &(standard_event[cmd_index]);
} }
@ -662,7 +662,7 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
return NULL; return NULL;
/* Try as a standard command */ /* Try as a standard command */
index = cmd - SIOCIWFIRST; index = IW_IOCTL_IDX(cmd);
if (index < handlers->num_standard) if (index < handlers->num_standard)
return handlers->standard[index]; return handlers->standard[index];
@ -954,9 +954,9 @@ static int ioctl_standard_call(struct net_device * dev,
int ret = -EINVAL; int ret = -EINVAL;
/* Get the description of the IOCTL */ /* Get the description of the IOCTL */
if ((cmd - SIOCIWFIRST) >= standard_ioctl_num) if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num)
return -EOPNOTSUPP; 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 */ /* Check if we have a pointer to user space data or not */
if (descr->header_type != IW_HEADER_TYPE_POINT) { 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; struct iw_point iwp;
int err; int err;
descr = standard_ioctl + (cmd - SIOCIWFIRST); descr = standard_ioctl + IW_IOCTL_IDX(cmd);
if (descr->header_type != IW_HEADER_TYPE_POINT) if (descr->header_type != IW_HEADER_TYPE_POINT)
return ioctl_standard_call(dev, iwr, cmd, info, handler); return ioctl_standard_call(dev, iwr, cmd, info, handler);