Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
This commit is contained in:
Коммит
7905e357eb
|
@ -2279,26 +2279,25 @@ void gelic_wl_interrupt(struct net_device *netdev, u64 status)
|
|||
/*
|
||||
* driver helpers
|
||||
*/
|
||||
#define IW_IOCTL(n) [(n) - SIOCSIWCOMMIT]
|
||||
static const iw_handler gelic_wl_wext_handler[] =
|
||||
{
|
||||
IW_IOCTL(SIOCGIWNAME) = gelic_wl_get_name,
|
||||
IW_IOCTL(SIOCGIWRANGE) = gelic_wl_get_range,
|
||||
IW_IOCTL(SIOCSIWSCAN) = gelic_wl_set_scan,
|
||||
IW_IOCTL(SIOCGIWSCAN) = gelic_wl_get_scan,
|
||||
IW_IOCTL(SIOCSIWAUTH) = gelic_wl_set_auth,
|
||||
IW_IOCTL(SIOCGIWAUTH) = gelic_wl_get_auth,
|
||||
IW_IOCTL(SIOCSIWESSID) = gelic_wl_set_essid,
|
||||
IW_IOCTL(SIOCGIWESSID) = gelic_wl_get_essid,
|
||||
IW_IOCTL(SIOCSIWENCODE) = gelic_wl_set_encode,
|
||||
IW_IOCTL(SIOCGIWENCODE) = gelic_wl_get_encode,
|
||||
IW_IOCTL(SIOCSIWAP) = gelic_wl_set_ap,
|
||||
IW_IOCTL(SIOCGIWAP) = gelic_wl_get_ap,
|
||||
IW_IOCTL(SIOCSIWENCODEEXT) = gelic_wl_set_encodeext,
|
||||
IW_IOCTL(SIOCGIWENCODEEXT) = gelic_wl_get_encodeext,
|
||||
IW_IOCTL(SIOCSIWMODE) = gelic_wl_set_mode,
|
||||
IW_IOCTL(SIOCGIWMODE) = gelic_wl_get_mode,
|
||||
IW_IOCTL(SIOCGIWNICKN) = gelic_wl_get_nick,
|
||||
IW_HANDLER(SIOCGIWNAME, gelic_wl_get_name),
|
||||
IW_HANDLER(SIOCGIWRANGE, gelic_wl_get_range),
|
||||
IW_HANDLER(SIOCSIWSCAN, gelic_wl_set_scan),
|
||||
IW_HANDLER(SIOCGIWSCAN, gelic_wl_get_scan),
|
||||
IW_HANDLER(SIOCSIWAUTH, gelic_wl_set_auth),
|
||||
IW_HANDLER(SIOCGIWAUTH, gelic_wl_get_auth),
|
||||
IW_HANDLER(SIOCSIWESSID, gelic_wl_set_essid),
|
||||
IW_HANDLER(SIOCGIWESSID, gelic_wl_get_essid),
|
||||
IW_HANDLER(SIOCSIWENCODE, gelic_wl_set_encode),
|
||||
IW_HANDLER(SIOCGIWENCODE, gelic_wl_get_encode),
|
||||
IW_HANDLER(SIOCSIWAP, gelic_wl_set_ap),
|
||||
IW_HANDLER(SIOCGIWAP, gelic_wl_get_ap),
|
||||
IW_HANDLER(SIOCSIWENCODEEXT, gelic_wl_set_encodeext),
|
||||
IW_HANDLER(SIOCGIWENCODEEXT, gelic_wl_get_encodeext),
|
||||
IW_HANDLER(SIOCSIWMODE, gelic_wl_set_mode),
|
||||
IW_HANDLER(SIOCGIWMODE, gelic_wl_get_mode),
|
||||
IW_HANDLER(SIOCGIWNICKN, gelic_wl_get_nick),
|
||||
};
|
||||
|
||||
static const struct iw_handler_def gelic_wl_wext_handler_def = {
|
||||
|
|
|
@ -3,7 +3,7 @@ menuconfig ATH_COMMON
|
|||
depends on CFG80211
|
||||
---help---
|
||||
This will enable the support for the Atheros wireless drivers.
|
||||
ath5k, ath9k and ar9170 drivers share some common code, this option
|
||||
ath5k, ath9k, ath9k_htc and ar9170 drivers share some common code, this option
|
||||
enables the common ath.ko module which shares common helpers.
|
||||
|
||||
For more information and documentation on this module you can visit:
|
||||
|
|
|
@ -32,3 +32,24 @@ config ATH9K_DEBUGFS
|
|||
|
||||
Also required for changing debug message flags at run time.
|
||||
|
||||
config ATH9K_HTC
|
||||
tristate "Atheros HTC based wireless cards support"
|
||||
depends on USB && MAC80211
|
||||
select ATH9K_HW
|
||||
select MAC80211_LEDS
|
||||
select LEDS_CLASS
|
||||
select NEW_LEDS
|
||||
select ATH9K_COMMON
|
||||
---help---
|
||||
Support for Atheros HTC based cards.
|
||||
Chipsets supported: AR9271
|
||||
|
||||
For more information: http://wireless.kernel.org/en/users/Drivers/ath9k_htc
|
||||
|
||||
The built module will be ath9k_htc.
|
||||
|
||||
config ATH9K_HTC_DEBUGFS
|
||||
bool "Atheros ath9k_htc debugging"
|
||||
depends on ATH9K_HTC && DEBUG_FS
|
||||
---help---
|
||||
Say Y, if you need access to ath9k_htc's statistics.
|
||||
|
|
|
@ -28,3 +28,13 @@ obj-$(CONFIG_ATH9K_HW) += ath9k_hw.o
|
|||
|
||||
obj-$(CONFIG_ATH9K_COMMON) += ath9k_common.o
|
||||
ath9k_common-y:= common.o
|
||||
|
||||
ath9k_htc-y += htc_hst.o \
|
||||
hif_usb.o \
|
||||
wmi.o \
|
||||
htc_drv_txrx.o \
|
||||
htc_drv_main.o \
|
||||
htc_drv_beacon.o \
|
||||
htc_drv_init.o
|
||||
|
||||
obj-$(CONFIG_ATH9K_HTC) += ath9k_htc.o
|
||||
|
|
|
@ -101,9 +101,13 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
|
|||
nf = 0 - ((nf ^ 0x1ff) + 1);
|
||||
ath_print(common, ATH_DBG_CALIBRATE,
|
||||
"NF calibrated [ctl] [chain 0] is %d\n", nf);
|
||||
|
||||
if (AR_SREV_9271(ah) && (nf >= -114))
|
||||
nf = -116;
|
||||
|
||||
nfarray[0] = nf;
|
||||
|
||||
if (!AR_SREV_9285(ah)) {
|
||||
if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) {
|
||||
if (AR_SREV_9280_10_OR_LATER(ah))
|
||||
nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
|
||||
AR9280_PHY_CH1_MINCCA_PWR);
|
||||
|
@ -139,9 +143,13 @@ static void ath9k_hw_do_getnf(struct ath_hw *ah,
|
|||
nf = 0 - ((nf ^ 0x1ff) + 1);
|
||||
ath_print(common, ATH_DBG_CALIBRATE,
|
||||
"NF calibrated [ext] [chain 0] is %d\n", nf);
|
||||
|
||||
if (AR_SREV_9271(ah) && (nf >= -114))
|
||||
nf = -116;
|
||||
|
||||
nfarray[3] = nf;
|
||||
|
||||
if (!AR_SREV_9285(ah)) {
|
||||
if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) {
|
||||
if (AR_SREV_9280_10_OR_LATER(ah))
|
||||
nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA),
|
||||
AR9280_PHY_CH1_EXT_MINCCA_PWR);
|
||||
|
@ -621,7 +629,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
|
|||
u8 chainmask, rx_chain_status;
|
||||
|
||||
rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK);
|
||||
if (AR_SREV_9285(ah))
|
||||
if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
|
||||
chainmask = 0x9;
|
||||
else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) {
|
||||
if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4))
|
||||
|
@ -715,7 +723,7 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah)
|
|||
|
||||
if (AR_SREV_9280(ah))
|
||||
noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE;
|
||||
else if (AR_SREV_9285(ah))
|
||||
else if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
|
||||
noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE;
|
||||
else if (AR_SREV_9287(ah))
|
||||
noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE;
|
||||
|
@ -1051,9 +1059,12 @@ bool ath9k_hw_calibrate(struct ath_hw *ah, struct ath9k_channel *chan,
|
|||
/* Do NF cal only at longer intervals */
|
||||
if (longcal) {
|
||||
/* Do periodic PAOffset Cal */
|
||||
if (AR_SREV_9271(ah))
|
||||
ath9k_hw_9271_pa_cal(ah, false);
|
||||
else if (AR_SREV_9285_11_OR_LATER(ah)) {
|
||||
if (AR_SREV_9271(ah)) {
|
||||
if (!ah->pacal_info.skipcount)
|
||||
ath9k_hw_9271_pa_cal(ah, false);
|
||||
else
|
||||
ah->pacal_info.skipcount--;
|
||||
} else if (AR_SREV_9285_11_OR_LATER(ah)) {
|
||||
if (!ah->pacal_info.skipcount)
|
||||
ath9k_hw_9285_pa_cal(ah, false);
|
||||
else
|
||||
|
|
|
@ -286,6 +286,427 @@ int ath9k_cmn_padpos(__le16 frame_control)
|
|||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_padpos);
|
||||
|
||||
int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
if (tx_info->control.hw_key) {
|
||||
if (tx_info->control.hw_key->alg == ALG_WEP)
|
||||
return ATH9K_KEY_TYPE_WEP;
|
||||
else if (tx_info->control.hw_key->alg == ALG_TKIP)
|
||||
return ATH9K_KEY_TYPE_TKIP;
|
||||
else if (tx_info->control.hw_key->alg == ALG_CCMP)
|
||||
return ATH9K_KEY_TYPE_AES;
|
||||
}
|
||||
|
||||
return ATH9K_KEY_TYPE_CLEAR;
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
|
||||
|
||||
/*
|
||||
* Calculate the RX filter to be set in the HW.
|
||||
*/
|
||||
u32 ath9k_cmn_calcrxfilter(struct ieee80211_hw *hw, struct ath_hw *ah,
|
||||
unsigned int rxfilter)
|
||||
{
|
||||
#define RX_FILTER_PRESERVE (ATH9K_RX_FILTER_PHYERR | ATH9K_RX_FILTER_PHYRADAR)
|
||||
|
||||
u32 rfilt;
|
||||
|
||||
rfilt = (ath9k_hw_getrxfilter(ah) & RX_FILTER_PRESERVE)
|
||||
| ATH9K_RX_FILTER_UCAST | ATH9K_RX_FILTER_BCAST
|
||||
| ATH9K_RX_FILTER_MCAST;
|
||||
|
||||
/* If not a STA, enable processing of Probe Requests */
|
||||
if (ah->opmode != NL80211_IFTYPE_STATION)
|
||||
rfilt |= ATH9K_RX_FILTER_PROBEREQ;
|
||||
|
||||
/*
|
||||
* Set promiscuous mode when FIF_PROMISC_IN_BSS is enabled for station
|
||||
* mode interface or when in monitor mode. AP mode does not need this
|
||||
* since it receives all in-BSS frames anyway.
|
||||
*/
|
||||
if (((ah->opmode != NL80211_IFTYPE_AP) &&
|
||||
(rxfilter & FIF_PROMISC_IN_BSS)) ||
|
||||
(ah->opmode == NL80211_IFTYPE_MONITOR))
|
||||
rfilt |= ATH9K_RX_FILTER_PROM;
|
||||
|
||||
if (rxfilter & FIF_CONTROL)
|
||||
rfilt |= ATH9K_RX_FILTER_CONTROL;
|
||||
|
||||
if ((ah->opmode == NL80211_IFTYPE_STATION) &&
|
||||
!(rxfilter & FIF_BCN_PRBRESP_PROMISC))
|
||||
rfilt |= ATH9K_RX_FILTER_MYBEACON;
|
||||
else
|
||||
rfilt |= ATH9K_RX_FILTER_BEACON;
|
||||
|
||||
if ((AR_SREV_9280_10_OR_LATER(ah) ||
|
||||
AR_SREV_9285_10_OR_LATER(ah)) &&
|
||||
(ah->opmode == NL80211_IFTYPE_AP) &&
|
||||
(rxfilter & FIF_PSPOLL))
|
||||
rfilt |= ATH9K_RX_FILTER_PSPOLL;
|
||||
|
||||
if (conf_is_ht(&hw->conf))
|
||||
rfilt |= ATH9K_RX_FILTER_COMP_BAR;
|
||||
|
||||
return rfilt;
|
||||
|
||||
#undef RX_FILTER_PRESERVE
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_calcrxfilter);
|
||||
|
||||
/*
|
||||
* Recv initialization for opmode change.
|
||||
*/
|
||||
void ath9k_cmn_opmode_init(struct ieee80211_hw *hw, struct ath_hw *ah,
|
||||
unsigned int rxfilter)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
|
||||
u32 rfilt, mfilt[2];
|
||||
|
||||
/* configure rx filter */
|
||||
rfilt = ath9k_cmn_calcrxfilter(hw, ah, rxfilter);
|
||||
ath9k_hw_setrxfilter(ah, rfilt);
|
||||
|
||||
/* configure bssid mask */
|
||||
if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
|
||||
ath_hw_setbssidmask(common);
|
||||
|
||||
/* configure operational mode */
|
||||
ath9k_hw_setopmode(ah);
|
||||
|
||||
/* Handle any link-level address change. */
|
||||
ath9k_hw_setmac(ah, common->macaddr);
|
||||
|
||||
/* calculate and install multicast filter */
|
||||
mfilt[0] = mfilt[1] = ~0;
|
||||
ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]);
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_opmode_init);
|
||||
|
||||
static u32 ath9k_get_extchanmode(struct ieee80211_channel *chan,
|
||||
enum nl80211_channel_type channel_type)
|
||||
{
|
||||
u32 chanmode = 0;
|
||||
|
||||
switch (chan->band) {
|
||||
case IEEE80211_BAND_2GHZ:
|
||||
switch (channel_type) {
|
||||
case NL80211_CHAN_NO_HT:
|
||||
case NL80211_CHAN_HT20:
|
||||
chanmode = CHANNEL_G_HT20;
|
||||
break;
|
||||
case NL80211_CHAN_HT40PLUS:
|
||||
chanmode = CHANNEL_G_HT40PLUS;
|
||||
break;
|
||||
case NL80211_CHAN_HT40MINUS:
|
||||
chanmode = CHANNEL_G_HT40MINUS;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IEEE80211_BAND_5GHZ:
|
||||
switch (channel_type) {
|
||||
case NL80211_CHAN_NO_HT:
|
||||
case NL80211_CHAN_HT20:
|
||||
chanmode = CHANNEL_A_HT20;
|
||||
break;
|
||||
case NL80211_CHAN_HT40PLUS:
|
||||
chanmode = CHANNEL_A_HT40PLUS;
|
||||
break;
|
||||
case NL80211_CHAN_HT40MINUS:
|
||||
chanmode = CHANNEL_A_HT40MINUS;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return chanmode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update internal channel flags.
|
||||
*/
|
||||
void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw,
|
||||
struct ath9k_channel *ichan)
|
||||
{
|
||||
struct ieee80211_channel *chan = hw->conf.channel;
|
||||
struct ieee80211_conf *conf = &hw->conf;
|
||||
|
||||
ichan->channel = chan->center_freq;
|
||||
ichan->chan = chan;
|
||||
|
||||
if (chan->band == IEEE80211_BAND_2GHZ) {
|
||||
ichan->chanmode = CHANNEL_G;
|
||||
ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM | CHANNEL_G;
|
||||
} else {
|
||||
ichan->chanmode = CHANNEL_A;
|
||||
ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
|
||||
}
|
||||
|
||||
if (conf_is_ht(conf))
|
||||
ichan->chanmode = ath9k_get_extchanmode(chan,
|
||||
conf->channel_type);
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_update_ichannel);
|
||||
|
||||
/*
|
||||
* Get the internal channel reference.
|
||||
*/
|
||||
struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
|
||||
struct ath_hw *ah)
|
||||
{
|
||||
struct ieee80211_channel *curchan = hw->conf.channel;
|
||||
struct ath9k_channel *channel;
|
||||
u8 chan_idx;
|
||||
|
||||
chan_idx = curchan->hw_value;
|
||||
channel = &ah->channels[chan_idx];
|
||||
ath9k_cmn_update_ichannel(hw, channel);
|
||||
|
||||
return channel;
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_get_curchannel);
|
||||
|
||||
static int ath_setkey_tkip(struct ath_common *common, u16 keyix, const u8 *key,
|
||||
struct ath9k_keyval *hk, const u8 *addr,
|
||||
bool authenticator)
|
||||
{
|
||||
struct ath_hw *ah = common->ah;
|
||||
const u8 *key_rxmic;
|
||||
const u8 *key_txmic;
|
||||
|
||||
key_txmic = key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY;
|
||||
key_rxmic = key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY;
|
||||
|
||||
if (addr == NULL) {
|
||||
/*
|
||||
* Group key installation - only two key cache entries are used
|
||||
* regardless of splitmic capability since group key is only
|
||||
* used either for TX or RX.
|
||||
*/
|
||||
if (authenticator) {
|
||||
memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
|
||||
memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_mic));
|
||||
} else {
|
||||
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
|
||||
memcpy(hk->kv_txmic, key_rxmic, sizeof(hk->kv_mic));
|
||||
}
|
||||
return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
|
||||
}
|
||||
if (!common->splitmic) {
|
||||
/* TX and RX keys share the same key cache entry. */
|
||||
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
|
||||
memcpy(hk->kv_txmic, key_txmic, sizeof(hk->kv_txmic));
|
||||
return ath9k_hw_set_keycache_entry(ah, keyix, hk, addr);
|
||||
}
|
||||
|
||||
/* Separate key cache entries for TX and RX */
|
||||
|
||||
/* TX key goes at first index, RX key at +32. */
|
||||
memcpy(hk->kv_mic, key_txmic, sizeof(hk->kv_mic));
|
||||
if (!ath9k_hw_set_keycache_entry(ah, keyix, hk, NULL)) {
|
||||
/* TX MIC entry failed. No need to proceed further */
|
||||
ath_print(common, ATH_DBG_FATAL,
|
||||
"Setting TX MIC Key Failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(hk->kv_mic, key_rxmic, sizeof(hk->kv_mic));
|
||||
/* XXX delete tx key on failure? */
|
||||
return ath9k_hw_set_keycache_entry(ah, keyix + 32, hk, addr);
|
||||
}
|
||||
|
||||
static int ath_reserve_key_cache_slot_tkip(struct ath_common *common)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
|
||||
if (test_bit(i, common->keymap) ||
|
||||
test_bit(i + 64, common->keymap))
|
||||
continue; /* At least one part of TKIP key allocated */
|
||||
if (common->splitmic &&
|
||||
(test_bit(i + 32, common->keymap) ||
|
||||
test_bit(i + 64 + 32, common->keymap)))
|
||||
continue; /* At least one part of TKIP key allocated */
|
||||
|
||||
/* Found a free slot for a TKIP key */
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int ath_reserve_key_cache_slot(struct ath_common *common)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* First, try to find slots that would not be available for TKIP. */
|
||||
if (common->splitmic) {
|
||||
for (i = IEEE80211_WEP_NKID; i < common->keymax / 4; i++) {
|
||||
if (!test_bit(i, common->keymap) &&
|
||||
(test_bit(i + 32, common->keymap) ||
|
||||
test_bit(i + 64, common->keymap) ||
|
||||
test_bit(i + 64 + 32, common->keymap)))
|
||||
return i;
|
||||
if (!test_bit(i + 32, common->keymap) &&
|
||||
(test_bit(i, common->keymap) ||
|
||||
test_bit(i + 64, common->keymap) ||
|
||||
test_bit(i + 64 + 32, common->keymap)))
|
||||
return i + 32;
|
||||
if (!test_bit(i + 64, common->keymap) &&
|
||||
(test_bit(i , common->keymap) ||
|
||||
test_bit(i + 32, common->keymap) ||
|
||||
test_bit(i + 64 + 32, common->keymap)))
|
||||
return i + 64;
|
||||
if (!test_bit(i + 64 + 32, common->keymap) &&
|
||||
(test_bit(i, common->keymap) ||
|
||||
test_bit(i + 32, common->keymap) ||
|
||||
test_bit(i + 64, common->keymap)))
|
||||
return i + 64 + 32;
|
||||
}
|
||||
} else {
|
||||
for (i = IEEE80211_WEP_NKID; i < common->keymax / 2; i++) {
|
||||
if (!test_bit(i, common->keymap) &&
|
||||
test_bit(i + 64, common->keymap))
|
||||
return i;
|
||||
if (test_bit(i, common->keymap) &&
|
||||
!test_bit(i + 64, common->keymap))
|
||||
return i + 64;
|
||||
}
|
||||
}
|
||||
|
||||
/* No partially used TKIP slots, pick any available slot */
|
||||
for (i = IEEE80211_WEP_NKID; i < common->keymax; i++) {
|
||||
/* Do not allow slots that could be needed for TKIP group keys
|
||||
* to be used. This limitation could be removed if we know that
|
||||
* TKIP will not be used. */
|
||||
if (i >= 64 && i < 64 + IEEE80211_WEP_NKID)
|
||||
continue;
|
||||
if (common->splitmic) {
|
||||
if (i >= 32 && i < 32 + IEEE80211_WEP_NKID)
|
||||
continue;
|
||||
if (i >= 64 + 32 && i < 64 + 32 + IEEE80211_WEP_NKID)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!test_bit(i, common->keymap))
|
||||
return i; /* Found a free slot for a key */
|
||||
}
|
||||
|
||||
/* No free slot found */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure encryption in the HW.
|
||||
*/
|
||||
int ath9k_cmn_key_config(struct ath_common *common,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key)
|
||||
{
|
||||
struct ath_hw *ah = common->ah;
|
||||
struct ath9k_keyval hk;
|
||||
const u8 *mac = NULL;
|
||||
int ret = 0;
|
||||
int idx;
|
||||
|
||||
memset(&hk, 0, sizeof(hk));
|
||||
|
||||
switch (key->alg) {
|
||||
case ALG_WEP:
|
||||
hk.kv_type = ATH9K_CIPHER_WEP;
|
||||
break;
|
||||
case ALG_TKIP:
|
||||
hk.kv_type = ATH9K_CIPHER_TKIP;
|
||||
break;
|
||||
case ALG_CCMP:
|
||||
hk.kv_type = ATH9K_CIPHER_AES_CCM;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
hk.kv_len = key->keylen;
|
||||
memcpy(hk.kv_val, key->key, key->keylen);
|
||||
|
||||
if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
|
||||
/* For now, use the default keys for broadcast keys. This may
|
||||
* need to change with virtual interfaces. */
|
||||
idx = key->keyidx;
|
||||
} else if (key->keyidx) {
|
||||
if (WARN_ON(!sta))
|
||||
return -EOPNOTSUPP;
|
||||
mac = sta->addr;
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_AP) {
|
||||
/* Only keyidx 0 should be used with unicast key, but
|
||||
* allow this for client mode for now. */
|
||||
idx = key->keyidx;
|
||||
} else
|
||||
return -EIO;
|
||||
} else {
|
||||
if (WARN_ON(!sta))
|
||||
return -EOPNOTSUPP;
|
||||
mac = sta->addr;
|
||||
|
||||
if (key->alg == ALG_TKIP)
|
||||
idx = ath_reserve_key_cache_slot_tkip(common);
|
||||
else
|
||||
idx = ath_reserve_key_cache_slot(common);
|
||||
if (idx < 0)
|
||||
return -ENOSPC; /* no free key cache entries */
|
||||
}
|
||||
|
||||
if (key->alg == ALG_TKIP)
|
||||
ret = ath_setkey_tkip(common, idx, key->key, &hk, mac,
|
||||
vif->type == NL80211_IFTYPE_AP);
|
||||
else
|
||||
ret = ath9k_hw_set_keycache_entry(ah, idx, &hk, mac);
|
||||
|
||||
if (!ret)
|
||||
return -EIO;
|
||||
|
||||
set_bit(idx, common->keymap);
|
||||
if (key->alg == ALG_TKIP) {
|
||||
set_bit(idx + 64, common->keymap);
|
||||
if (common->splitmic) {
|
||||
set_bit(idx + 32, common->keymap);
|
||||
set_bit(idx + 64 + 32, common->keymap);
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_key_config);
|
||||
|
||||
/*
|
||||
* Delete Key.
|
||||
*/
|
||||
void ath9k_cmn_key_delete(struct ath_common *common,
|
||||
struct ieee80211_key_conf *key)
|
||||
{
|
||||
struct ath_hw *ah = common->ah;
|
||||
|
||||
ath9k_hw_keyreset(ah, key->hw_key_idx);
|
||||
if (key->hw_key_idx < IEEE80211_WEP_NKID)
|
||||
return;
|
||||
|
||||
clear_bit(key->hw_key_idx, common->keymap);
|
||||
if (key->alg != ALG_TKIP)
|
||||
return;
|
||||
|
||||
clear_bit(key->hw_key_idx + 64, common->keymap);
|
||||
if (common->splitmic) {
|
||||
ath9k_hw_keyreset(ah, key->hw_key_idx + 32);
|
||||
clear_bit(key->hw_key_idx + 32, common->keymap);
|
||||
clear_bit(key->hw_key_idx + 64 + 32, common->keymap);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_key_delete);
|
||||
|
||||
static int __init ath9k_cmn_init(void)
|
||||
{
|
||||
return 0;
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
|
||||
/* Common header for Atheros 802.11n base driver cores */
|
||||
|
||||
#define IEEE80211_WEP_NKID 4
|
||||
|
||||
#define WME_NUM_TID 16
|
||||
#define WME_BA_BMP_SIZE 64
|
||||
#define WME_MAX_BA WME_BA_BMP_SIZE
|
||||
|
@ -125,3 +127,18 @@ void ath9k_cmn_rx_skb_postprocess(struct ath_common *common,
|
|||
bool decrypt_error);
|
||||
|
||||
int ath9k_cmn_padpos(__le16 frame_control);
|
||||
int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
|
||||
u32 ath9k_cmn_calcrxfilter(struct ieee80211_hw *hw, struct ath_hw *ah,
|
||||
unsigned int rxfilter);
|
||||
void ath9k_cmn_opmode_init(struct ieee80211_hw *hw, struct ath_hw *ah,
|
||||
unsigned int rxfilter);
|
||||
void ath9k_cmn_update_ichannel(struct ieee80211_hw *hw,
|
||||
struct ath9k_channel *ichan);
|
||||
struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
|
||||
struct ath_hw *ah);
|
||||
int ath9k_cmn_key_config(struct ath_common *common,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key);
|
||||
void ath9k_cmn_key_delete(struct ath_common *common,
|
||||
struct ieee80211_key_conf *key);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,713 @@
|
|||
/*
|
||||
* Copyright (c) 2010 Atheros Communications Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "htc.h"
|
||||
|
||||
MODULE_AUTHOR("Atheros Communications");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_DESCRIPTION("Atheros driver 802.11n HTC based wireless devices");
|
||||
|
||||
static unsigned int ath9k_debug = ATH_DBG_DEFAULT;
|
||||
module_param_named(debug, ath9k_debug, uint, 0);
|
||||
MODULE_PARM_DESC(debug, "Debugging mask");
|
||||
|
||||
int htc_modparam_nohwcrypt;
|
||||
module_param_named(nohwcrypt, htc_modparam_nohwcrypt, int, 0444);
|
||||
MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption");
|
||||
|
||||
#define CHAN2G(_freq, _idx) { \
|
||||
.center_freq = (_freq), \
|
||||
.hw_value = (_idx), \
|
||||
.max_power = 20, \
|
||||
}
|
||||
|
||||
static struct ieee80211_channel ath9k_2ghz_channels[] = {
|
||||
CHAN2G(2412, 0), /* Channel 1 */
|
||||
CHAN2G(2417, 1), /* Channel 2 */
|
||||
CHAN2G(2422, 2), /* Channel 3 */
|
||||
CHAN2G(2427, 3), /* Channel 4 */
|
||||
CHAN2G(2432, 4), /* Channel 5 */
|
||||
CHAN2G(2437, 5), /* Channel 6 */
|
||||
CHAN2G(2442, 6), /* Channel 7 */
|
||||
CHAN2G(2447, 7), /* Channel 8 */
|
||||
CHAN2G(2452, 8), /* Channel 9 */
|
||||
CHAN2G(2457, 9), /* Channel 10 */
|
||||
CHAN2G(2462, 10), /* Channel 11 */
|
||||
CHAN2G(2467, 11), /* Channel 12 */
|
||||
CHAN2G(2472, 12), /* Channel 13 */
|
||||
CHAN2G(2484, 13), /* Channel 14 */
|
||||
};
|
||||
|
||||
/* Atheros hardware rate code addition for short premble */
|
||||
#define SHPCHECK(__hw_rate, __flags) \
|
||||
((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04) : 0)
|
||||
|
||||
#define RATE(_bitrate, _hw_rate, _flags) { \
|
||||
.bitrate = (_bitrate), \
|
||||
.flags = (_flags), \
|
||||
.hw_value = (_hw_rate), \
|
||||
.hw_value_short = (SHPCHECK(_hw_rate, _flags)) \
|
||||
}
|
||||
|
||||
static struct ieee80211_rate ath9k_legacy_rates[] = {
|
||||
RATE(10, 0x1b, 0),
|
||||
RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp : 0x1e */
|
||||
RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), /* shortp: 0x1d */
|
||||
RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), /* short: 0x1c */
|
||||
RATE(60, 0x0b, 0),
|
||||
RATE(90, 0x0f, 0),
|
||||
RATE(120, 0x0a, 0),
|
||||
RATE(180, 0x0e, 0),
|
||||
RATE(240, 0x09, 0),
|
||||
RATE(360, 0x0d, 0),
|
||||
RATE(480, 0x08, 0),
|
||||
RATE(540, 0x0c, 0),
|
||||
};
|
||||
|
||||
static int ath9k_htc_wait_for_target(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
int time_left;
|
||||
|
||||
/* Firmware can take up to 50ms to get ready, to be safe use 1 second */
|
||||
time_left = wait_for_completion_timeout(&priv->htc->target_wait, HZ);
|
||||
if (!time_left) {
|
||||
dev_err(priv->dev, "ath9k_htc: Target is unresponsive\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ath9k_deinit_priv(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
ath9k_htc_exit_debug(priv->ah);
|
||||
ath9k_hw_deinit(priv->ah);
|
||||
tasklet_kill(&priv->wmi_tasklet);
|
||||
tasklet_kill(&priv->rx_tasklet);
|
||||
tasklet_kill(&priv->tx_tasklet);
|
||||
kfree(priv->ah);
|
||||
priv->ah = NULL;
|
||||
}
|
||||
|
||||
static void ath9k_deinit_device(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
struct ieee80211_hw *hw = priv->hw;
|
||||
|
||||
wiphy_rfkill_stop_polling(hw->wiphy);
|
||||
ath9k_deinit_leds(priv);
|
||||
ieee80211_unregister_hw(hw);
|
||||
ath9k_rx_cleanup(priv);
|
||||
ath9k_tx_cleanup(priv);
|
||||
ath9k_deinit_priv(priv);
|
||||
}
|
||||
|
||||
static inline int ath9k_htc_connect_svc(struct ath9k_htc_priv *priv,
|
||||
u16 service_id,
|
||||
void (*tx) (void *,
|
||||
struct sk_buff *,
|
||||
enum htc_endpoint_id,
|
||||
bool txok),
|
||||
enum htc_endpoint_id *ep_id)
|
||||
{
|
||||
struct htc_service_connreq req;
|
||||
|
||||
memset(&req, 0, sizeof(struct htc_service_connreq));
|
||||
|
||||
req.service_id = service_id;
|
||||
req.ep_callbacks.priv = priv;
|
||||
req.ep_callbacks.rx = ath9k_htc_rxep;
|
||||
req.ep_callbacks.tx = tx;
|
||||
|
||||
return htc_connect_service(priv->htc, &req, ep_id);
|
||||
}
|
||||
|
||||
static int ath9k_init_htc_services(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* WMI CMD*/
|
||||
ret = ath9k_wmi_connect(priv->htc, priv->wmi, &priv->wmi_cmd_ep);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* Beacon */
|
||||
ret = ath9k_htc_connect_svc(priv, WMI_BEACON_SVC, NULL,
|
||||
&priv->beacon_ep);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* CAB */
|
||||
ret = ath9k_htc_connect_svc(priv, WMI_CAB_SVC, ath9k_htc_txep,
|
||||
&priv->cab_ep);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
|
||||
/* UAPSD */
|
||||
ret = ath9k_htc_connect_svc(priv, WMI_UAPSD_SVC, ath9k_htc_txep,
|
||||
&priv->uapsd_ep);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* MGMT */
|
||||
ret = ath9k_htc_connect_svc(priv, WMI_MGMT_SVC, ath9k_htc_txep,
|
||||
&priv->mgmt_ep);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* DATA BE */
|
||||
ret = ath9k_htc_connect_svc(priv, WMI_DATA_BE_SVC, ath9k_htc_txep,
|
||||
&priv->data_be_ep);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* DATA BK */
|
||||
ret = ath9k_htc_connect_svc(priv, WMI_DATA_BK_SVC, ath9k_htc_txep,
|
||||
&priv->data_bk_ep);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* DATA VI */
|
||||
ret = ath9k_htc_connect_svc(priv, WMI_DATA_VI_SVC, ath9k_htc_txep,
|
||||
&priv->data_vi_ep);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* DATA VO */
|
||||
ret = ath9k_htc_connect_svc(priv, WMI_DATA_VO_SVC, ath9k_htc_txep,
|
||||
&priv->data_vo_ep);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = htc_init(priv->htc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
dev_err(priv->dev, "ath9k_htc: Unable to initialize HTC services\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ath9k_reg_notifier(struct wiphy *wiphy,
|
||||
struct regulatory_request *request)
|
||||
{
|
||||
struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
|
||||
struct ath9k_htc_priv *priv = hw->priv;
|
||||
|
||||
return ath_reg_notifier_apply(wiphy, request,
|
||||
ath9k_hw_regulatory(priv->ah));
|
||||
}
|
||||
|
||||
static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset)
|
||||
{
|
||||
struct ath_hw *ah = (struct ath_hw *) hw_priv;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
|
||||
__be32 val, reg = cpu_to_be32(reg_offset);
|
||||
int r;
|
||||
|
||||
r = ath9k_wmi_cmd(priv->wmi, WMI_REG_READ_CMDID,
|
||||
(u8 *) ®, sizeof(reg),
|
||||
(u8 *) &val, sizeof(val),
|
||||
100);
|
||||
if (unlikely(r)) {
|
||||
ath_print(common, ATH_DBG_WMI,
|
||||
"REGISTER READ FAILED: (0x%04x, %d)\n",
|
||||
reg_offset, r);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return be32_to_cpu(val);
|
||||
}
|
||||
|
||||
static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset)
|
||||
{
|
||||
struct ath_hw *ah = (struct ath_hw *) hw_priv;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
|
||||
__be32 buf[2] = {
|
||||
cpu_to_be32(reg_offset),
|
||||
cpu_to_be32(val),
|
||||
};
|
||||
int r;
|
||||
|
||||
r = ath9k_wmi_cmd(priv->wmi, WMI_REG_WRITE_CMDID,
|
||||
(u8 *) &buf, sizeof(buf),
|
||||
(u8 *) &val, sizeof(val),
|
||||
100);
|
||||
if (unlikely(r)) {
|
||||
ath_print(common, ATH_DBG_WMI,
|
||||
"REGISTER WRITE FAILED:(0x%04x, %d)\n",
|
||||
reg_offset, r);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ath_ops ath9k_common_ops = {
|
||||
.read = ath9k_ioread32,
|
||||
.write = ath9k_iowrite32,
|
||||
};
|
||||
|
||||
static void ath_usb_read_cachesize(struct ath_common *common, int *csz)
|
||||
{
|
||||
*csz = L1_CACHE_BYTES >> 2;
|
||||
}
|
||||
|
||||
static bool ath_usb_eeprom_read(struct ath_common *common, u32 off, u16 *data)
|
||||
{
|
||||
struct ath_hw *ah = (struct ath_hw *) common->ah;
|
||||
|
||||
(void)REG_READ(ah, AR5416_EEPROM_OFFSET + (off << AR5416_EEPROM_S));
|
||||
|
||||
if (!ath9k_hw_wait(ah,
|
||||
AR_EEPROM_STATUS_DATA,
|
||||
AR_EEPROM_STATUS_DATA_BUSY |
|
||||
AR_EEPROM_STATUS_DATA_PROT_ACCESS, 0,
|
||||
AH_WAIT_TIMEOUT))
|
||||
return false;
|
||||
|
||||
*data = MS(REG_READ(ah, AR_EEPROM_STATUS_DATA),
|
||||
AR_EEPROM_STATUS_DATA_VAL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct ath_bus_ops ath9k_usb_bus_ops = {
|
||||
.read_cachesize = ath_usb_read_cachesize,
|
||||
.eeprom_read = ath_usb_eeprom_read,
|
||||
};
|
||||
|
||||
static void setup_ht_cap(struct ath9k_htc_priv *priv,
|
||||
struct ieee80211_sta_ht_cap *ht_info)
|
||||
{
|
||||
ht_info->ht_supported = true;
|
||||
ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
||||
IEEE80211_HT_CAP_SM_PS |
|
||||
IEEE80211_HT_CAP_SGI_40 |
|
||||
IEEE80211_HT_CAP_DSSSCCK40;
|
||||
|
||||
ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
|
||||
ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8;
|
||||
|
||||
memset(&ht_info->mcs, 0, sizeof(ht_info->mcs));
|
||||
ht_info->mcs.rx_mask[0] = 0xff;
|
||||
ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED;
|
||||
}
|
||||
|
||||
static int ath9k_init_queues(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(priv->ah);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(priv->hwq_map); i++)
|
||||
priv->hwq_map[i] = -1;
|
||||
|
||||
if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_BE)) {
|
||||
ath_print(common, ATH_DBG_FATAL,
|
||||
"Unable to setup xmit queue for BE traffic\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_BK)) {
|
||||
ath_print(common, ATH_DBG_FATAL,
|
||||
"Unable to setup xmit queue for BK traffic\n");
|
||||
goto err;
|
||||
}
|
||||
if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_VI)) {
|
||||
ath_print(common, ATH_DBG_FATAL,
|
||||
"Unable to setup xmit queue for VI traffic\n");
|
||||
goto err;
|
||||
}
|
||||
if (!ath9k_htc_txq_setup(priv, ATH9K_WME_AC_VO)) {
|
||||
ath_print(common, ATH_DBG_FATAL,
|
||||
"Unable to setup xmit queue for VO traffic\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void ath9k_init_crypto(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(priv->ah);
|
||||
int i = 0;
|
||||
|
||||
/* Get the hardware key cache size. */
|
||||
common->keymax = priv->ah->caps.keycache_size;
|
||||
if (common->keymax > ATH_KEYMAX) {
|
||||
ath_print(common, ATH_DBG_ANY,
|
||||
"Warning, using only %u entries in %u key cache\n",
|
||||
ATH_KEYMAX, common->keymax);
|
||||
common->keymax = ATH_KEYMAX;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset the key cache since some parts do not
|
||||
* reset the contents on initial power up.
|
||||
*/
|
||||
for (i = 0; i < common->keymax; i++)
|
||||
ath9k_hw_keyreset(priv->ah, (u16) i);
|
||||
|
||||
if (ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER,
|
||||
ATH9K_CIPHER_TKIP, NULL)) {
|
||||
/*
|
||||
* Whether we should enable h/w TKIP MIC.
|
||||
* XXX: if we don't support WME TKIP MIC, then we wouldn't
|
||||
* report WMM capable, so it's always safe to turn on
|
||||
* TKIP MIC in this case.
|
||||
*/
|
||||
ath9k_hw_setcapability(priv->ah, ATH9K_CAP_TKIP_MIC, 0, 1, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether the separate key cache entries
|
||||
* are required to handle both tx+rx MIC keys.
|
||||
* With split mic keys the number of stations is limited
|
||||
* to 27 otherwise 59.
|
||||
*/
|
||||
if (ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER,
|
||||
ATH9K_CIPHER_TKIP, NULL)
|
||||
&& ath9k_hw_getcapability(priv->ah, ATH9K_CAP_CIPHER,
|
||||
ATH9K_CIPHER_MIC, NULL)
|
||||
&& ath9k_hw_getcapability(priv->ah, ATH9K_CAP_TKIP_SPLIT,
|
||||
0, NULL))
|
||||
common->splitmic = 1;
|
||||
|
||||
/* turn on mcast key search if possible */
|
||||
if (!ath9k_hw_getcapability(priv->ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL))
|
||||
(void)ath9k_hw_setcapability(priv->ah, ATH9K_CAP_MCAST_KEYSRCH,
|
||||
1, 1, NULL);
|
||||
}
|
||||
|
||||
static void ath9k_init_channels_rates(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes)) {
|
||||
priv->sbands[IEEE80211_BAND_2GHZ].channels =
|
||||
ath9k_2ghz_channels;
|
||||
priv->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ;
|
||||
priv->sbands[IEEE80211_BAND_2GHZ].n_channels =
|
||||
ARRAY_SIZE(ath9k_2ghz_channels);
|
||||
priv->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates;
|
||||
priv->sbands[IEEE80211_BAND_2GHZ].n_bitrates =
|
||||
ARRAY_SIZE(ath9k_legacy_rates);
|
||||
}
|
||||
}
|
||||
|
||||
static void ath9k_init_misc(struct ath9k_htc_priv *priv)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(priv->ah);
|
||||
|
||||
common->tx_chainmask = priv->ah->caps.tx_chainmask;
|
||||
common->rx_chainmask = priv->ah->caps.rx_chainmask;
|
||||
|
||||
if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK)
|
||||
memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
|
||||
|
||||
priv->op_flags |= OP_TXAGGR;
|
||||
}
|
||||
|
||||
static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid)
|
||||
{
|
||||
struct ath_hw *ah = NULL;
|
||||
struct ath_common *common;
|
||||
int ret = 0, csz = 0;
|
||||
|
||||
priv->op_flags |= OP_INVALID;
|
||||
|
||||
ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL);
|
||||
if (!ah)
|
||||
return -ENOMEM;
|
||||
|
||||
ah->hw_version.devid = devid;
|
||||
ah->hw_version.subsysid = 0; /* FIXME */
|
||||
priv->ah = ah;
|
||||
|
||||
common = ath9k_hw_common(ah);
|
||||
common->ops = &ath9k_common_ops;
|
||||
common->bus_ops = &ath9k_usb_bus_ops;
|
||||
common->ah = ah;
|
||||
common->hw = priv->hw;
|
||||
common->priv = priv;
|
||||
common->debug_mask = ath9k_debug;
|
||||
|
||||
spin_lock_init(&priv->wmi->wmi_lock);
|
||||
spin_lock_init(&priv->beacon_lock);
|
||||
mutex_init(&priv->mutex);
|
||||
mutex_init(&priv->aggr_work.mutex);
|
||||
tasklet_init(&priv->wmi_tasklet, ath9k_wmi_tasklet,
|
||||
(unsigned long)priv);
|
||||
tasklet_init(&priv->rx_tasklet, ath9k_rx_tasklet,
|
||||
(unsigned long)priv);
|
||||
tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet, (unsigned long)priv);
|
||||
INIT_DELAYED_WORK(&priv->ath9k_aggr_work, ath9k_htc_aggr_work);
|
||||
INIT_DELAYED_WORK(&priv->ath9k_ani_work, ath9k_ani_work);
|
||||
|
||||
/*
|
||||
* Cache line size is used to size and align various
|
||||
* structures used to communicate with the hardware.
|
||||
*/
|
||||
ath_read_cachesize(common, &csz);
|
||||
common->cachelsz = csz << 2; /* convert to bytes */
|
||||
|
||||
ret = ath9k_hw_init(ah);
|
||||
if (ret) {
|
||||
ath_print(common, ATH_DBG_FATAL,
|
||||
"Unable to initialize hardware; "
|
||||
"initialization status: %d\n", ret);
|
||||
goto err_hw;
|
||||
}
|
||||
|
||||
ret = ath9k_htc_init_debug(ah);
|
||||
if (ret) {
|
||||
ath_print(common, ATH_DBG_FATAL,
|
||||
"Unable to create debugfs files\n");
|
||||
goto err_debug;
|
||||
}
|
||||
|
||||
ret = ath9k_init_queues(priv);
|
||||
if (ret)
|
||||
goto err_queues;
|
||||
|
||||
ath9k_init_crypto(priv);
|
||||
ath9k_init_channels_rates(priv);
|
||||
ath9k_init_misc(priv);
|
||||
|
||||
return 0;
|
||||
|
||||
err_queues:
|
||||
ath9k_htc_exit_debug(ah);
|
||||
err_debug:
|
||||
ath9k_hw_deinit(ah);
|
||||
err_hw:
|
||||
|
||||
kfree(ah);
|
||||
priv->ah = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ath9k_set_hw_capab(struct ath9k_htc_priv *priv,
|
||||
struct ieee80211_hw *hw)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(priv->ah);
|
||||
|
||||
hw->flags = IEEE80211_HW_SIGNAL_DBM |
|
||||
IEEE80211_HW_AMPDU_AGGREGATION |
|
||||
IEEE80211_HW_SPECTRUM_MGMT |
|
||||
IEEE80211_HW_HAS_RATE_CONTROL;
|
||||
|
||||
hw->wiphy->interface_modes =
|
||||
BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_ADHOC);
|
||||
|
||||
hw->queues = 4;
|
||||
hw->channel_change_time = 5000;
|
||||
hw->max_listen_interval = 10;
|
||||
hw->vif_data_size = sizeof(struct ath9k_htc_vif);
|
||||
hw->sta_data_size = sizeof(struct ath9k_htc_sta);
|
||||
|
||||
/* tx_frame_hdr is larger than tx_mgmt_hdr anyway */
|
||||
hw->extra_tx_headroom = sizeof(struct tx_frame_hdr) +
|
||||
sizeof(struct htc_frame_hdr) + 4;
|
||||
|
||||
if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes))
|
||||
hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
|
||||
&priv->sbands[IEEE80211_BAND_2GHZ];
|
||||
|
||||
if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_HT) {
|
||||
if (test_bit(ATH9K_MODE_11G, priv->ah->caps.wireless_modes))
|
||||
setup_ht_cap(priv,
|
||||
&priv->sbands[IEEE80211_BAND_2GHZ].ht_cap);
|
||||
}
|
||||
|
||||
SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
|
||||
}
|
||||
|
||||
static int ath9k_init_device(struct ath9k_htc_priv *priv, u16 devid)
|
||||
{
|
||||
struct ieee80211_hw *hw = priv->hw;
|
||||
struct ath_common *common;
|
||||
struct ath_hw *ah;
|
||||
int error = 0;
|
||||
struct ath_regulatory *reg;
|
||||
|
||||
/* Bring up device */
|
||||
error = ath9k_init_priv(priv, devid);
|
||||
if (error != 0)
|
||||
goto err_init;
|
||||
|
||||
ah = priv->ah;
|
||||
common = ath9k_hw_common(ah);
|
||||
ath9k_set_hw_capab(priv, hw);
|
||||
|
||||
/* Initialize regulatory */
|
||||
error = ath_regd_init(&common->regulatory, priv->hw->wiphy,
|
||||
ath9k_reg_notifier);
|
||||
if (error)
|
||||
goto err_regd;
|
||||
|
||||
reg = &common->regulatory;
|
||||
|
||||
/* Setup TX */
|
||||
error = ath9k_tx_init(priv);
|
||||
if (error != 0)
|
||||
goto err_tx;
|
||||
|
||||
/* Setup RX */
|
||||
error = ath9k_rx_init(priv);
|
||||
if (error != 0)
|
||||
goto err_rx;
|
||||
|
||||
/* Register with mac80211 */
|
||||
error = ieee80211_register_hw(hw);
|
||||
if (error)
|
||||
goto err_register;
|
||||
|
||||
/* Handle world regulatory */
|
||||
if (!ath_is_world_regd(reg)) {
|
||||
error = regulatory_hint(hw->wiphy, reg->alpha2);
|
||||
if (error)
|
||||
goto err_world;
|
||||
}
|
||||
|
||||
ath9k_init_leds(priv);
|
||||
ath9k_start_rfkill_poll(priv);
|
||||
|
||||
return 0;
|
||||
|
||||
err_world:
|
||||
ieee80211_unregister_hw(hw);
|
||||
err_register:
|
||||
ath9k_rx_cleanup(priv);
|
||||
err_rx:
|
||||
ath9k_tx_cleanup(priv);
|
||||
err_tx:
|
||||
/* Nothing */
|
||||
err_regd:
|
||||
ath9k_deinit_priv(priv);
|
||||
err_init:
|
||||
return error;
|
||||
}
|
||||
|
||||
int ath9k_htc_probe_device(struct htc_target *htc_handle, struct device *dev,
|
||||
u16 devid)
|
||||
{
|
||||
struct ieee80211_hw *hw;
|
||||
struct ath9k_htc_priv *priv;
|
||||
int ret;
|
||||
|
||||
hw = ieee80211_alloc_hw(sizeof(struct ath9k_htc_priv), &ath9k_htc_ops);
|
||||
if (!hw)
|
||||
return -ENOMEM;
|
||||
|
||||
priv = hw->priv;
|
||||
priv->hw = hw;
|
||||
priv->htc = htc_handle;
|
||||
priv->dev = dev;
|
||||
htc_handle->drv_priv = priv;
|
||||
SET_IEEE80211_DEV(hw, priv->dev);
|
||||
|
||||
ret = ath9k_htc_wait_for_target(priv);
|
||||
if (ret)
|
||||
goto err_free;
|
||||
|
||||
priv->wmi = ath9k_init_wmi(priv);
|
||||
if (!priv->wmi) {
|
||||
ret = -EINVAL;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = ath9k_init_htc_services(priv);
|
||||
if (ret)
|
||||
goto err_init;
|
||||
|
||||
ret = ath9k_init_device(priv, devid);
|
||||
if (ret)
|
||||
goto err_init;
|
||||
|
||||
return 0;
|
||||
|
||||
err_init:
|
||||
ath9k_deinit_wmi(priv);
|
||||
err_free:
|
||||
ieee80211_free_hw(hw);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ath9k_htc_disconnect_device(struct htc_target *htc_handle, bool hotunplug)
|
||||
{
|
||||
if (htc_handle->drv_priv) {
|
||||
ath9k_deinit_device(htc_handle->drv_priv);
|
||||
ath9k_deinit_wmi(htc_handle->drv_priv);
|
||||
ieee80211_free_hw(htc_handle->drv_priv->hw);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int ath9k_htc_resume(struct htc_target *htc_handle)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ath9k_htc_wait_for_target(htc_handle->drv_priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ath9k_init_htc_services(htc_handle->drv_priv);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __init ath9k_htc_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = ath9k_htc_debug_create_root();
|
||||
if (error < 0) {
|
||||
printk(KERN_ERR
|
||||
"ath9k_htc: Unable to create debugfs root: %d\n",
|
||||
error);
|
||||
goto err_dbg;
|
||||
}
|
||||
|
||||
error = ath9k_hif_usb_init();
|
||||
if (error < 0) {
|
||||
printk(KERN_ERR
|
||||
"ath9k_htc: No USB devices found,"
|
||||
" driver not installed.\n");
|
||||
error = -ENODEV;
|
||||
goto err_usb;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_usb:
|
||||
ath9k_htc_debug_remove_root();
|
||||
err_dbg:
|
||||
return error;
|
||||
}
|
||||
module_init(ath9k_htc_init);
|
||||
|
||||
static void __exit ath9k_htc_exit(void)
|
||||
{
|
||||
ath9k_hif_usb_exit();
|
||||
ath9k_htc_debug_remove_root();
|
||||
printk(KERN_INFO "ath9k_htc: Driver unloaded\n");
|
||||
}
|
||||
module_exit(ath9k_htc_exit);
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -499,8 +499,10 @@ static int ath9k_hw_post_init(struct ath_hw *ah)
|
|||
{
|
||||
int ecode;
|
||||
|
||||
if (!ath9k_hw_chip_test(ah))
|
||||
return -ENODEV;
|
||||
if (!AR_SREV_9271(ah)) {
|
||||
if (!ath9k_hw_chip_test(ah))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ecode = ath9k_hw_rf_claim(ah);
|
||||
if (ecode != 0)
|
||||
|
@ -603,9 +605,23 @@ static void ath9k_hw_init_mode_regs(struct ath_hw *ah)
|
|||
ARRAY_SIZE(ar9271Modes_9271), 6);
|
||||
INIT_INI_ARRAY(&ah->iniCommon, ar9271Common_9271,
|
||||
ARRAY_SIZE(ar9271Common_9271), 2);
|
||||
INIT_INI_ARRAY(&ah->iniCommon_normal_cck_fir_coeff_9271,
|
||||
ar9271Common_normal_cck_fir_coeff_9271,
|
||||
ARRAY_SIZE(ar9271Common_normal_cck_fir_coeff_9271), 2);
|
||||
INIT_INI_ARRAY(&ah->iniCommon_japan_2484_cck_fir_coeff_9271,
|
||||
ar9271Common_japan_2484_cck_fir_coeff_9271,
|
||||
ARRAY_SIZE(ar9271Common_japan_2484_cck_fir_coeff_9271), 2);
|
||||
INIT_INI_ARRAY(&ah->iniModes_9271_1_0_only,
|
||||
ar9271Modes_9271_1_0_only,
|
||||
ARRAY_SIZE(ar9271Modes_9271_1_0_only), 6);
|
||||
INIT_INI_ARRAY(&ah->iniModes_9271_ANI_reg, ar9271Modes_9271_ANI_reg,
|
||||
ARRAY_SIZE(ar9271Modes_9271_ANI_reg), 6);
|
||||
INIT_INI_ARRAY(&ah->iniModes_high_power_tx_gain_9271,
|
||||
ar9271Modes_high_power_tx_gain_9271,
|
||||
ARRAY_SIZE(ar9271Modes_high_power_tx_gain_9271), 6);
|
||||
INIT_INI_ARRAY(&ah->iniModes_normal_power_tx_gain_9271,
|
||||
ar9271Modes_normal_power_tx_gain_9271,
|
||||
ARRAY_SIZE(ar9271Modes_normal_power_tx_gain_9271), 6);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -990,22 +1006,6 @@ static void ath9k_hw_init_qos(struct ath_hw *ah)
|
|||
REG_WRITE(ah, AR_TXOP_12_15, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
static void ath9k_hw_change_target_baud(struct ath_hw *ah, u32 freq, u32 baud)
|
||||
{
|
||||
u32 lcr;
|
||||
u32 baud_divider = freq * 1000 * 1000 / 16 / baud;
|
||||
|
||||
lcr = REG_READ(ah , 0x5100c);
|
||||
lcr |= 0x80;
|
||||
|
||||
REG_WRITE(ah, 0x5100c, lcr);
|
||||
REG_WRITE(ah, 0x51004, (baud_divider >> 8));
|
||||
REG_WRITE(ah, 0x51000, (baud_divider & 0xff));
|
||||
|
||||
lcr &= ~0x80;
|
||||
REG_WRITE(ah, 0x5100c, lcr);
|
||||
}
|
||||
|
||||
static void ath9k_hw_init_pll(struct ath_hw *ah,
|
||||
struct ath9k_channel *chan)
|
||||
{
|
||||
|
@ -1071,22 +1071,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
|
|||
|
||||
/* Switch the core clock for ar9271 to 117Mhz */
|
||||
if (AR_SREV_9271(ah)) {
|
||||
if ((pll == 0x142c) || (pll == 0x2850) ) {
|
||||
udelay(500);
|
||||
/* set CLKOBS to output AHB clock */
|
||||
REG_WRITE(ah, 0x7020, 0xe);
|
||||
/*
|
||||
* 0x304: 117Mhz, ahb_ratio: 1x1
|
||||
* 0x306: 40Mhz, ahb_ratio: 1x1
|
||||
*/
|
||||
REG_WRITE(ah, 0x50040, 0x304);
|
||||
/*
|
||||
* makes adjustments for the baud dividor to keep the
|
||||
* targetted baud rate based on the used core clock.
|
||||
*/
|
||||
ath9k_hw_change_target_baud(ah, AR9271_CORE_CLOCK,
|
||||
AR9271_TARGET_BAUD_RATE);
|
||||
}
|
||||
udelay(500);
|
||||
REG_WRITE(ah, 0x50040, 0x304);
|
||||
}
|
||||
|
||||
udelay(RTC_PLL_SETTLE_DELAY);
|
||||
|
@ -1241,7 +1227,7 @@ void ath9k_hw_deinit(struct ath_hw *ah)
|
|||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
|
||||
if (common->state <= ATH_HW_INITIALIZED)
|
||||
if (common->state < ATH_HW_INITIALIZED)
|
||||
goto free_hw;
|
||||
|
||||
if (!AR_SREV_9100(ah))
|
||||
|
@ -1252,8 +1238,6 @@ void ath9k_hw_deinit(struct ath_hw *ah)
|
|||
free_hw:
|
||||
if (!AR_SREV_9280_10_OR_LATER(ah))
|
||||
ath9k_hw_rf_free_ext_banks(ah);
|
||||
kfree(ah);
|
||||
ah = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_hw_deinit);
|
||||
|
||||
|
@ -1266,26 +1250,6 @@ static void ath9k_hw_override_ini(struct ath_hw *ah,
|
|||
{
|
||||
u32 val;
|
||||
|
||||
if (AR_SREV_9271(ah)) {
|
||||
/*
|
||||
* Enable spectral scan to solution for issues with stuck
|
||||
* beacons on AR9271 1.0. The beacon stuck issue is not seeon on
|
||||
* AR9271 1.1
|
||||
*/
|
||||
if (AR_SREV_9271_10(ah)) {
|
||||
val = REG_READ(ah, AR_PHY_SPECTRAL_SCAN) |
|
||||
AR_PHY_SPECTRAL_SCAN_ENABLE;
|
||||
REG_WRITE(ah, AR_PHY_SPECTRAL_SCAN, val);
|
||||
}
|
||||
else if (AR_SREV_9271_11(ah))
|
||||
/*
|
||||
* change AR_PHY_RF_CTL3 setting to fix MAC issue
|
||||
* present on AR9271 1.1
|
||||
*/
|
||||
REG_WRITE(ah, AR_PHY_RF_CTL3, 0x3a020001);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the RX_ABORT and RX_DIS and clear if off only after
|
||||
* RXE is set for MAC. This prevents frames with corrupted
|
||||
|
@ -1294,8 +1258,10 @@ static void ath9k_hw_override_ini(struct ath_hw *ah,
|
|||
REG_SET_BIT(ah, AR_DIAG_SW, (AR_DIAG_RX_DIS | AR_DIAG_RX_ABORT));
|
||||
|
||||
if (AR_SREV_9280_10_OR_LATER(ah)) {
|
||||
val = REG_READ(ah, AR_PCU_MISC_MODE2) &
|
||||
(~AR_PCU_MISC_MODE2_HWWAR1);
|
||||
val = REG_READ(ah, AR_PCU_MISC_MODE2);
|
||||
|
||||
if (!AR_SREV_9271(ah))
|
||||
val &= ~AR_PCU_MISC_MODE2_HWWAR1;
|
||||
|
||||
if (AR_SREV_9287_10_OR_LATER(ah))
|
||||
val = val & (~AR_PCU_MISC_MODE2_HWWAR2);
|
||||
|
@ -1439,7 +1405,10 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set correct baseband to analog shift setting to access analog chips */
|
||||
REG_WRITE(ah, AR_PHY(0), 0x00000007);
|
||||
|
||||
/* Write ADDAC shifts */
|
||||
REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO);
|
||||
ah->eep_ops->set_addac(ah, chan);
|
||||
|
||||
|
@ -1451,9 +1420,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
|
|||
sizeof(u32) * ah->iniAddac.ia_rows *
|
||||
ah->iniAddac.ia_columns;
|
||||
|
||||
/* For AR5416 2.0/2.1 */
|
||||
memcpy(ah->addac5416_21,
|
||||
ah->iniAddac.ia_array, addacSize);
|
||||
|
||||
/* override CLKDRV value at [row, column] = [31, 1] */
|
||||
(ah->addac5416_21)[31 * ah->iniAddac.ia_columns + 1] = 0;
|
||||
|
||||
temp.ia_array = ah->addac5416_21;
|
||||
|
@ -1485,6 +1456,11 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
|
|||
AR_SREV_9287_10_OR_LATER(ah))
|
||||
REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
|
||||
|
||||
if (AR_SREV_9271_10(ah))
|
||||
REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only,
|
||||
modesIndex, regWrites);
|
||||
|
||||
/* Write common array parameters */
|
||||
for (i = 0; i < ah->iniCommon.ia_rows; i++) {
|
||||
u32 reg = INI_RA(&ah->iniCommon, i, 0);
|
||||
u32 val = INI_RA(&ah->iniCommon, i, 1);
|
||||
|
@ -1499,11 +1475,16 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
|
|||
DO_DELAY(regWrites);
|
||||
}
|
||||
|
||||
ath9k_hw_write_regs(ah, freqIndex, regWrites);
|
||||
if (AR_SREV_9271(ah)) {
|
||||
if (ah->eep_ops->get_eeprom(ah, EEP_TXGAIN_TYPE) == 1)
|
||||
REG_WRITE_ARRAY(&ah->iniModes_high_power_tx_gain_9271,
|
||||
modesIndex, regWrites);
|
||||
else
|
||||
REG_WRITE_ARRAY(&ah->iniModes_normal_power_tx_gain_9271,
|
||||
modesIndex, regWrites);
|
||||
}
|
||||
|
||||
if (AR_SREV_9271_10(ah))
|
||||
REG_WRITE_ARRAY(&ah->iniModes_9271_1_0_only,
|
||||
modesIndex, regWrites);
|
||||
ath9k_hw_write_regs(ah, freqIndex, regWrites);
|
||||
|
||||
if (AR_SREV_9280_20(ah) && IS_CHAN_A_5MHZ_SPACED(chan)) {
|
||||
REG_WRITE_ARRAY(&ah->iniModesAdditional, modesIndex,
|
||||
|
@ -1517,6 +1498,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
|
|||
if (OLC_FOR_AR9280_20_LATER)
|
||||
ath9k_olc_init(ah);
|
||||
|
||||
/* Set TX power */
|
||||
ah->eep_ops->set_txpower(ah, chan,
|
||||
ath9k_regd_get_ctl(regulatory, chan),
|
||||
channel->max_antenna_gain * 2,
|
||||
|
@ -1524,6 +1506,7 @@ static int ath9k_hw_process_ini(struct ath_hw *ah,
|
|||
min((u32) MAX_RATE_POWER,
|
||||
(u32) regulatory->power_limit));
|
||||
|
||||
/* Write analog registers */
|
||||
if (!ath9k_hw_set_rf_regs(ah, chan, freqIndex)) {
|
||||
ath_print(ath9k_hw_common(ah), ATH_DBG_FATAL,
|
||||
"ar5416SetRfRegs failed\n");
|
||||
|
@ -1966,6 +1949,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
|
|||
|
||||
ath9k_hw_mark_phy_inactive(ah);
|
||||
|
||||
/* Only required on the first reset */
|
||||
if (AR_SREV_9271(ah) && ah->htc_reset_init) {
|
||||
REG_WRITE(ah,
|
||||
AR9271_RESET_POWER_DOWN_CONTROL,
|
||||
|
@ -1978,6 +1962,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Only required on the first reset */
|
||||
if (AR_SREV_9271(ah) && ah->htc_reset_init) {
|
||||
ah->htc_reset_init = false;
|
||||
REG_WRITE(ah,
|
||||
|
@ -2438,7 +2423,7 @@ static void ath9k_set_power_sleep(struct ath_hw *ah, int setChip)
|
|||
if (!AR_SREV_9100(ah))
|
||||
REG_WRITE(ah, AR_RC, AR_RC_AHB | AR_RC_HOSTIF);
|
||||
|
||||
if(!AR_SREV_5416(ah))
|
||||
if (!AR_SREV_5416(ah) && !AR_SREV_9271(ah))
|
||||
REG_CLR_BIT(ah, (AR_RTC_RESET),
|
||||
AR_RTC_RESET_EN);
|
||||
}
|
||||
|
@ -3216,7 +3201,9 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
|
|||
else
|
||||
pCap->tx_triglevel_max = MAX_TX_FIFO_THRESHOLD;
|
||||
|
||||
if (AR_SREV_9285_10_OR_LATER(ah))
|
||||
if (AR_SREV_9271(ah))
|
||||
pCap->num_gpio_pins = AR9271_NUM_GPIO;
|
||||
else if (AR_SREV_9285_10_OR_LATER(ah))
|
||||
pCap->num_gpio_pins = AR9285_NUM_GPIO;
|
||||
else if (AR_SREV_9280_10_OR_LATER(ah))
|
||||
pCap->num_gpio_pins = AR928X_NUM_GPIO;
|
||||
|
@ -3452,7 +3439,9 @@ u32 ath9k_hw_gpio_get(struct ath_hw *ah, u32 gpio)
|
|||
if (gpio >= ah->caps.num_gpio_pins)
|
||||
return 0xffffffff;
|
||||
|
||||
if (AR_SREV_9287_10_OR_LATER(ah))
|
||||
if (AR_SREV_9271(ah))
|
||||
return MS_REG_READ(AR9271, gpio) != 0;
|
||||
else if (AR_SREV_9287_10_OR_LATER(ah))
|
||||
return MS_REG_READ(AR9287, gpio) != 0;
|
||||
else if (AR_SREV_9285_10_OR_LATER(ah))
|
||||
return MS_REG_READ(AR9285, gpio) != 0;
|
||||
|
@ -3481,6 +3470,9 @@ EXPORT_SYMBOL(ath9k_hw_cfg_output);
|
|||
|
||||
void ath9k_hw_set_gpio(struct ath_hw *ah, u32 gpio, u32 val)
|
||||
{
|
||||
if (AR_SREV_9271(ah))
|
||||
val = ~val;
|
||||
|
||||
REG_RMW(ah, AR_GPIO_IN_OUT, ((val & 1) << gpio),
|
||||
AR_GPIO_BIT(gpio));
|
||||
}
|
||||
|
@ -3865,6 +3857,16 @@ void ath_gen_timer_isr(struct ath_hw *ah)
|
|||
}
|
||||
EXPORT_SYMBOL(ath_gen_timer_isr);
|
||||
|
||||
/********/
|
||||
/* HTC */
|
||||
/********/
|
||||
|
||||
void ath9k_hw_htc_resetinit(struct ath_hw *ah)
|
||||
{
|
||||
ah->htc_reset_init = true;
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_hw_htc_resetinit);
|
||||
|
||||
static struct {
|
||||
u32 version;
|
||||
const char * name;
|
||||
|
|
|
@ -599,6 +599,11 @@ struct ath_hw {
|
|||
struct ar5416IniArray iniModes_9271_1_0_only;
|
||||
struct ar5416IniArray iniCckfirNormal;
|
||||
struct ar5416IniArray iniCckfirJapan2484;
|
||||
struct ar5416IniArray iniCommon_normal_cck_fir_coeff_9271;
|
||||
struct ar5416IniArray iniCommon_japan_2484_cck_fir_coeff_9271;
|
||||
struct ar5416IniArray iniModes_9271_ANI_reg;
|
||||
struct ar5416IniArray iniModes_high_power_tx_gain_9271;
|
||||
struct ar5416IniArray iniModes_normal_power_tx_gain_9271;
|
||||
|
||||
u32 intr_gen_timer_trigger;
|
||||
u32 intr_gen_timer_thresh;
|
||||
|
@ -702,6 +707,9 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah);
|
|||
|
||||
void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len);
|
||||
|
||||
/* HTC */
|
||||
void ath9k_hw_htc_resetinit(struct ath_hw *ah);
|
||||
|
||||
#define ATH_PCIE_CAP_LINK_CTRL 0x70
|
||||
#define ATH_PCIE_CAP_LINK_L0S 1
|
||||
#define ATH_PCIE_CAP_LINK_L1 2
|
||||
|
|
|
@ -758,6 +758,9 @@ static void ath9k_deinit_softc(struct ath_softc *sc)
|
|||
|
||||
tasklet_kill(&sc->intr_tq);
|
||||
tasklet_kill(&sc->bcon_tasklet);
|
||||
|
||||
kfree(sc->sc_ah);
|
||||
sc->sc_ah = NULL;
|
||||
}
|
||||
|
||||
void ath9k_deinit_device(struct ath_softc *sc)
|
||||
|
|
|
@ -6441,7 +6441,7 @@ static const u_int32_t ar9271Modes_9271[][6] = {
|
|||
{ 0x00009a44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 },
|
||||
{ 0x00009a48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 },
|
||||
{ 0x00009a4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 },
|
||||
{ 0x00009a50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 },
|
||||
{ 0x00009a50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000 },
|
||||
{ 0x00009a54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 },
|
||||
{ 0x00009a58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 },
|
||||
{ 0x00009a5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 },
|
||||
|
@ -6455,8 +6455,8 @@ static const u_int32_t ar9271Modes_9271[][6] = {
|
|||
{ 0x00009a7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 },
|
||||
{ 0x00009a80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 },
|
||||
{ 0x00009a84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 },
|
||||
{ 0x00009a88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 },
|
||||
{ 0x00009a8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
|
||||
{ 0x00009a88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000 },
|
||||
{ 0x00009a8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 },
|
||||
{ 0x00009a90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
|
||||
{ 0x00009a94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 },
|
||||
{ 0x00009a98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 },
|
||||
|
@ -6569,7 +6569,7 @@ static const u_int32_t ar9271Modes_9271[][6] = {
|
|||
{ 0x0000aa44, 0x00000000, 0x00000000, 0x000581a8, 0x000581a8, 0x00000000 },
|
||||
{ 0x0000aa48, 0x00000000, 0x00000000, 0x00058284, 0x00058284, 0x00000000 },
|
||||
{ 0x0000aa4c, 0x00000000, 0x00000000, 0x00058288, 0x00058288, 0x00000000 },
|
||||
{ 0x0000aa50, 0x00000000, 0x00000000, 0x00058220, 0x00058220, 0x00000000 },
|
||||
{ 0x0000aa50, 0x00000000, 0x00000000, 0x00058224, 0x00058224, 0x00000000 },
|
||||
{ 0x0000aa54, 0x00000000, 0x00000000, 0x00058290, 0x00058290, 0x00000000 },
|
||||
{ 0x0000aa58, 0x00000000, 0x00000000, 0x00058300, 0x00058300, 0x00000000 },
|
||||
{ 0x0000aa5c, 0x00000000, 0x00000000, 0x00058304, 0x00058304, 0x00000000 },
|
||||
|
@ -6583,8 +6583,8 @@ static const u_int32_t ar9271Modes_9271[][6] = {
|
|||
{ 0x0000aa7c, 0x00000000, 0x00000000, 0x0006870c, 0x0006870c, 0x00000000 },
|
||||
{ 0x0000aa80, 0x00000000, 0x00000000, 0x00068780, 0x00068780, 0x00000000 },
|
||||
{ 0x0000aa84, 0x00000000, 0x00000000, 0x00068784, 0x00068784, 0x00000000 },
|
||||
{ 0x0000aa88, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 },
|
||||
{ 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
|
||||
{ 0x0000aa88, 0x00000000, 0x00000000, 0x00078b00, 0x00078b00, 0x00000000 },
|
||||
{ 0x0000aa8c, 0x00000000, 0x00000000, 0x00078b04, 0x00078b04, 0x00000000 },
|
||||
{ 0x0000aa90, 0x00000000, 0x00000000, 0x00078b08, 0x00078b08, 0x00000000 },
|
||||
{ 0x0000aa94, 0x00000000, 0x00000000, 0x00078b0c, 0x00078b0c, 0x00000000 },
|
||||
{ 0x0000aa98, 0x00000000, 0x00000000, 0x00078b80, 0x00078b80, 0x00000000 },
|
||||
|
@ -6683,25 +6683,6 @@ static const u_int32_t ar9271Modes_9271[][6] = {
|
|||
{ 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a },
|
||||
{ 0x0000a230, 0x00000000, 0x00000000, 0x00000210, 0x00000108, 0x00000000 },
|
||||
{ 0x0000a250, 0x0004f000, 0x0004f000, 0x0004a000, 0x0004a000, 0x0004a000 },
|
||||
{ 0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a218652, 0x0a218652, 0x0a22a652 },
|
||||
{ 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000 },
|
||||
{ 0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000 },
|
||||
{ 0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000 },
|
||||
{ 0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610, 0x00000000 },
|
||||
{ 0x0000a314, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0, 0x00000000 },
|
||||
{ 0x0000a318, 0x00000000, 0x00000000, 0x00039758, 0x00039758, 0x00000000 },
|
||||
{ 0x0000a31c, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759, 0x00000000 },
|
||||
{ 0x0000a320, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a, 0x00000000 },
|
||||
{ 0x0000a324, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c, 0x00000000 },
|
||||
{ 0x0000a328, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e, 0x00000000 },
|
||||
{ 0x0000a32c, 0x00000000, 0x00000000, 0x0004979f, 0x0004979f, 0x00000000 },
|
||||
{ 0x0000a330, 0x00000000, 0x00000000, 0x0004d7df, 0x0004d7df, 0x00000000 },
|
||||
{ 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 },
|
||||
{ 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 },
|
||||
{ 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 },
|
||||
{ 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a358, 0x7999aa02, 0x7999aa02, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e },
|
||||
};
|
||||
|
||||
|
@ -6879,7 +6860,7 @@ static const u_int32_t ar9271Common_9271[][2] = {
|
|||
{ 0x00008258, 0x00000000 },
|
||||
{ 0x0000825c, 0x400000ff },
|
||||
{ 0x00008260, 0x00080922 },
|
||||
{ 0x00008264, 0x88a00010 },
|
||||
{ 0x00008264, 0xa8a00010 },
|
||||
{ 0x00008270, 0x00000000 },
|
||||
{ 0x00008274, 0x40000000 },
|
||||
{ 0x00008278, 0x003e4180 },
|
||||
|
@ -6910,13 +6891,10 @@ static const u_int32_t ar9271Common_9271[][2] = {
|
|||
{ 0x00007810, 0x71c0d388 },
|
||||
{ 0x00007814, 0x924934a8 },
|
||||
{ 0x0000781c, 0x00000000 },
|
||||
{ 0x00007820, 0x00000c04 },
|
||||
{ 0x00007824, 0x00d8abff },
|
||||
{ 0x00007828, 0x66964300 },
|
||||
{ 0x0000782c, 0x8db6d961 },
|
||||
{ 0x00007830, 0x8db6d96c },
|
||||
{ 0x00007834, 0x6140008b },
|
||||
{ 0x00007838, 0x00000029 },
|
||||
{ 0x0000783c, 0x72ee0a72 },
|
||||
{ 0x00007840, 0xbbfffffc },
|
||||
{ 0x00007844, 0x000c0db6 },
|
||||
|
@ -6929,7 +6907,6 @@ static const u_int32_t ar9271Common_9271[][2] = {
|
|||
{ 0x00007860, 0x21084210 },
|
||||
{ 0x00007864, 0xf7d7ffde },
|
||||
{ 0x00007868, 0xc2034080 },
|
||||
{ 0x0000786c, 0x48609eb4 },
|
||||
{ 0x00007870, 0x10142c00 },
|
||||
{ 0x00009808, 0x00000000 },
|
||||
{ 0x0000980c, 0xafe68e30 },
|
||||
|
@ -6982,9 +6959,6 @@ static const u_int32_t ar9271Common_9271[][2] = {
|
|||
{ 0x000099e8, 0x3c466478 },
|
||||
{ 0x000099ec, 0x0cc80caa },
|
||||
{ 0x000099f0, 0x00000000 },
|
||||
{ 0x0000a1f4, 0x00000000 },
|
||||
{ 0x0000a1f8, 0x71733d01 },
|
||||
{ 0x0000a1fc, 0xd0ad5c12 },
|
||||
{ 0x0000a208, 0x803e68c8 },
|
||||
{ 0x0000a210, 0x4080a333 },
|
||||
{ 0x0000a214, 0x00206c10 },
|
||||
|
@ -7004,13 +6978,9 @@ static const u_int32_t ar9271Common_9271[][2] = {
|
|||
{ 0x0000a260, 0xdfa90f01 },
|
||||
{ 0x0000a268, 0x00000000 },
|
||||
{ 0x0000a26c, 0x0ebae9e6 },
|
||||
{ 0x0000a278, 0x3bdef7bd },
|
||||
{ 0x0000a27c, 0x050e83bd },
|
||||
{ 0x0000a388, 0x0c000000 },
|
||||
{ 0x0000a38c, 0x20202020 },
|
||||
{ 0x0000a390, 0x20202020 },
|
||||
{ 0x0000a394, 0x3bdef7bd },
|
||||
{ 0x0000a398, 0x000003bd },
|
||||
{ 0x0000a39c, 0x00000001 },
|
||||
{ 0x0000a3a0, 0x00000000 },
|
||||
{ 0x0000a3a4, 0x00000000 },
|
||||
|
@ -7025,8 +6995,6 @@ static const u_int32_t ar9271Common_9271[][2] = {
|
|||
{ 0x0000a3cc, 0x20202020 },
|
||||
{ 0x0000a3d0, 0x20202020 },
|
||||
{ 0x0000a3d4, 0x20202020 },
|
||||
{ 0x0000a3dc, 0x3bdef7bd },
|
||||
{ 0x0000a3e0, 0x000003bd },
|
||||
{ 0x0000a3e4, 0x00000000 },
|
||||
{ 0x0000a3e8, 0x18c43433 },
|
||||
{ 0x0000a3ec, 0x00f70081 },
|
||||
|
@ -7046,7 +7014,102 @@ static const u_int32_t ar9271Common_9271[][2] = {
|
|||
{ 0x0000d384, 0xf3307ff0 },
|
||||
};
|
||||
|
||||
static const u_int32_t ar9271Common_normal_cck_fir_coeff_9271[][2] = {
|
||||
{ 0x0000a1f4, 0x00fffeff },
|
||||
{ 0x0000a1f8, 0x00f5f9ff },
|
||||
{ 0x0000a1fc, 0xb79f6427 },
|
||||
};
|
||||
|
||||
static const u_int32_t ar9271Common_japan_2484_cck_fir_coeff_9271[][2] = {
|
||||
{ 0x0000a1f4, 0x00000000 },
|
||||
{ 0x0000a1f8, 0xefff0301 },
|
||||
{ 0x0000a1fc, 0xca9228ee },
|
||||
};
|
||||
|
||||
static const u_int32_t ar9271Modes_9271_1_0_only[][6] = {
|
||||
{ 0x00009910, 0x30002311, 0x30002311, 0x30002311, 0x30002311, 0x30002311 },
|
||||
{ 0x00009828, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001, 0x0a020001 },
|
||||
};
|
||||
|
||||
static const u_int32_t ar9271Modes_9271_ANI_reg[][6] = {
|
||||
{ 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 },
|
||||
{ 0x0000985c, 0x3139605e, 0x3139605e, 0x3137605e, 0x3137605e, 0x3139605e },
|
||||
{ 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e },
|
||||
{ 0x0000986c, 0x06903881, 0x06903881, 0x06903881, 0x06903881, 0x06903881 },
|
||||
{ 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 },
|
||||
{ 0x0000a208, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8, 0x803e68c8 },
|
||||
{ 0x00009924, 0xd00a8007, 0xd00a8007, 0xd00a800d, 0xd00a800d, 0xd00a800d },
|
||||
{ 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 },
|
||||
};
|
||||
|
||||
static const u_int32_t ar9271Modes_normal_power_tx_gain_9271[][6] = {
|
||||
{ 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },
|
||||
{ 0x0000a304, 0x00000000, 0x00000000, 0x00009200, 0x00009200, 0x00000000 },
|
||||
{ 0x0000a308, 0x00000000, 0x00000000, 0x00010208, 0x00010208, 0x00000000 },
|
||||
{ 0x0000a30c, 0x00000000, 0x00000000, 0x00019608, 0x00019608, 0x00000000 },
|
||||
{ 0x0000a310, 0x00000000, 0x00000000, 0x0001e610, 0x0001e610, 0x00000000 },
|
||||
{ 0x0000a314, 0x00000000, 0x00000000, 0x0002d6d0, 0x0002d6d0, 0x00000000 },
|
||||
{ 0x0000a318, 0x00000000, 0x00000000, 0x00039758, 0x00039758, 0x00000000 },
|
||||
{ 0x0000a31c, 0x00000000, 0x00000000, 0x0003b759, 0x0003b759, 0x00000000 },
|
||||
{ 0x0000a320, 0x00000000, 0x00000000, 0x0003d75a, 0x0003d75a, 0x00000000 },
|
||||
{ 0x0000a324, 0x00000000, 0x00000000, 0x0004175c, 0x0004175c, 0x00000000 },
|
||||
{ 0x0000a328, 0x00000000, 0x00000000, 0x0004575e, 0x0004575e, 0x00000000 },
|
||||
{ 0x0000a32c, 0x00000000, 0x00000000, 0x0004979f, 0x0004979f, 0x00000000 },
|
||||
{ 0x0000a330, 0x00000000, 0x00000000, 0x0004d7df, 0x0004d7df, 0x00000000 },
|
||||
{ 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 },
|
||||
{ 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 },
|
||||
{ 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 },
|
||||
{ 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x00007838, 0x00000029, 0x00000029, 0x00000029, 0x00000029, 0x00000029 },
|
||||
{ 0x00007824, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff },
|
||||
{ 0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4 },
|
||||
{ 0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04 },
|
||||
{ 0x0000a274, 0x0a21c652, 0x0a21c652, 0x0a218652, 0x0a218652, 0x0a22a652 },
|
||||
{ 0x0000a278, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd },
|
||||
{ 0x0000a27c, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd },
|
||||
{ 0x0000a394, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd },
|
||||
{ 0x0000a398, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd },
|
||||
{ 0x0000a3dc, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd },
|
||||
{ 0x0000a3e0, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd },
|
||||
};
|
||||
|
||||
static const u_int32_t ar9271Modes_high_power_tx_gain_9271[][6] = {
|
||||
{ 0x0000a300, 0x00000000, 0x00000000, 0x00010000, 0x00010000, 0x00000000 },
|
||||
{ 0x0000a304, 0x00000000, 0x00000000, 0x00016200, 0x00016200, 0x00000000 },
|
||||
{ 0x0000a308, 0x00000000, 0x00000000, 0x00018201, 0x00018201, 0x00000000 },
|
||||
{ 0x0000a30c, 0x00000000, 0x00000000, 0x0001b240, 0x0001b240, 0x00000000 },
|
||||
{ 0x0000a310, 0x00000000, 0x00000000, 0x0001d241, 0x0001d241, 0x00000000 },
|
||||
{ 0x0000a314, 0x00000000, 0x00000000, 0x0001f600, 0x0001f600, 0x00000000 },
|
||||
{ 0x0000a318, 0x00000000, 0x00000000, 0x00022800, 0x00022800, 0x00000000 },
|
||||
{ 0x0000a31c, 0x00000000, 0x00000000, 0x00026802, 0x00026802, 0x00000000 },
|
||||
{ 0x0000a320, 0x00000000, 0x00000000, 0x0002b805, 0x0002b805, 0x00000000 },
|
||||
{ 0x0000a324, 0x00000000, 0x00000000, 0x0002ea41, 0x0002ea41, 0x00000000 },
|
||||
{ 0x0000a328, 0x00000000, 0x00000000, 0x00038b00, 0x00038b00, 0x00000000 },
|
||||
{ 0x0000a32c, 0x00000000, 0x00000000, 0x0003ab40, 0x0003ab40, 0x00000000 },
|
||||
{ 0x0000a330, 0x00000000, 0x00000000, 0x0003cd80, 0x0003cd80, 0x00000000 },
|
||||
{ 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x00000000 },
|
||||
{ 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x00000000 },
|
||||
{ 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x00000000 },
|
||||
{ 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x00000000 },
|
||||
{ 0x00007838, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b },
|
||||
{ 0x00007824, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff },
|
||||
{ 0x0000786c, 0x08609eb6, 0x08609eb6, 0x08609eba, 0x08609eba, 0x08609eb6 },
|
||||
{ 0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00 },
|
||||
{ 0x0000a274, 0x0a22a652, 0x0a22a652, 0x0a212652, 0x0a212652, 0x0a22a652 },
|
||||
{ 0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 },
|
||||
{ 0x0000a27c, 0x05018063, 0x05038063, 0x05018063, 0x05018063, 0x05018063 },
|
||||
{ 0x0000a394, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63 },
|
||||
{ 0x0000a398, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063 },
|
||||
{ 0x0000a3dc, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63 },
|
||||
{ 0x0000a3e0, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063 },
|
||||
};
|
||||
|
|
|
@ -351,7 +351,7 @@ void ath9k_hw_set11n_txdesc(struct ath_hw *ah, struct ath_desc *ds,
|
|||
|
||||
ads->ds_ctl6 = SM(keyType, AR_EncrType);
|
||||
|
||||
if (AR_SREV_9285(ah)) {
|
||||
if (AR_SREV_9285(ah) || AR_SREV_9271(ah)) {
|
||||
ads->ds_ctl8 = 0;
|
||||
ads->ds_ctl9 = 0;
|
||||
ads->ds_ctl10 = 0;
|
||||
|
|
|
@ -150,6 +150,32 @@ struct ath_rx_status {
|
|||
u32 evm2;
|
||||
};
|
||||
|
||||
struct ath_htc_rx_status {
|
||||
u64 rs_tstamp;
|
||||
u16 rs_datalen;
|
||||
u8 rs_status;
|
||||
u8 rs_phyerr;
|
||||
int8_t rs_rssi;
|
||||
int8_t rs_rssi_ctl0;
|
||||
int8_t rs_rssi_ctl1;
|
||||
int8_t rs_rssi_ctl2;
|
||||
int8_t rs_rssi_ext0;
|
||||
int8_t rs_rssi_ext1;
|
||||
int8_t rs_rssi_ext2;
|
||||
u8 rs_keyix;
|
||||
u8 rs_rate;
|
||||
u8 rs_antenna;
|
||||
u8 rs_more;
|
||||
u8 rs_isaggr;
|
||||
u8 rs_moreaggr;
|
||||
u8 rs_num_delims;
|
||||
u8 rs_flags;
|
||||
u8 rs_dummy;
|
||||
u32 evm0;
|
||||
u32 evm1;
|
||||
u32 evm2;
|
||||
};
|
||||
|
||||
#define ATH9K_RXERR_CRC 0x01
|
||||
#define ATH9K_RXERR_PHY 0x02
|
||||
#define ATH9K_RXERR_FIFO 0x04
|
||||
|
|
|
@ -110,8 +110,8 @@ struct ath_rate_table {
|
|||
int rate_cnt;
|
||||
int mcs_start;
|
||||
struct {
|
||||
int valid;
|
||||
int valid_single_stream;
|
||||
u8 valid;
|
||||
u8 valid_single_stream;
|
||||
u8 phy;
|
||||
u32 ratekbps;
|
||||
u32 user_ratekbps;
|
||||
|
|
|
@ -940,6 +940,7 @@ enum {
|
|||
#define AR928X_NUM_GPIO 10
|
||||
#define AR9285_NUM_GPIO 12
|
||||
#define AR9287_NUM_GPIO 11
|
||||
#define AR9271_NUM_GPIO 16
|
||||
|
||||
#define AR_GPIO_IN_OUT 0x4048
|
||||
#define AR_GPIO_IN_VAL 0x0FFFC000
|
||||
|
@ -950,6 +951,8 @@ enum {
|
|||
#define AR9285_GPIO_IN_VAL_S 12
|
||||
#define AR9287_GPIO_IN_VAL 0x003FF800
|
||||
#define AR9287_GPIO_IN_VAL_S 11
|
||||
#define AR9271_GPIO_IN_VAL 0xFFFF0000
|
||||
#define AR9271_GPIO_IN_VAL_S 16
|
||||
|
||||
#define AR_GPIO_OE_OUT 0x404c
|
||||
#define AR_GPIO_OE_OUT_DRV 0x3
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -59,6 +59,7 @@ enum ATH_DEBUG {
|
|||
ATH_DBG_PS = 0x00000800,
|
||||
ATH_DBG_HWTIMER = 0x00001000,
|
||||
ATH_DBG_BTCOEX = 0x00002000,
|
||||
ATH_DBG_WMI = 0x00004000,
|
||||
ATH_DBG_ANY = 0xffffffff
|
||||
};
|
||||
|
||||
|
|
|
@ -4348,11 +4348,10 @@ static int b43_wireless_core_init(struct b43_wldev *dev)
|
|||
b43_set_phytxctl_defaults(dev);
|
||||
|
||||
/* Minimum Contention Window */
|
||||
if (phy->type == B43_PHYTYPE_B) {
|
||||
if (phy->type == B43_PHYTYPE_B)
|
||||
b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0x1F);
|
||||
} else {
|
||||
else
|
||||
b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MINCONT, 0xF);
|
||||
}
|
||||
/* Maximum Contention Window */
|
||||
b43_shm_write16(dev, B43_SHM_SCRATCH, B43_SHM_SC_MAXCONT, 0x3FF);
|
||||
|
||||
|
|
|
@ -9995,49 +9995,48 @@ static int ipw_wx_sw_reset(struct net_device *dev,
|
|||
}
|
||||
|
||||
/* Rebase the WE IOCTLs to zero for the handler array */
|
||||
#define IW_IOCTL(x) [(x)-SIOCSIWCOMMIT]
|
||||
static iw_handler ipw_wx_handlers[] = {
|
||||
IW_IOCTL(SIOCGIWNAME) = (iw_handler) cfg80211_wext_giwname,
|
||||
IW_IOCTL(SIOCSIWFREQ) = ipw_wx_set_freq,
|
||||
IW_IOCTL(SIOCGIWFREQ) = ipw_wx_get_freq,
|
||||
IW_IOCTL(SIOCSIWMODE) = ipw_wx_set_mode,
|
||||
IW_IOCTL(SIOCGIWMODE) = ipw_wx_get_mode,
|
||||
IW_IOCTL(SIOCSIWSENS) = ipw_wx_set_sens,
|
||||
IW_IOCTL(SIOCGIWSENS) = ipw_wx_get_sens,
|
||||
IW_IOCTL(SIOCGIWRANGE) = ipw_wx_get_range,
|
||||
IW_IOCTL(SIOCSIWAP) = ipw_wx_set_wap,
|
||||
IW_IOCTL(SIOCGIWAP) = ipw_wx_get_wap,
|
||||
IW_IOCTL(SIOCSIWSCAN) = ipw_wx_set_scan,
|
||||
IW_IOCTL(SIOCGIWSCAN) = ipw_wx_get_scan,
|
||||
IW_IOCTL(SIOCSIWESSID) = ipw_wx_set_essid,
|
||||
IW_IOCTL(SIOCGIWESSID) = ipw_wx_get_essid,
|
||||
IW_IOCTL(SIOCSIWNICKN) = ipw_wx_set_nick,
|
||||
IW_IOCTL(SIOCGIWNICKN) = ipw_wx_get_nick,
|
||||
IW_IOCTL(SIOCSIWRATE) = ipw_wx_set_rate,
|
||||
IW_IOCTL(SIOCGIWRATE) = ipw_wx_get_rate,
|
||||
IW_IOCTL(SIOCSIWRTS) = ipw_wx_set_rts,
|
||||
IW_IOCTL(SIOCGIWRTS) = ipw_wx_get_rts,
|
||||
IW_IOCTL(SIOCSIWFRAG) = ipw_wx_set_frag,
|
||||
IW_IOCTL(SIOCGIWFRAG) = ipw_wx_get_frag,
|
||||
IW_IOCTL(SIOCSIWTXPOW) = ipw_wx_set_txpow,
|
||||
IW_IOCTL(SIOCGIWTXPOW) = ipw_wx_get_txpow,
|
||||
IW_IOCTL(SIOCSIWRETRY) = ipw_wx_set_retry,
|
||||
IW_IOCTL(SIOCGIWRETRY) = ipw_wx_get_retry,
|
||||
IW_IOCTL(SIOCSIWENCODE) = ipw_wx_set_encode,
|
||||
IW_IOCTL(SIOCGIWENCODE) = ipw_wx_get_encode,
|
||||
IW_IOCTL(SIOCSIWPOWER) = ipw_wx_set_power,
|
||||
IW_IOCTL(SIOCGIWPOWER) = ipw_wx_get_power,
|
||||
IW_IOCTL(SIOCSIWSPY) = iw_handler_set_spy,
|
||||
IW_IOCTL(SIOCGIWSPY) = iw_handler_get_spy,
|
||||
IW_IOCTL(SIOCSIWTHRSPY) = iw_handler_set_thrspy,
|
||||
IW_IOCTL(SIOCGIWTHRSPY) = iw_handler_get_thrspy,
|
||||
IW_IOCTL(SIOCSIWGENIE) = ipw_wx_set_genie,
|
||||
IW_IOCTL(SIOCGIWGENIE) = ipw_wx_get_genie,
|
||||
IW_IOCTL(SIOCSIWMLME) = ipw_wx_set_mlme,
|
||||
IW_IOCTL(SIOCSIWAUTH) = ipw_wx_set_auth,
|
||||
IW_IOCTL(SIOCGIWAUTH) = ipw_wx_get_auth,
|
||||
IW_IOCTL(SIOCSIWENCODEEXT) = ipw_wx_set_encodeext,
|
||||
IW_IOCTL(SIOCGIWENCODEEXT) = ipw_wx_get_encodeext,
|
||||
IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname),
|
||||
IW_HANDLER(SIOCSIWFREQ, ipw_wx_set_freq),
|
||||
IW_HANDLER(SIOCGIWFREQ, ipw_wx_get_freq),
|
||||
IW_HANDLER(SIOCSIWMODE, ipw_wx_set_mode),
|
||||
IW_HANDLER(SIOCGIWMODE, ipw_wx_get_mode),
|
||||
IW_HANDLER(SIOCSIWSENS, ipw_wx_set_sens),
|
||||
IW_HANDLER(SIOCGIWSENS, ipw_wx_get_sens),
|
||||
IW_HANDLER(SIOCGIWRANGE, ipw_wx_get_range),
|
||||
IW_HANDLER(SIOCSIWAP, ipw_wx_set_wap),
|
||||
IW_HANDLER(SIOCGIWAP, ipw_wx_get_wap),
|
||||
IW_HANDLER(SIOCSIWSCAN, ipw_wx_set_scan),
|
||||
IW_HANDLER(SIOCGIWSCAN, ipw_wx_get_scan),
|
||||
IW_HANDLER(SIOCSIWESSID, ipw_wx_set_essid),
|
||||
IW_HANDLER(SIOCGIWESSID, ipw_wx_get_essid),
|
||||
IW_HANDLER(SIOCSIWNICKN, ipw_wx_set_nick),
|
||||
IW_HANDLER(SIOCGIWNICKN, ipw_wx_get_nick),
|
||||
IW_HANDLER(SIOCSIWRATE, ipw_wx_set_rate),
|
||||
IW_HANDLER(SIOCGIWRATE, ipw_wx_get_rate),
|
||||
IW_HANDLER(SIOCSIWRTS, ipw_wx_set_rts),
|
||||
IW_HANDLER(SIOCGIWRTS, ipw_wx_get_rts),
|
||||
IW_HANDLER(SIOCSIWFRAG, ipw_wx_set_frag),
|
||||
IW_HANDLER(SIOCGIWFRAG, ipw_wx_get_frag),
|
||||
IW_HANDLER(SIOCSIWTXPOW, ipw_wx_set_txpow),
|
||||
IW_HANDLER(SIOCGIWTXPOW, ipw_wx_get_txpow),
|
||||
IW_HANDLER(SIOCSIWRETRY, ipw_wx_set_retry),
|
||||
IW_HANDLER(SIOCGIWRETRY, ipw_wx_get_retry),
|
||||
IW_HANDLER(SIOCSIWENCODE, ipw_wx_set_encode),
|
||||
IW_HANDLER(SIOCGIWENCODE, ipw_wx_get_encode),
|
||||
IW_HANDLER(SIOCSIWPOWER, ipw_wx_set_power),
|
||||
IW_HANDLER(SIOCGIWPOWER, ipw_wx_get_power),
|
||||
IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
|
||||
IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
|
||||
IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
|
||||
IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
|
||||
IW_HANDLER(SIOCSIWGENIE, ipw_wx_set_genie),
|
||||
IW_HANDLER(SIOCGIWGENIE, ipw_wx_get_genie),
|
||||
IW_HANDLER(SIOCSIWMLME, ipw_wx_set_mlme),
|
||||
IW_HANDLER(SIOCSIWAUTH, ipw_wx_set_auth),
|
||||
IW_HANDLER(SIOCGIWAUTH, ipw_wx_get_auth),
|
||||
IW_HANDLER(SIOCSIWENCODEEXT, ipw_wx_set_encodeext),
|
||||
IW_HANDLER(SIOCGIWENCODEEXT, ipw_wx_get_encodeext),
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
|
@ -212,6 +212,9 @@ static struct iwl_lib_ops iwl1000_lib = {
|
|||
.set_ct_kill = iwl1000_set_ct_threshold,
|
||||
},
|
||||
.add_bcast_station = iwl_add_bcast_station,
|
||||
.recover_from_tx_stall = iwl_bg_monitor_recover,
|
||||
.check_plcp_health = iwl_good_plcp_health,
|
||||
.check_ack_health = iwl_good_ack_health,
|
||||
};
|
||||
|
||||
static const struct iwl_ops iwl1000_ops = {
|
||||
|
@ -223,7 +226,7 @@ static const struct iwl_ops iwl1000_ops = {
|
|||
};
|
||||
|
||||
struct iwl_cfg iwl1000_bgn_cfg = {
|
||||
.name = "1000 Series BGN",
|
||||
.name = "Intel(R) Centrino(R) Wireless-N 1000 BGN",
|
||||
.fw_name_pre = IWL1000_FW_PRE,
|
||||
.ucode_api_max = IWL1000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL1000_UCODE_API_MIN,
|
||||
|
@ -249,10 +252,11 @@ struct iwl_cfg iwl1000_bgn_cfg = {
|
|||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl1000_bg_cfg = {
|
||||
.name = "1000 Series BG",
|
||||
.name = "Intel(R) Centrino(R) Wireless-N 1000 BG",
|
||||
.fw_name_pre = IWL1000_FW_PRE,
|
||||
.ucode_api_max = IWL1000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL1000_UCODE_API_MIN,
|
||||
|
@ -277,6 +281,7 @@ struct iwl_cfg iwl1000_bg_cfg = {
|
|||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX));
|
||||
|
|
|
@ -329,16 +329,25 @@ static void iwl3945_collect_tx_data(struct iwl3945_rs_sta *rs_sta,
|
|||
|
||||
}
|
||||
|
||||
static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_sta *sta, void *priv_sta)
|
||||
/*
|
||||
* Called after adding a new station to initialize rate scaling
|
||||
*/
|
||||
void iwl3945_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id)
|
||||
{
|
||||
struct iwl3945_rs_sta *rs_sta = priv_sta;
|
||||
struct iwl_priv *priv = (struct iwl_priv *)priv_r;
|
||||
struct ieee80211_hw *hw = priv->hw;
|
||||
struct ieee80211_conf *conf = &priv->hw->conf;
|
||||
struct iwl3945_sta_priv *psta;
|
||||
struct iwl3945_rs_sta *rs_sta;
|
||||
struct ieee80211_supported_band *sband;
|
||||
int i;
|
||||
|
||||
IWL_DEBUG_RATE(priv, "enter\n");
|
||||
IWL_DEBUG_INFO(priv, "enter \n");
|
||||
if (sta_id == priv->hw_params.bcast_sta_id)
|
||||
goto out;
|
||||
|
||||
spin_lock_init(&rs_sta->lock);
|
||||
psta = (struct iwl3945_sta_priv *) sta->drv_priv;
|
||||
rs_sta = &psta->rs_sta;
|
||||
sband = hw->wiphy->bands[conf->channel->band];
|
||||
|
||||
rs_sta->priv = priv;
|
||||
|
||||
|
@ -351,9 +360,7 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
|
|||
rs_sta->last_flush = jiffies;
|
||||
rs_sta->flush_time = IWL_RATE_FLUSH;
|
||||
rs_sta->last_tx_packets = 0;
|
||||
rs_sta->ibss_sta_added = 0;
|
||||
|
||||
init_timer(&rs_sta->rate_scale_flush);
|
||||
rs_sta->rate_scale_flush.data = (unsigned long)rs_sta;
|
||||
rs_sta->rate_scale_flush.function = iwl3945_bg_rate_scale_flush;
|
||||
|
||||
|
@ -380,8 +387,10 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
|
|||
IWL_FIRST_OFDM_RATE;
|
||||
}
|
||||
|
||||
out:
|
||||
priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
|
||||
|
||||
IWL_DEBUG_RATE(priv, "leave\n");
|
||||
IWL_DEBUG_INFO(priv, "leave\n");
|
||||
}
|
||||
|
||||
static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
|
||||
|
@ -405,6 +414,9 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp)
|
|||
|
||||
rs_sta = &psta->rs_sta;
|
||||
|
||||
spin_lock_init(&rs_sta->lock);
|
||||
init_timer(&rs_sta->rate_scale_flush);
|
||||
|
||||
IWL_DEBUG_RATE(priv, "leave\n");
|
||||
|
||||
return rs_sta;
|
||||
|
@ -413,13 +425,14 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp)
|
|||
static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta,
|
||||
void *priv_sta)
|
||||
{
|
||||
struct iwl3945_sta_priv *psta = (void *) sta->drv_priv;
|
||||
struct iwl3945_rs_sta *rs_sta = &psta->rs_sta;
|
||||
struct iwl_priv *priv __maybe_unused = rs_sta->priv;
|
||||
struct iwl3945_rs_sta *rs_sta = priv_sta;
|
||||
|
||||
IWL_DEBUG_RATE(priv, "enter\n");
|
||||
/*
|
||||
* Be careful not to use any members of iwl3945_rs_sta (like trying
|
||||
* to use iwl_priv to print out debugging) since it may not be fully
|
||||
* initialized at this point.
|
||||
*/
|
||||
del_timer_sync(&rs_sta->rate_scale_flush);
|
||||
IWL_DEBUG_RATE(priv, "leave\n");
|
||||
}
|
||||
|
||||
|
||||
|
@ -458,6 +471,13 @@ static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband
|
|||
return;
|
||||
}
|
||||
|
||||
/* Treat uninitialized rate scaling data same as non-existing. */
|
||||
if (!rs_sta->priv) {
|
||||
IWL_DEBUG_RATE(priv, "leave: STA priv data uninitialized!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
rs_sta->tx_packets++;
|
||||
|
||||
scale_rate_index = first_index;
|
||||
|
@ -625,7 +645,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
|
|||
u32 fail_count;
|
||||
s8 scale_action = 0;
|
||||
unsigned long flags;
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0;
|
||||
s8 max_rate_idx = -1;
|
||||
struct iwl_priv *priv = (struct iwl_priv *)priv_r;
|
||||
|
@ -633,6 +652,12 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
|
|||
|
||||
IWL_DEBUG_RATE(priv, "enter\n");
|
||||
|
||||
/* Treat uninitialized rate scaling data same as non-existing. */
|
||||
if (rs_sta && !rs_sta->priv) {
|
||||
IWL_DEBUG_RATE(priv, "Rate scaling information not initialized yet.\n");
|
||||
priv_sta = NULL;
|
||||
}
|
||||
|
||||
if (rate_control_send_low(sta, priv_sta, txrc))
|
||||
return;
|
||||
|
||||
|
@ -650,20 +675,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta,
|
|||
if (sband->band == IEEE80211_BAND_5GHZ)
|
||||
rate_mask = rate_mask << IWL_FIRST_OFDM_RATE;
|
||||
|
||||
if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
|
||||
!rs_sta->ibss_sta_added) {
|
||||
u8 sta_id = iwl_find_station(priv, hdr->addr1);
|
||||
|
||||
if (sta_id == IWL_INVALID_STATION) {
|
||||
IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n",
|
||||
hdr->addr1);
|
||||
sta_id = iwl_add_station(priv, hdr->addr1, false,
|
||||
CMD_ASYNC, NULL);
|
||||
}
|
||||
if (sta_id != IWL_INVALID_STATION)
|
||||
rs_sta->ibss_sta_added = 1;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&rs_sta->lock, flags);
|
||||
|
||||
/* for recent assoc, choose best rate regarding
|
||||
|
@ -883,12 +894,22 @@ static void iwl3945_remove_debugfs(void *priv, void *priv_sta)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialization of rate scaling information is done by driver after
|
||||
* the station is added. Since mac80211 calls this function before a
|
||||
* station is added we ignore it.
|
||||
*/
|
||||
static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_sta *sta, void *priv_sta)
|
||||
{
|
||||
}
|
||||
|
||||
static struct rate_control_ops rs_ops = {
|
||||
.module = NULL,
|
||||
.name = RS_NAME,
|
||||
.tx_status = rs_tx_status,
|
||||
.get_rate = rs_get_rate,
|
||||
.rate_init = rs_rate_init,
|
||||
.rate_init = rs_rate_init_stub,
|
||||
.alloc = rs_alloc,
|
||||
.free = rs_free,
|
||||
.alloc_sta = rs_alloc_sta,
|
||||
|
@ -899,7 +920,6 @@ static struct rate_control_ops rs_ops = {
|
|||
#endif
|
||||
|
||||
};
|
||||
|
||||
void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
|
||||
{
|
||||
struct iwl_priv *priv = hw->priv;
|
||||
|
@ -916,6 +936,7 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
|
|||
sta = ieee80211_find_sta(priv->vif,
|
||||
priv->stations[sta_id].sta.sta.addr);
|
||||
if (!sta) {
|
||||
IWL_DEBUG_RATE(priv, "Unable to find station to initialize rate scaling.\n");
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1911,6 +1911,8 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv)
|
|||
"configuration (%d).\n", rc);
|
||||
return rc;
|
||||
}
|
||||
iwl_clear_ucode_stations(priv, false);
|
||||
iwl_restore_stations(priv);
|
||||
}
|
||||
|
||||
IWL_DEBUG_INFO(priv, "Sending RXON\n"
|
||||
|
@ -1941,7 +1943,10 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv)
|
|||
|
||||
memcpy(active_rxon, staging_rxon, sizeof(*active_rxon));
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
if (!new_assoc) {
|
||||
iwl_clear_ucode_stations(priv, false);
|
||||
iwl_restore_stations(priv);
|
||||
}
|
||||
|
||||
/* If we issue a new RXON command which required a tune then we must
|
||||
* send a new TXPOWER command or we won't be able to Tx any frames */
|
||||
|
@ -1951,19 +1956,6 @@ static int iwl3945_commit_rxon(struct iwl_priv *priv)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/* Add the broadcast address so we can send broadcast frames */
|
||||
priv->cfg->ops->lib->add_bcast_station(priv);
|
||||
|
||||
/* If we have set the ASSOC_MSK and we are in BSS mode then
|
||||
* add the IWL_AP_ID to the station rate table */
|
||||
if (iwl_is_associated(priv) &&
|
||||
(priv->iw_mode == NL80211_IFTYPE_STATION))
|
||||
if (iwl_add_station(priv, priv->active_rxon.bssid_addr,
|
||||
true, CMD_SYNC, NULL) == IWL_INVALID_STATION) {
|
||||
IWL_ERR(priv, "Error adding AP address for transmit\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Init the hardware's rate fallback order based on the band */
|
||||
rc = iwl3945_init_hw_rate_table(priv);
|
||||
if (rc) {
|
||||
|
@ -2828,6 +2820,7 @@ static struct iwl_cfg iwl3945_bg_cfg = {
|
|||
.led_compensation = 64,
|
||||
.broken_powersave = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
static struct iwl_cfg iwl3945_abg_cfg = {
|
||||
|
@ -2846,6 +2839,7 @@ static struct iwl_cfg iwl3945_abg_cfg = {
|
|||
.led_compensation = 64,
|
||||
.broken_powersave = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
DEFINE_PCI_DEVICE_TABLE(iwl3945_hw_card_ids) = {
|
||||
|
|
|
@ -95,7 +95,6 @@ struct iwl3945_rs_sta {
|
|||
u8 tgg;
|
||||
u8 flush_pending;
|
||||
u8 start_rate;
|
||||
u8 ibss_sta_added;
|
||||
struct timer_list rate_scale_flush;
|
||||
struct iwl3945_rate_scale_data win[IWL_RATE_COUNT_3945];
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
|
|
|
@ -2187,6 +2187,7 @@ static struct iwl_lib_ops iwl4965_lib = {
|
|||
.load_ucode = iwl4965_load_bsm,
|
||||
.dump_nic_event_log = iwl_dump_nic_event_log,
|
||||
.dump_nic_error_log = iwl_dump_nic_error_log,
|
||||
.dump_fh = iwl_dump_fh,
|
||||
.set_channel_switch = iwl4965_hw_channel_switch,
|
||||
.apm_ops = {
|
||||
.init = iwl_apm_init,
|
||||
|
@ -2220,6 +2221,7 @@ static struct iwl_lib_ops iwl4965_lib = {
|
|||
.set_ct_kill = iwl4965_set_ct_threshold,
|
||||
},
|
||||
.add_bcast_station = iwl_add_bcast_station,
|
||||
.check_plcp_health = iwl_good_plcp_health,
|
||||
};
|
||||
|
||||
static const struct iwl_ops iwl4965_ops = {
|
||||
|
@ -2231,7 +2233,7 @@ static const struct iwl_ops iwl4965_ops = {
|
|||
};
|
||||
|
||||
struct iwl_cfg iwl4965_agn_cfg = {
|
||||
.name = "4965AGN",
|
||||
.name = "Intel(R) Wireless WiFi Link 4965AGN",
|
||||
.fw_name_pre = IWL4965_FW_PRE,
|
||||
.ucode_api_max = IWL4965_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL4965_UCODE_API_MIN,
|
||||
|
@ -2254,6 +2256,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
|
|||
.led_compensation = 61,
|
||||
.chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
/* Module firmware */
|
||||
|
|
|
@ -544,7 +544,6 @@ void iwl5000_init_alive_start(struct iwl_priv *priv)
|
|||
goto restart;
|
||||
}
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
ret = priv->cfg->ops->lib->alive_notify(priv);
|
||||
if (ret) {
|
||||
IWL_WARN(priv,
|
||||
|
@ -1500,6 +1499,9 @@ struct iwl_lib_ops iwl5000_lib = {
|
|||
.set_ct_kill = iwl5000_set_ct_threshold,
|
||||
},
|
||||
.add_bcast_station = iwl_add_bcast_station,
|
||||
.recover_from_tx_stall = iwl_bg_monitor_recover,
|
||||
.check_plcp_health = iwl_good_plcp_health,
|
||||
.check_ack_health = iwl_good_ack_health,
|
||||
};
|
||||
|
||||
static struct iwl_lib_ops iwl5150_lib = {
|
||||
|
@ -1554,6 +1556,9 @@ static struct iwl_lib_ops iwl5150_lib = {
|
|||
.set_ct_kill = iwl5150_set_ct_threshold,
|
||||
},
|
||||
.add_bcast_station = iwl_add_bcast_station,
|
||||
.recover_from_tx_stall = iwl_bg_monitor_recover,
|
||||
.check_plcp_health = iwl_good_plcp_health,
|
||||
.check_ack_health = iwl_good_ack_health,
|
||||
};
|
||||
|
||||
static const struct iwl_ops iwl5000_ops = {
|
||||
|
@ -1580,7 +1585,7 @@ struct iwl_mod_params iwl50_mod_params = {
|
|||
|
||||
|
||||
struct iwl_cfg iwl5300_agn_cfg = {
|
||||
.name = "5300AGN",
|
||||
.name = "Intel(R) Ultimate N WiFi Link 5300 AGN",
|
||||
.fw_name_pre = IWL5000_FW_PRE,
|
||||
.ucode_api_max = IWL5000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL5000_UCODE_API_MIN,
|
||||
|
@ -1603,10 +1608,11 @@ struct iwl_cfg iwl5300_agn_cfg = {
|
|||
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl5100_bgn_cfg = {
|
||||
.name = "5100BGN",
|
||||
.name = "Intel(R) WiFi Link 5100 BGN",
|
||||
.fw_name_pre = IWL5000_FW_PRE,
|
||||
.ucode_api_max = IWL5000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL5000_UCODE_API_MIN,
|
||||
|
@ -1629,10 +1635,11 @@ struct iwl_cfg iwl5100_bgn_cfg = {
|
|||
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl5100_abg_cfg = {
|
||||
.name = "5100ABG",
|
||||
.name = "Intel(R) WiFi Link 5100 ABG",
|
||||
.fw_name_pre = IWL5000_FW_PRE,
|
||||
.ucode_api_max = IWL5000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL5000_UCODE_API_MIN,
|
||||
|
@ -1653,10 +1660,11 @@ struct iwl_cfg iwl5100_abg_cfg = {
|
|||
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl5100_agn_cfg = {
|
||||
.name = "5100AGN",
|
||||
.name = "Intel(R) WiFi Link 5100 AGN",
|
||||
.fw_name_pre = IWL5000_FW_PRE,
|
||||
.ucode_api_max = IWL5000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL5000_UCODE_API_MIN,
|
||||
|
@ -1679,10 +1687,11 @@ struct iwl_cfg iwl5100_agn_cfg = {
|
|||
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl5350_agn_cfg = {
|
||||
.name = "5350AGN",
|
||||
.name = "Intel(R) WiMAX/WiFi Link 5350 AGN",
|
||||
.fw_name_pre = IWL5000_FW_PRE,
|
||||
.ucode_api_max = IWL5000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL5000_UCODE_API_MIN,
|
||||
|
@ -1705,10 +1714,11 @@ struct iwl_cfg iwl5350_agn_cfg = {
|
|||
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl5150_agn_cfg = {
|
||||
.name = "5150AGN",
|
||||
.name = "Intel(R) WiMAX/WiFi Link 5150 AGN",
|
||||
.fw_name_pre = IWL5150_FW_PRE,
|
||||
.ucode_api_max = IWL5150_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL5150_UCODE_API_MIN,
|
||||
|
@ -1731,10 +1741,11 @@ struct iwl_cfg iwl5150_agn_cfg = {
|
|||
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl5150_abg_cfg = {
|
||||
.name = "5150ABG",
|
||||
.name = "Intel(R) WiMAX/WiFi Link 5150 ABG",
|
||||
.fw_name_pre = IWL5150_FW_PRE,
|
||||
.ucode_api_max = IWL5150_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL5150_UCODE_API_MIN,
|
||||
|
@ -1755,6 +1766,7 @@ struct iwl_cfg iwl5150_abg_cfg = {
|
|||
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
|
||||
|
|
|
@ -278,6 +278,9 @@ static struct iwl_lib_ops iwl6000_lib = {
|
|||
.set_ct_kill = iwl6000_set_ct_threshold,
|
||||
},
|
||||
.add_bcast_station = iwl_add_bcast_station,
|
||||
.recover_from_tx_stall = iwl_bg_monitor_recover,
|
||||
.check_plcp_health = iwl_good_plcp_health,
|
||||
.check_ack_health = iwl_good_ack_health,
|
||||
};
|
||||
|
||||
static const struct iwl_ops iwl6000_ops = {
|
||||
|
@ -343,6 +346,9 @@ static struct iwl_lib_ops iwl6050_lib = {
|
|||
.set_calib_version = iwl6050_set_calib_version,
|
||||
},
|
||||
.add_bcast_station = iwl_add_bcast_station,
|
||||
.recover_from_tx_stall = iwl_bg_monitor_recover,
|
||||
.check_plcp_health = iwl_good_plcp_health,
|
||||
.check_ack_health = iwl_good_ack_health,
|
||||
};
|
||||
|
||||
static const struct iwl_ops iwl6050_ops = {
|
||||
|
@ -357,7 +363,7 @@ static const struct iwl_ops iwl6050_ops = {
|
|||
* "i": Internal configuration, use internal Power Amplifier
|
||||
*/
|
||||
struct iwl_cfg iwl6000i_2agn_cfg = {
|
||||
.name = "6000 Series 2x2 AGN",
|
||||
.name = "Intel(R) Centrino(R) Advanced-N 6200 AGN",
|
||||
.fw_name_pre = IWL6000_FW_PRE,
|
||||
.ucode_api_max = IWL6000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL6000_UCODE_API_MIN,
|
||||
|
@ -386,10 +392,11 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
|
|||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl6000i_2abg_cfg = {
|
||||
.name = "6000 Series 2x2 ABG",
|
||||
.name = "Intel(R) Centrino(R) Advanced-N 6200 ABG",
|
||||
.fw_name_pre = IWL6000_FW_PRE,
|
||||
.ucode_api_max = IWL6000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL6000_UCODE_API_MIN,
|
||||
|
@ -417,10 +424,11 @@ struct iwl_cfg iwl6000i_2abg_cfg = {
|
|||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl6000i_2bg_cfg = {
|
||||
.name = "6000 Series 2x2 BG",
|
||||
.name = "Intel(R) Centrino(R) Advanced-N 6200 BG",
|
||||
.fw_name_pre = IWL6000_FW_PRE,
|
||||
.ucode_api_max = IWL6000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL6000_UCODE_API_MIN,
|
||||
|
@ -448,10 +456,11 @@ struct iwl_cfg iwl6000i_2bg_cfg = {
|
|||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl6050_2agn_cfg = {
|
||||
.name = "6050 Series 2x2 AGN",
|
||||
.name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN",
|
||||
.fw_name_pre = IWL6050_FW_PRE,
|
||||
.ucode_api_max = IWL6050_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL6050_UCODE_API_MIN,
|
||||
|
@ -480,10 +489,11 @@ struct iwl_cfg iwl6050_2agn_cfg = {
|
|||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1500,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl6050_2abg_cfg = {
|
||||
.name = "6050 Series 2x2 ABG",
|
||||
.name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG",
|
||||
.fw_name_pre = IWL6050_FW_PRE,
|
||||
.ucode_api_max = IWL6050_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL6050_UCODE_API_MIN,
|
||||
|
@ -511,10 +521,11 @@ struct iwl_cfg iwl6050_2abg_cfg = {
|
|||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1500,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
struct iwl_cfg iwl6000_3agn_cfg = {
|
||||
.name = "6000 Series 3x3 AGN",
|
||||
.name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN",
|
||||
.fw_name_pre = IWL6000_FW_PRE,
|
||||
.ucode_api_max = IWL6000_UCODE_API_MAX,
|
||||
.ucode_api_min = IWL6000_UCODE_API_MIN,
|
||||
|
@ -543,6 +554,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
|
|||
.support_ct_kill_exit = true,
|
||||
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
|
||||
.chain_noise_scale = 1000,
|
||||
.monitor_recover_period = IWL_MONITORING_PERIOD,
|
||||
};
|
||||
|
||||
MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
|
||||
|
|
|
@ -769,6 +769,15 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
|
|||
|
||||
IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n");
|
||||
|
||||
/* Treat uninitialized rate scaling data same as non-existing. */
|
||||
if (!lq_sta) {
|
||||
IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n");
|
||||
return;
|
||||
} else if (!lq_sta->drv) {
|
||||
IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ieee80211_is_data(hdr->frame_control) ||
|
||||
info->flags & IEEE80211_TX_CTL_NO_ACK)
|
||||
return;
|
||||
|
@ -778,10 +787,6 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
|
|||
!(info->flags & IEEE80211_TX_STAT_AMPDU))
|
||||
return;
|
||||
|
||||
if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
|
||||
!lq_sta->ibss_sta_added)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Ignore this Tx frame response if its initial rate doesn't match
|
||||
* that of latest Link Quality command. There may be stragglers
|
||||
|
@ -827,7 +832,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband,
|
|||
lq_sta->missed_rate_counter++;
|
||||
if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) {
|
||||
lq_sta->missed_rate_counter = 0;
|
||||
iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
|
||||
iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false);
|
||||
}
|
||||
/* Regardless, ignore this status info for outdated rate */
|
||||
return;
|
||||
|
@ -1915,7 +1920,7 @@ static u32 rs_update_rate_tbl(struct iwl_priv *priv,
|
|||
/* Update uCode's rate table. */
|
||||
rate = rate_n_flags_from_tbl(priv, tbl, index, is_green);
|
||||
rs_fill_link_cmd(priv, lq_sta, rate);
|
||||
iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
|
||||
iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false);
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
@ -2291,7 +2296,7 @@ lq_update:
|
|||
IWL_DEBUG_RATE(priv, "Switch current mcs: %X index: %d\n",
|
||||
tbl->current_rate, index);
|
||||
rs_fill_link_cmd(priv, lq_sta, tbl->current_rate);
|
||||
iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
|
||||
iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC, false);
|
||||
} else
|
||||
done_search = 1;
|
||||
}
|
||||
|
@ -2340,7 +2345,20 @@ out:
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* rs_initialize_lq - Initialize a station's hardware rate table
|
||||
*
|
||||
* The uCode's station table contains a table of fallback rates
|
||||
* for automatic fallback during transmission.
|
||||
*
|
||||
* NOTE: This sets up a default set of values. These will be replaced later
|
||||
* if the driver's iwl-agn-rs rate scaling algorithm is used, instead of
|
||||
* rc80211_simple.
|
||||
*
|
||||
* NOTE: Run REPLY_ADD_STA command to set up station table entry, before
|
||||
* calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
|
||||
* which requires station table entry to exist).
|
||||
*/
|
||||
static void rs_initialize_lq(struct iwl_priv *priv,
|
||||
struct ieee80211_conf *conf,
|
||||
struct ieee80211_sta *sta,
|
||||
|
@ -2359,10 +2377,6 @@ static void rs_initialize_lq(struct iwl_priv *priv,
|
|||
|
||||
i = lq_sta->last_txrate_idx;
|
||||
|
||||
if ((lq_sta->lq.sta_id == 0xff) &&
|
||||
(priv->iw_mode == NL80211_IFTYPE_ADHOC))
|
||||
goto out;
|
||||
|
||||
valid_tx_ant = priv->hw_params.valid_tx_ant;
|
||||
|
||||
if (!lq_sta->search_better_tbl)
|
||||
|
@ -2390,7 +2404,8 @@ static void rs_initialize_lq(struct iwl_priv *priv,
|
|||
tbl->current_rate = rate;
|
||||
rs_set_expected_tpt_table(lq_sta, tbl);
|
||||
rs_fill_link_cmd(NULL, lq_sta, rate);
|
||||
iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_ASYNC);
|
||||
priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq;
|
||||
iwl_send_lq_cmd(priv, &lq_sta->lq, CMD_SYNC, true);
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
@ -2402,9 +2417,6 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
|
|||
struct sk_buff *skb = txrc->skb;
|
||||
struct ieee80211_supported_band *sband = txrc->sband;
|
||||
struct iwl_priv *priv = (struct iwl_priv *)priv_r;
|
||||
struct ieee80211_conf *conf = &priv->hw->conf;
|
||||
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct iwl_lq_sta *lq_sta = priv_sta;
|
||||
int rate_idx;
|
||||
|
@ -2422,30 +2434,18 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta,
|
|||
lq_sta->max_rate_idx = -1;
|
||||
}
|
||||
|
||||
/* Treat uninitialized rate scaling data same as non-existing. */
|
||||
if (lq_sta && !lq_sta->drv) {
|
||||
IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n");
|
||||
priv_sta = NULL;
|
||||
}
|
||||
|
||||
/* Send management frames and NO_ACK data using lowest rate. */
|
||||
if (rate_control_send_low(sta, priv_sta, txrc))
|
||||
return;
|
||||
|
||||
rate_idx = lq_sta->last_txrate_idx;
|
||||
|
||||
if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) &&
|
||||
!lq_sta->ibss_sta_added) {
|
||||
u8 sta_id = iwl_find_station(priv, hdr->addr1);
|
||||
|
||||
if (sta_id == IWL_INVALID_STATION) {
|
||||
IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n",
|
||||
hdr->addr1);
|
||||
sta_id = iwl_add_station(priv, hdr->addr1,
|
||||
false, CMD_ASYNC, ht_cap);
|
||||
}
|
||||
if ((sta_id != IWL_INVALID_STATION)) {
|
||||
lq_sta->lq.sta_id = sta_id;
|
||||
lq_sta->lq.rs_table[0].rate_n_flags = 0;
|
||||
lq_sta->ibss_sta_added = 1;
|
||||
rs_initialize_lq(priv, conf, sta, lq_sta);
|
||||
}
|
||||
}
|
||||
|
||||
if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) {
|
||||
rate_idx -= IWL_FIRST_OFDM_RATE;
|
||||
/* 6M and 9M shared same MCS index */
|
||||
|
@ -2495,16 +2495,25 @@ static void *rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta,
|
|||
return lq_sta;
|
||||
}
|
||||
|
||||
static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_sta *sta, void *priv_sta)
|
||||
/*
|
||||
* Called after adding a new station to initialize rate scaling
|
||||
*/
|
||||
void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_id)
|
||||
{
|
||||
int i, j;
|
||||
struct iwl_priv *priv = (struct iwl_priv *)priv_r;
|
||||
struct ieee80211_hw *hw = priv->hw;
|
||||
struct ieee80211_conf *conf = &priv->hw->conf;
|
||||
struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap;
|
||||
struct iwl_lq_sta *lq_sta = priv_sta;
|
||||
struct iwl_station_priv *sta_priv;
|
||||
struct iwl_lq_sta *lq_sta;
|
||||
struct ieee80211_supported_band *sband;
|
||||
|
||||
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 (i = 0; i < IWL_RATE_COUNT; i++)
|
||||
|
@ -2516,33 +2525,13 @@ static void rs_rate_init(void *priv_r, struct ieee80211_supported_band *sband,
|
|||
for (i = 0; i < IWL_RATE_COUNT; i++)
|
||||
rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
|
||||
|
||||
IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init ***\n");
|
||||
IWL_DEBUG_RATE(priv, "LQ: *** rate scale station global init for station %d ***\n",
|
||||
sta_id);
|
||||
/* TODO: what is a good starting rate for STA? About middle? Maybe not
|
||||
* the lowest or the highest rate.. Could consider using RSSI from
|
||||
* previous packets? Need to have IEEE 802.1X auth succeed immediately
|
||||
* after assoc.. */
|
||||
|
||||
lq_sta->ibss_sta_added = 0;
|
||||
if (priv->iw_mode == NL80211_IFTYPE_AP) {
|
||||
u8 sta_id = iwl_find_station(priv,
|
||||
sta->addr);
|
||||
|
||||
/* for IBSS the call are from tasklet */
|
||||
IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", sta->addr);
|
||||
|
||||
if (sta_id == IWL_INVALID_STATION) {
|
||||
IWL_DEBUG_RATE(priv, "LQ: ADD station %pM\n", sta->addr);
|
||||
sta_id = iwl_add_station(priv, sta->addr, false,
|
||||
CMD_ASYNC, ht_cap);
|
||||
}
|
||||
if ((sta_id != IWL_INVALID_STATION)) {
|
||||
lq_sta->lq.sta_id = sta_id;
|
||||
lq_sta->lq.rs_table[0].rate_n_flags = 0;
|
||||
}
|
||||
/* FIXME: this is w/a remove it later */
|
||||
priv->assoc_station_added = 1;
|
||||
}
|
||||
|
||||
lq_sta->is_dup = 0;
|
||||
lq_sta->max_rate_idx = -1;
|
||||
lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX;
|
||||
|
@ -2795,7 +2784,7 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
|
|||
|
||||
if (lq_sta->dbg_fixed_rate) {
|
||||
rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate);
|
||||
iwl_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC);
|
||||
iwl_send_lq_cmd(lq_sta->drv, &lq_sta->lq, CMD_ASYNC, false);
|
||||
}
|
||||
|
||||
return count;
|
||||
|
@ -2992,12 +2981,21 @@ static void rs_remove_debugfs(void *priv, void *priv_sta)
|
|||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialization of rate scaling information is done by driver after
|
||||
* the station is added. Since mac80211 calls this function before a
|
||||
* station is added we ignore it.
|
||||
*/
|
||||
static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband,
|
||||
struct ieee80211_sta *sta, void *priv_sta)
|
||||
{
|
||||
}
|
||||
static struct rate_control_ops rs_ops = {
|
||||
.module = NULL,
|
||||
.name = RS_NAME,
|
||||
.tx_status = rs_tx_status,
|
||||
.get_rate = rs_get_rate,
|
||||
.rate_init = rs_rate_init,
|
||||
.rate_init = rs_rate_init_stub,
|
||||
.alloc = rs_alloc,
|
||||
.free = rs_free,
|
||||
.alloc_sta = rs_alloc_sta,
|
||||
|
|
|
@ -403,7 +403,6 @@ struct iwl_lq_sta {
|
|||
u8 is_green;
|
||||
u8 is_dup;
|
||||
enum ieee80211_band band;
|
||||
u8 ibss_sta_added;
|
||||
|
||||
/* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */
|
||||
u32 supp_rates;
|
||||
|
@ -478,6 +477,12 @@ static inline u8 iwl3945_get_prev_ieee_rate(u8 rate_index)
|
|||
*/
|
||||
extern void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id);
|
||||
|
||||
/* Initialize station's rate scaling information after adding station */
|
||||
extern void iwl_rs_rate_init(struct iwl_priv *priv,
|
||||
struct ieee80211_sta *sta, u8 sta_id);
|
||||
extern void iwl3945_rs_rate_init(struct iwl_priv *priv,
|
||||
struct ieee80211_sta *sta, u8 sta_id);
|
||||
|
||||
/**
|
||||
* iwl_rate_control_register - Register the rate control algorithm callbacks
|
||||
*
|
||||
|
|
|
@ -144,9 +144,6 @@ int iwl_commit_rxon(struct iwl_priv *priv)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* station table will be cleared */
|
||||
priv->assoc_station_added = 0;
|
||||
|
||||
/* If we are currently associated and the new config requires
|
||||
* an RXON_ASSOC and the new config wants the associated mask enabled,
|
||||
* we must clear the associated from the active configuration
|
||||
|
@ -166,6 +163,8 @@ int iwl_commit_rxon(struct iwl_priv *priv)
|
|||
IWL_ERR(priv, "Error clearing ASSOC_MSK (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
iwl_clear_ucode_stations(priv, false);
|
||||
iwl_restore_stations(priv);
|
||||
}
|
||||
|
||||
IWL_DEBUG_INFO(priv, "Sending RXON\n"
|
||||
|
@ -179,9 +178,8 @@ int iwl_commit_rxon(struct iwl_priv *priv)
|
|||
iwl_set_rxon_hwcrypto(priv, !priv->cfg->mod_params->sw_crypto);
|
||||
|
||||
/* Apply the new configuration
|
||||
* RXON unassoc clears the station table in uCode, send it before
|
||||
* we add the bcast station. If assoc bit is set, we will send RXON
|
||||
* after having added the bcast and bssid station.
|
||||
* RXON unassoc clears the station table in uCode so restoration of
|
||||
* stations is needed after it (the RXON command) completes
|
||||
*/
|
||||
if (!new_assoc) {
|
||||
ret = iwl_send_cmd_pdu(priv, REPLY_RXON,
|
||||
|
@ -190,35 +188,14 @@ int iwl_commit_rxon(struct iwl_priv *priv)
|
|||
IWL_ERR(priv, "Error setting new RXON (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
IWL_DEBUG_INFO(priv, "Return from !new_assoc RXON. \n");
|
||||
memcpy(active_rxon, &priv->staging_rxon, sizeof(*active_rxon));
|
||||
iwl_clear_ucode_stations(priv, false);
|
||||
iwl_restore_stations(priv);
|
||||
}
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
|
||||
priv->start_calib = 0;
|
||||
|
||||
/* Add the broadcast address so we can send broadcast frames */
|
||||
priv->cfg->ops->lib->add_bcast_station(priv);
|
||||
|
||||
|
||||
/* If we have set the ASSOC_MSK and we are in BSS mode then
|
||||
* add the IWL_AP_ID to the station rate table */
|
||||
if (new_assoc) {
|
||||
if (priv->iw_mode == NL80211_IFTYPE_STATION) {
|
||||
ret = iwl_rxon_add_station(priv,
|
||||
priv->active_rxon.bssid_addr, 1);
|
||||
if (ret == IWL_INVALID_STATION) {
|
||||
IWL_ERR(priv,
|
||||
"Error adding AP address for TX.\n");
|
||||
return -EIO;
|
||||
}
|
||||
priv->assoc_station_added = 1;
|
||||
if (priv->default_wep_key &&
|
||||
iwl_send_static_wepkey_cmd(priv, 0))
|
||||
IWL_ERR(priv,
|
||||
"Could not send WEP static key.\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* allow CTS-to-self if possible for new association.
|
||||
* this is relevant only for 5000 series and up,
|
||||
|
@ -2087,7 +2064,6 @@ static void iwl_alive_start(struct iwl_priv *priv)
|
|||
goto restart;
|
||||
}
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
ret = priv->cfg->ops->lib->alive_notify(priv);
|
||||
if (ret) {
|
||||
IWL_WARN(priv,
|
||||
|
@ -2098,6 +2074,13 @@ static void iwl_alive_start(struct iwl_priv *priv)
|
|||
/* After the ALIVE response, we can send host commands to the uCode */
|
||||
set_bit(STATUS_ALIVE, &priv->status);
|
||||
|
||||
if (priv->cfg->ops->lib->recover_from_tx_stall) {
|
||||
/* Enable timer to monitor the driver queues */
|
||||
mod_timer(&priv->monitor_recover,
|
||||
jiffies +
|
||||
msecs_to_jiffies(priv->cfg->monitor_recover_period));
|
||||
}
|
||||
|
||||
if (iwl_is_rfkill(priv))
|
||||
return;
|
||||
|
||||
|
@ -2143,6 +2126,8 @@ static void iwl_alive_start(struct iwl_priv *priv)
|
|||
wake_up_interruptible(&priv->wait_command_queue);
|
||||
|
||||
iwl_power_update_mode(priv, true);
|
||||
IWL_DEBUG_INFO(priv, "Updated power mode\n");
|
||||
|
||||
|
||||
return;
|
||||
|
||||
|
@ -2162,7 +2147,7 @@ static void __iwl_down(struct iwl_priv *priv)
|
|||
if (!exit_pending)
|
||||
set_bit(STATUS_EXIT_PENDING, &priv->status);
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
iwl_clear_ucode_stations(priv, true);
|
||||
|
||||
/* Unblock any waiting calls */
|
||||
wake_up_interruptible_all(&priv->wait_command_queue);
|
||||
|
@ -2359,8 +2344,6 @@ static int __iwl_up(struct iwl_priv *priv)
|
|||
|
||||
for (i = 0; i < MAX_HW_RESTARTS; i++) {
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
|
||||
/* load bootstrap state machine,
|
||||
* load bootstrap program into processor's memory,
|
||||
* prepare to load the "initialize" uCode */
|
||||
|
@ -2501,10 +2484,6 @@ void iwl_post_associate(struct iwl_priv *priv)
|
|||
return;
|
||||
}
|
||||
|
||||
IWL_DEBUG_ASSOC(priv, "Associated as %d to: %pM\n",
|
||||
priv->assoc_id, priv->active_rxon.bssid_addr);
|
||||
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
|
@ -2556,6 +2535,9 @@ void iwl_post_associate(struct iwl_priv *priv)
|
|||
|
||||
iwlcore_commit_rxon(priv);
|
||||
|
||||
IWL_DEBUG_ASSOC(priv, "Associated as %d to: %pM\n",
|
||||
priv->assoc_id, priv->active_rxon.bssid_addr);
|
||||
|
||||
switch (priv->iw_mode) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
break;
|
||||
|
@ -2565,7 +2547,7 @@ void iwl_post_associate(struct iwl_priv *priv)
|
|||
/* assume default assoc id */
|
||||
priv->assoc_id = 1;
|
||||
|
||||
iwl_rxon_add_station(priv, priv->bssid, 0);
|
||||
iwl_add_local_station(priv, priv->bssid, true);
|
||||
iwl_send_beacon_cmd(priv);
|
||||
|
||||
break;
|
||||
|
@ -2576,9 +2558,6 @@ void iwl_post_associate(struct iwl_priv *priv)
|
|||
break;
|
||||
}
|
||||
|
||||
if (priv->iw_mode == NL80211_IFTYPE_ADHOC)
|
||||
priv->assoc_station_added = 1;
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
iwl_activate_qos(priv, 0);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
@ -2937,10 +2916,21 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw,
|
|||
return ret;
|
||||
case IEEE80211_AMPDU_TX_START:
|
||||
IWL_DEBUG_HT(priv, "start Tx\n");
|
||||
return iwl_tx_agg_start(priv, sta->addr, tid, ssn);
|
||||
ret = iwl_tx_agg_start(priv, sta->addr, tid, ssn);
|
||||
if (ret == 0) {
|
||||
priv->_agn.agg_tids_count++;
|
||||
IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
|
||||
priv->_agn.agg_tids_count);
|
||||
}
|
||||
return ret;
|
||||
case IEEE80211_AMPDU_TX_STOP:
|
||||
IWL_DEBUG_HT(priv, "stop Tx\n");
|
||||
ret = iwl_tx_agg_stop(priv, sta->addr, tid);
|
||||
if ((ret == 0) && (priv->_agn.agg_tids_count > 0)) {
|
||||
priv->_agn.agg_tids_count--;
|
||||
IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
|
||||
priv->_agn.agg_tids_count);
|
||||
}
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return 0;
|
||||
else
|
||||
|
@ -2977,18 +2967,7 @@ static void iwl_mac_sta_notify(struct ieee80211_hw *hw,
|
|||
struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
|
||||
int sta_id;
|
||||
|
||||
/*
|
||||
* TODO: We really should use this callback to
|
||||
* actually maintain the station table in
|
||||
* the device.
|
||||
*/
|
||||
|
||||
switch (cmd) {
|
||||
case STA_NOTIFY_ADD:
|
||||
atomic_set(&sta_priv->pending_frames, 0);
|
||||
if (vif->type == NL80211_IFTYPE_AP)
|
||||
sta_priv->client = true;
|
||||
break;
|
||||
case STA_NOTIFY_SLEEP:
|
||||
WARN_ON(!sta_priv->client);
|
||||
sta_priv->asleep = true;
|
||||
|
@ -3009,6 +2988,55 @@ static void iwl_mac_sta_notify(struct ieee80211_hw *hw,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* iwl_restore_wepkeys - Restore WEP keys to device
|
||||
*/
|
||||
static void iwl_restore_wepkeys(struct iwl_priv *priv)
|
||||
{
|
||||
mutex_lock(&priv->mutex);
|
||||
if (priv->iw_mode == NL80211_IFTYPE_STATION &&
|
||||
priv->default_wep_key &&
|
||||
iwl_send_static_wepkey_cmd(priv, 0))
|
||||
IWL_ERR(priv, "Could not send WEP static key\n");
|
||||
mutex_unlock(&priv->mutex);
|
||||
}
|
||||
|
||||
static int iwlagn_mac_sta_add(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
struct iwl_priv *priv = hw->priv;
|
||||
struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
|
||||
bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION;
|
||||
int ret;
|
||||
u8 sta_id;
|
||||
|
||||
IWL_DEBUG_INFO(priv, "received request to add station %pM\n",
|
||||
sta->addr);
|
||||
|
||||
atomic_set(&sta_priv->pending_frames, 0);
|
||||
if (vif->type == NL80211_IFTYPE_AP)
|
||||
sta_priv->client = true;
|
||||
|
||||
ret = iwl_add_station_common(priv, sta->addr, is_ap, &sta->ht_cap,
|
||||
&sta_id);
|
||||
if (ret) {
|
||||
IWL_ERR(priv, "Unable to add station %pM (%d)\n",
|
||||
sta->addr, ret);
|
||||
/* Should we return success if return code is EEXIST ? */
|
||||
return ret;
|
||||
}
|
||||
|
||||
iwl_restore_wepkeys(priv);
|
||||
|
||||
/* Initialize rate scaling */
|
||||
IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM \n",
|
||||
sta->addr);
|
||||
iwl_rs_rate_init(priv, sta, sta_id);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* sysfs attributes
|
||||
|
@ -3214,6 +3242,13 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
|
|||
priv->ucode_trace.data = (unsigned long)priv;
|
||||
priv->ucode_trace.function = iwl_bg_ucode_trace;
|
||||
|
||||
if (priv->cfg->ops->lib->recover_from_tx_stall) {
|
||||
init_timer(&priv->monitor_recover);
|
||||
priv->monitor_recover.data = (unsigned long)priv;
|
||||
priv->monitor_recover.function =
|
||||
priv->cfg->ops->lib->recover_from_tx_stall;
|
||||
}
|
||||
|
||||
if (!priv->cfg->use_isr_legacy)
|
||||
tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
|
||||
iwl_irq_tasklet, (unsigned long)priv);
|
||||
|
@ -3233,6 +3268,8 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
|
|||
cancel_work_sync(&priv->beacon_update);
|
||||
del_timer_sync(&priv->statistics_periodic);
|
||||
del_timer_sync(&priv->ucode_trace);
|
||||
if (priv->cfg->ops->lib->recover_from_tx_stall)
|
||||
del_timer_sync(&priv->monitor_recover);
|
||||
}
|
||||
|
||||
static void iwl_init_hw_rates(struct iwl_priv *priv,
|
||||
|
@ -3270,9 +3307,6 @@ static int iwl_init_drv(struct iwl_priv *priv)
|
|||
mutex_init(&priv->mutex);
|
||||
mutex_init(&priv->sync_cmd_mutex);
|
||||
|
||||
/* Clear the driver's (not device's) station table */
|
||||
iwl_clear_stations_table(priv);
|
||||
|
||||
priv->ieee_channels = NULL;
|
||||
priv->ieee_rates = NULL;
|
||||
priv->band = IEEE80211_BAND_2GHZ;
|
||||
|
@ -3280,6 +3314,7 @@ static int iwl_init_drv(struct iwl_priv *priv)
|
|||
priv->iw_mode = NL80211_IFTYPE_STATION;
|
||||
priv->current_ht_config.smps = IEEE80211_SMPS_STATIC;
|
||||
priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF;
|
||||
priv->_agn.agg_tids_count = 0;
|
||||
|
||||
/* initialize force reset */
|
||||
priv->force_reset[IWL_RF_RESET].reset_duration =
|
||||
|
@ -3365,6 +3400,8 @@ static struct ieee80211_ops iwl_hw_ops = {
|
|||
.ampdu_action = iwl_mac_ampdu_action,
|
||||
.hw_scan = iwl_mac_hw_scan,
|
||||
.sta_notify = iwl_mac_sta_notify,
|
||||
.sta_add = iwlagn_mac_sta_add,
|
||||
.sta_remove = iwl_mac_sta_remove,
|
||||
};
|
||||
|
||||
static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
|
@ -3468,7 +3505,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
|
||||
|
||||
iwl_hw_detect(priv);
|
||||
IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s REV=0x%X\n",
|
||||
IWL_INFO(priv, "Detected %s, REV=0x%X\n",
|
||||
priv->cfg->name, priv->hw_rev);
|
||||
|
||||
/* We disable the RETRY_TIMEOUT register (0x41) to keep
|
||||
|
@ -3649,7 +3686,6 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev)
|
|||
iwl_rx_queue_free(priv, &priv->rxq);
|
||||
iwl_hw_txq_ctx_free(priv);
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
iwl_eeprom_free(priv);
|
||||
|
||||
|
||||
|
|
|
@ -2283,8 +2283,6 @@ static int iwl_set_mode(struct iwl_priv *priv, struct ieee80211_vif *vif)
|
|||
|
||||
memcpy(priv->staging_rxon.node_addr, priv->mac_addr, ETH_ALEN);
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
|
||||
return iwlcore_commit_rxon(priv);
|
||||
}
|
||||
|
||||
|
@ -2317,6 +2315,10 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
|||
err = iwl_set_mode(priv, vif);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* Add the broadcast address so we can send broadcast frames */
|
||||
priv->cfg->ops->lib->add_bcast_station(priv);
|
||||
|
||||
goto out;
|
||||
|
||||
out_err:
|
||||
|
@ -2339,6 +2341,8 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw,
|
|||
|
||||
mutex_lock(&priv->mutex);
|
||||
|
||||
iwl_clear_ucode_stations(priv, true);
|
||||
|
||||
if (iwl_is_ready_rf(priv)) {
|
||||
iwl_scan_cancel_timeout(priv, 100);
|
||||
priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
|
||||
|
@ -2526,7 +2530,6 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
|
|||
spin_lock_irqsave(&priv->lock, flags);
|
||||
priv->assoc_id = 0;
|
||||
priv->assoc_capability = 0;
|
||||
priv->assoc_station_added = 0;
|
||||
|
||||
/* new association get rid of ibss beacon skb */
|
||||
if (priv->ibss_beacon)
|
||||
|
@ -3048,6 +3051,99 @@ int iwl_force_reset(struct iwl_priv *priv, int mode)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_force_reset);
|
||||
|
||||
/**
|
||||
* iwl_bg_monitor_recover - Timer callback to check for stuck queue and recover
|
||||
*
|
||||
* During normal condition (no queue is stuck), the timer is continually set to
|
||||
* execute every monitor_recover_period milliseconds after the last timer
|
||||
* expired. When the queue read_ptr is at the same place, the timer is
|
||||
* shorten to 100mSecs. This is
|
||||
* 1) to reduce the chance that the read_ptr may wrap around (not stuck)
|
||||
* 2) to detect the stuck queues quicker before the station and AP can
|
||||
* disassociate each other.
|
||||
*
|
||||
* This function monitors all the tx queues and recover from it if any
|
||||
* of the queues are stuck.
|
||||
* 1. It first check the cmd queue for stuck conditions. If it is stuck,
|
||||
* it will recover by resetting the firmware and return.
|
||||
* 2. Then, it checks for station association. If it associates it will check
|
||||
* other queues. If any queue is stuck, it will recover by resetting
|
||||
* the firmware.
|
||||
* Note: It the number of times the queue read_ptr to be at the same place to
|
||||
* be MAX_REPEAT+1 in order to consider to be stuck.
|
||||
*/
|
||||
/*
|
||||
* The maximum number of times the read pointer of the tx queue at the
|
||||
* same place without considering to be stuck.
|
||||
*/
|
||||
#define MAX_REPEAT (2)
|
||||
static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt)
|
||||
{
|
||||
struct iwl_tx_queue *txq;
|
||||
struct iwl_queue *q;
|
||||
|
||||
txq = &priv->txq[cnt];
|
||||
q = &txq->q;
|
||||
/* queue is empty, skip */
|
||||
if (q->read_ptr != q->write_ptr) {
|
||||
if (q->read_ptr == q->last_read_ptr) {
|
||||
/* a queue has not been read from last time */
|
||||
if (q->repeat_same_read_ptr > MAX_REPEAT) {
|
||||
IWL_ERR(priv,
|
||||
"queue %d stuck %d time. Fw reload.\n",
|
||||
q->id, q->repeat_same_read_ptr);
|
||||
q->repeat_same_read_ptr = 0;
|
||||
iwl_force_reset(priv, IWL_FW_RESET);
|
||||
} else {
|
||||
q->repeat_same_read_ptr++;
|
||||
IWL_DEBUG_RADIO(priv,
|
||||
"queue %d, not read %d time\n",
|
||||
q->id,
|
||||
q->repeat_same_read_ptr);
|
||||
mod_timer(&priv->monitor_recover, jiffies +
|
||||
msecs_to_jiffies(IWL_ONE_HUNDRED_MSECS));
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
q->last_read_ptr = q->read_ptr;
|
||||
q->repeat_same_read_ptr = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iwl_bg_monitor_recover(unsigned long data)
|
||||
{
|
||||
struct iwl_priv *priv = (struct iwl_priv *)data;
|
||||
int cnt;
|
||||
|
||||
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
|
||||
return;
|
||||
|
||||
/* monitor and check for stuck cmd queue */
|
||||
if (iwl_check_stuck_queue(priv, IWL_CMD_QUEUE_NUM))
|
||||
return;
|
||||
|
||||
/* monitor and check for other stuck queues */
|
||||
if (iwl_is_associated(priv)) {
|
||||
for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
|
||||
/* skip as we already checked the command queue */
|
||||
if (cnt == IWL_CMD_QUEUE_NUM)
|
||||
continue;
|
||||
if (iwl_check_stuck_queue(priv, cnt))
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Reschedule the timer to occur in
|
||||
* priv->cfg->monitor_recover_period
|
||||
*/
|
||||
mod_timer(&priv->monitor_recover,
|
||||
jiffies + msecs_to_jiffies(priv->cfg->monitor_recover_period));
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_bg_monitor_recover);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
|
|
|
@ -191,6 +191,14 @@ struct iwl_lib_ops {
|
|||
struct iwl_temp_ops temp_ops;
|
||||
/* station management */
|
||||
void (*add_bcast_station)(struct iwl_priv *priv);
|
||||
/* recover from tx queue stall */
|
||||
void (*recover_from_tx_stall)(unsigned long data);
|
||||
/* check for plcp health */
|
||||
bool (*check_plcp_health)(struct iwl_priv *priv,
|
||||
struct iwl_rx_packet *pkt);
|
||||
/* check for ack health */
|
||||
bool (*check_ack_health)(struct iwl_priv *priv,
|
||||
struct iwl_rx_packet *pkt);
|
||||
};
|
||||
|
||||
struct iwl_led_ops {
|
||||
|
@ -295,6 +303,8 @@ struct iwl_cfg {
|
|||
const bool support_wimax_coexist;
|
||||
u8 plcp_delta_threshold;
|
||||
s32 chain_noise_scale;
|
||||
/* timer period for monitor the driver queues */
|
||||
u32 monitor_recover_period;
|
||||
};
|
||||
|
||||
/***************************
|
||||
|
@ -430,6 +440,10 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
|
|||
struct iwl_rx_mem_buffer *rxb);
|
||||
void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv,
|
||||
struct iwl_rx_mem_buffer *rxb);
|
||||
bool iwl_good_plcp_health(struct iwl_priv *priv,
|
||||
struct iwl_rx_packet *pkt);
|
||||
bool iwl_good_ack_health(struct iwl_priv *priv,
|
||||
struct iwl_rx_packet *pkt);
|
||||
void iwl_rx_statistics(struct iwl_priv *priv,
|
||||
struct iwl_rx_mem_buffer *rxb);
|
||||
void iwl_reply_statistics(struct iwl_priv *priv,
|
||||
|
@ -568,6 +582,9 @@ static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv)
|
|||
pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
|
||||
return pci_lnk_ctl;
|
||||
}
|
||||
|
||||
void iwl_bg_monitor_recover(unsigned long data);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state);
|
||||
int iwl_pci_resume(struct pci_dev *pdev);
|
||||
|
@ -667,7 +684,7 @@ extern int iwl_send_statistics_request(struct iwl_priv *priv,
|
|||
u8 flags, bool clear);
|
||||
extern int iwl_verify_ucode(struct iwl_priv *priv);
|
||||
extern int iwl_send_lq_cmd(struct iwl_priv *priv,
|
||||
struct iwl_link_quality_cmd *lq, u8 flags);
|
||||
struct iwl_link_quality_cmd *lq, u8 flags, bool init);
|
||||
extern void iwl_rx_reply_rx(struct iwl_priv *priv,
|
||||
struct iwl_rx_mem_buffer *rxb);
|
||||
extern void iwl_rx_reply_rx_phy(struct iwl_priv *priv,
|
||||
|
|
|
@ -183,6 +183,10 @@ struct iwl_queue {
|
|||
int n_bd; /* number of BDs in this queue */
|
||||
int write_ptr; /* 1-st empty entry (index) host_w*/
|
||||
int read_ptr; /* last used entry (index) host_r*/
|
||||
/* use for monitoring and recovering the stuck queue */
|
||||
int last_read_ptr; /* storing the last read_ptr */
|
||||
/* number of time read_ptr and last_read_ptr are the same */
|
||||
u8 repeat_same_read_ptr;
|
||||
dma_addr_t dma_addr; /* physical addr for BD's */
|
||||
int n_window; /* safe queue window */
|
||||
u32 id;
|
||||
|
@ -544,11 +548,18 @@ struct iwl_qos_info {
|
|||
struct iwl_qosparam_cmd def_qos_parm;
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure should be accessed with sta_lock held. When station addition
|
||||
* is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only
|
||||
* the commands (iwl_addsta_cmd and iwl_link_quality_cmd) without sta_lock
|
||||
* held.
|
||||
*/
|
||||
struct iwl_station_entry {
|
||||
struct iwl_addsta_cmd sta;
|
||||
struct iwl_tid_data tid[MAX_TID_COUNT];
|
||||
u8 used;
|
||||
struct iwl_hw_key keyinfo;
|
||||
struct iwl_link_quality_cmd *lq;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1037,6 +1048,11 @@ struct iwl_event_log {
|
|||
#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3)
|
||||
#define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5)
|
||||
|
||||
/* timer constants use to monitor and recover stuck tx queues in mSecs */
|
||||
#define IWL_MONITORING_PERIOD (1000)
|
||||
#define IWL_ONE_HUNDRED_MSECS (100)
|
||||
#define IWL_SIXTY_SECS (60000)
|
||||
|
||||
enum iwl_reset {
|
||||
IWL_RF_RESET = 0,
|
||||
IWL_FW_RESET,
|
||||
|
@ -1163,7 +1179,6 @@ struct iwl_priv {
|
|||
|
||||
u16 active_rate;
|
||||
|
||||
u8 assoc_station_added;
|
||||
u8 start_calib;
|
||||
struct iwl_sensitivity_data sensitivity_data;
|
||||
struct iwl_chain_noise_data chain_noise_data;
|
||||
|
@ -1285,6 +1300,11 @@ struct iwl_priv {
|
|||
int ict_index;
|
||||
u32 inta;
|
||||
bool use_ict;
|
||||
/*
|
||||
* reporting the number of tids has AGG on. 0 means
|
||||
* no AGGREGATION
|
||||
*/
|
||||
u8 agg_tids_count;
|
||||
} _agn;
|
||||
#endif
|
||||
};
|
||||
|
@ -1348,6 +1368,7 @@ struct iwl_priv {
|
|||
struct work_struct run_time_calib_work;
|
||||
struct timer_list statistics_periodic;
|
||||
struct timer_list ucode_trace;
|
||||
struct timer_list monitor_recover;
|
||||
bool hw_ready;
|
||||
|
||||
struct iwl_event_log event_log;
|
||||
|
|
|
@ -616,29 +616,77 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv,
|
|||
|
||||
#define REG_RECALIB_PERIOD (60)
|
||||
|
||||
#define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n"
|
||||
void iwl_rx_statistics(struct iwl_priv *priv,
|
||||
struct iwl_rx_mem_buffer *rxb)
|
||||
/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */
|
||||
#define ACK_CNT_RATIO (50)
|
||||
#define BA_TIMEOUT_CNT (5)
|
||||
#define BA_TIMEOUT_MAX (16)
|
||||
|
||||
#if defined(CONFIG_IWLAGN) || defined(CONFIG_IWLAGN_MODULE)
|
||||
/**
|
||||
* iwl_good_ack_health - checks for ACK count ratios, BA timeout retries.
|
||||
*
|
||||
* When the ACK count ratio is 0 and aggregated BA timeout retries exceeding
|
||||
* the BA_TIMEOUT_MAX, reload firmware and bring system back to normal
|
||||
* operation state.
|
||||
*/
|
||||
bool iwl_good_ack_health(struct iwl_priv *priv,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
int change;
|
||||
struct iwl_rx_packet *pkt = rxb_addr(rxb);
|
||||
bool rc = true;
|
||||
int actual_ack_cnt_delta, expected_ack_cnt_delta;
|
||||
int ba_timeout_delta;
|
||||
|
||||
actual_ack_cnt_delta =
|
||||
le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) -
|
||||
le32_to_cpu(priv->statistics.tx.actual_ack_cnt);
|
||||
expected_ack_cnt_delta =
|
||||
le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) -
|
||||
le32_to_cpu(priv->statistics.tx.expected_ack_cnt);
|
||||
ba_timeout_delta =
|
||||
le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) -
|
||||
le32_to_cpu(priv->statistics.tx.agg.ba_timeout);
|
||||
if ((priv->_agn.agg_tids_count > 0) &&
|
||||
(expected_ack_cnt_delta > 0) &&
|
||||
(((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta)
|
||||
< ACK_CNT_RATIO) &&
|
||||
(ba_timeout_delta > BA_TIMEOUT_CNT)) {
|
||||
IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d,"
|
||||
" expected_ack_cnt = %d\n",
|
||||
actual_ack_cnt_delta, expected_ack_cnt_delta);
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta = %d\n",
|
||||
priv->delta_statistics.tx.rx_detected_cnt);
|
||||
IWL_DEBUG_RADIO(priv,
|
||||
"ack_or_ba_timeout_collision delta = %d\n",
|
||||
priv->delta_statistics.tx.
|
||||
ack_or_ba_timeout_collision);
|
||||
#endif
|
||||
IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n",
|
||||
ba_timeout_delta);
|
||||
if (!actual_ack_cnt_delta &&
|
||||
(ba_timeout_delta >= BA_TIMEOUT_MAX))
|
||||
rc = false;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_good_ack_health);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* iwl_good_plcp_health - checks for plcp error.
|
||||
*
|
||||
* When the plcp error is exceeding the thresholds, reset the radio
|
||||
* to improve the throughput.
|
||||
*/
|
||||
bool iwl_good_plcp_health(struct iwl_priv *priv,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
bool rc = true;
|
||||
int combined_plcp_delta;
|
||||
unsigned int plcp_msec;
|
||||
unsigned long plcp_received_jiffies;
|
||||
|
||||
IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
|
||||
(int)sizeof(priv->statistics),
|
||||
le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
|
||||
|
||||
change = ((priv->statistics.general.temperature !=
|
||||
pkt->u.stats.general.temperature) ||
|
||||
((priv->statistics.flag &
|
||||
STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
|
||||
(pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)));
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUG
|
||||
iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats);
|
||||
#endif
|
||||
/*
|
||||
* check for plcp_err and trigger radio reset if it exceeds
|
||||
* the plcp error threshold plcp_delta.
|
||||
|
@ -659,11 +707,11 @@ void iwl_rx_statistics(struct iwl_priv *priv,
|
|||
le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err));
|
||||
|
||||
if ((combined_plcp_delta > 0) &&
|
||||
((combined_plcp_delta * 100) / plcp_msec) >
|
||||
((combined_plcp_delta * 100) / plcp_msec) >
|
||||
priv->cfg->plcp_delta_threshold) {
|
||||
/*
|
||||
* if plcp_err exceed the threshold, the following
|
||||
* data is printed in csv format:
|
||||
* if plcp_err exceed the threshold,
|
||||
* the following data is printed in csv format:
|
||||
* Text: plcp_err exceeded %d,
|
||||
* Received ofdm.plcp_err,
|
||||
* Current ofdm.plcp_err,
|
||||
|
@ -672,22 +720,73 @@ void iwl_rx_statistics(struct iwl_priv *priv,
|
|||
* combined_plcp_delta,
|
||||
* plcp_msec
|
||||
*/
|
||||
IWL_DEBUG_RADIO(priv, PLCP_MSG,
|
||||
IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, "
|
||||
"%u, %u, %u, %u, %d, %u mSecs\n",
|
||||
priv->cfg->plcp_delta_threshold,
|
||||
le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err),
|
||||
le32_to_cpu(priv->statistics.rx.ofdm.plcp_err),
|
||||
le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err),
|
||||
le32_to_cpu(
|
||||
priv->statistics.rx.ofdm_ht.plcp_err),
|
||||
priv->statistics.rx.ofdm_ht.plcp_err),
|
||||
combined_plcp_delta, plcp_msec);
|
||||
|
||||
/*
|
||||
* Reset the RF radio due to the high plcp
|
||||
* error rate
|
||||
*/
|
||||
iwl_force_reset(priv, IWL_RF_RESET);
|
||||
rc = false;
|
||||
}
|
||||
}
|
||||
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));
|
||||
|
||||
|
|
|
@ -29,14 +29,12 @@
|
|||
|
||||
#include <net/mac80211.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "iwl-dev.h"
|
||||
#include "iwl-core.h"
|
||||
#include "iwl-sta.h"
|
||||
|
||||
#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
|
||||
#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */
|
||||
|
||||
u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr)
|
||||
{
|
||||
int i;
|
||||
|
@ -64,6 +62,19 @@ u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr)
|
|||
addr, priv->num_stations);
|
||||
|
||||
out:
|
||||
/*
|
||||
* It may be possible that more commands interacting with stations
|
||||
* arrive before we completed processing the adding of
|
||||
* station
|
||||
*/
|
||||
if (ret != IWL_INVALID_STATION &&
|
||||
(!(priv->stations[ret].used & IWL_STA_UCODE_ACTIVE) ||
|
||||
((priv->stations[ret].used & IWL_STA_UCODE_ACTIVE) &&
|
||||
(priv->stations[ret].used & IWL_STA_UCODE_INPROGRESS)))) {
|
||||
IWL_ERR(priv, "Requested station info for sta %d before ready. \n",
|
||||
ret);
|
||||
ret = IWL_INVALID_STATION;
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->sta_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
@ -158,13 +169,6 @@ static void iwl_process_add_sta_resp(struct iwl_priv *priv,
|
|||
priv->stations[sta_id].sta.mode ==
|
||||
STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
|
||||
addsta->sta.addr);
|
||||
|
||||
/*
|
||||
* Determine if we wanted to modify or add a station,
|
||||
* if adding a station succeeded we have some more initialization
|
||||
* to do when using station notification. TODO
|
||||
*/
|
||||
|
||||
spin_unlock_irqrestore(&priv->sta_lock, flags);
|
||||
}
|
||||
|
||||
|
@ -190,6 +194,10 @@ int iwl_send_add_sta(struct iwl_priv *priv,
|
|||
.flags = flags,
|
||||
.data = data,
|
||||
};
|
||||
u8 sta_id = sta->sta.sta_id;
|
||||
|
||||
IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n",
|
||||
sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : "");
|
||||
|
||||
if (flags & CMD_ASYNC)
|
||||
cmd.callback = iwl_add_sta_callback;
|
||||
|
@ -263,18 +271,19 @@ static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index,
|
|||
}
|
||||
|
||||
/**
|
||||
* iwl_add_station - Add station to tables in driver and device
|
||||
* iwl_prep_station - Prepare station information for addition
|
||||
*
|
||||
* should be called with sta_lock held
|
||||
*/
|
||||
u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags,
|
||||
struct ieee80211_sta_ht_cap *ht_info)
|
||||
static u8 iwl_prep_station(struct iwl_priv *priv, const u8 *addr,
|
||||
bool is_ap,
|
||||
struct ieee80211_sta_ht_cap *ht_info)
|
||||
{
|
||||
struct iwl_station_entry *station;
|
||||
unsigned long flags_spin;
|
||||
int i;
|
||||
int sta_id = IWL_INVALID_STATION;
|
||||
u8 sta_id = IWL_INVALID_STATION;
|
||||
u16 rate;
|
||||
|
||||
spin_lock_irqsave(&priv->sta_lock, flags_spin);
|
||||
if (is_ap)
|
||||
sta_id = IWL_AP_ID;
|
||||
else if (is_broadcast_ether_addr(addr))
|
||||
|
@ -292,20 +301,32 @@ u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags,
|
|||
sta_id = i;
|
||||
}
|
||||
|
||||
/* These two conditions have the same outcome, but keep them separate
|
||||
since they have different meanings */
|
||||
if (unlikely(sta_id == IWL_INVALID_STATION)) {
|
||||
spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
|
||||
/*
|
||||
* These two conditions have the same outcome, but keep them
|
||||
* separate
|
||||
*/
|
||||
if (unlikely(sta_id == IWL_INVALID_STATION))
|
||||
return sta_id;
|
||||
|
||||
/*
|
||||
* uCode is not able to deal with multiple requests to add a
|
||||
* station. Keep track if one is in progress so that we do not send
|
||||
* another.
|
||||
*/
|
||||
if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) {
|
||||
IWL_DEBUG_INFO(priv, "STA %d already in process of being added.\n",
|
||||
sta_id);
|
||||
return sta_id;
|
||||
}
|
||||
|
||||
if (priv->stations[sta_id].used &&
|
||||
if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) &&
|
||||
(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) &&
|
||||
!compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) {
|
||||
spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
|
||||
IWL_DEBUG_ASSOC(priv, "STA %d (%pM) already added, not adding again.\n",
|
||||
sta_id, addr);
|
||||
return sta_id;
|
||||
}
|
||||
|
||||
|
||||
station = &priv->stations[sta_id];
|
||||
station->used = IWL_STA_DRIVER_ACTIVE;
|
||||
IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n",
|
||||
|
@ -330,86 +351,185 @@ u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags,
|
|||
/* Turn on both antennas for the station... */
|
||||
station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK);
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
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;
|
||||
u8 sta_id = iwl_find_station(priv, addr);
|
||||
struct iwl_station_entry *station;
|
||||
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 */
|
||||
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;
|
||||
|
||||
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,
|
||||
struct iwl_device_cmd *cmd,
|
||||
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)
|
||||
static int iwl_send_remove_station(struct iwl_priv *priv,
|
||||
struct iwl_station_entry *station)
|
||||
{
|
||||
struct iwl_rx_packet *pkt;
|
||||
int ret;
|
||||
|
||||
unsigned long flags_spin;
|
||||
struct iwl_rem_sta_cmd rm_sta_cmd;
|
||||
|
||||
struct iwl_host_cmd cmd = {
|
||||
.id = REPLY_REMOVE_STA,
|
||||
.len = sizeof(struct iwl_rem_sta_cmd),
|
||||
.flags = flags,
|
||||
.flags = CMD_SYNC,
|
||||
.data = &rm_sta_cmd,
|
||||
};
|
||||
|
||||
memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd));
|
||||
rm_sta_cmd.num_sta = 1;
|
||||
memcpy(&rm_sta_cmd.addr, addr , ETH_ALEN);
|
||||
memcpy(&rm_sta_cmd.addr, &station->sta.sta.addr , ETH_ALEN);
|
||||
|
||||
cmd.flags |= CMD_WANT_SKB;
|
||||
|
||||
if (flags & CMD_ASYNC)
|
||||
cmd.callback = iwl_remove_sta_callback;
|
||||
else
|
||||
cmd.flags |= CMD_WANT_SKB;
|
||||
ret = iwl_send_cmd(priv, &cmd);
|
||||
|
||||
if (ret || (flags & CMD_ASYNC))
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pkt = (struct iwl_rx_packet *)cmd.reply_page;
|
||||
|
@ -422,7 +542,9 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr,
|
|||
if (!ret) {
|
||||
switch (pkt->u.rem_sta.status) {
|
||||
case REM_STA_SUCCESS_MSK:
|
||||
iwl_sta_ucode_deactivate(priv, addr);
|
||||
spin_lock_irqsave(&priv->sta_lock, flags_spin);
|
||||
iwl_sta_ucode_deactivate(priv, station->sta.sta.sta_id);
|
||||
spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
|
||||
IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n");
|
||||
break;
|
||||
default:
|
||||
|
@ -439,23 +561,35 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr,
|
|||
/**
|
||||
* iwl_remove_station - Remove driver's knowledge of station.
|
||||
*/
|
||||
int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
|
||||
static int iwl_remove_station(struct iwl_priv *priv, struct ieee80211_sta *sta)
|
||||
{
|
||||
int sta_id = IWL_INVALID_STATION;
|
||||
int i, ret = -EINVAL;
|
||||
unsigned long flags;
|
||||
bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION;
|
||||
struct iwl_station_entry *station;
|
||||
|
||||
if (!iwl_is_ready(priv)) {
|
||||
IWL_DEBUG_INFO(priv,
|
||||
"Unable to remove station %pM, device not ready. \n",
|
||||
sta->addr);
|
||||
/*
|
||||
* It is typical for stations to be removed when we are
|
||||
* going down. Return success since device will be down
|
||||
* soon anyway
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&priv->sta_lock, flags);
|
||||
|
||||
if (is_ap)
|
||||
sta_id = IWL_AP_ID;
|
||||
else if (is_broadcast_ether_addr(addr))
|
||||
sta_id = priv->hw_params.bcast_sta_id;
|
||||
else
|
||||
for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++)
|
||||
if (priv->stations[i].used &&
|
||||
!compare_ether_addr(priv->stations[i].sta.sta.addr,
|
||||
addr)) {
|
||||
sta->addr)) {
|
||||
sta_id = i;
|
||||
break;
|
||||
}
|
||||
|
@ -464,17 +598,17 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
|
|||
goto out;
|
||||
|
||||
IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n",
|
||||
sta_id, addr);
|
||||
sta_id, sta->addr);
|
||||
|
||||
if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
|
||||
IWL_ERR(priv, "Removing %pM but non DRIVER active\n",
|
||||
addr);
|
||||
IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n",
|
||||
sta->addr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
|
||||
IWL_ERR(priv, "Removing %pM but non UCODE active\n",
|
||||
addr);
|
||||
IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n",
|
||||
sta->addr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -485,9 +619,10 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
|
|||
|
||||
BUG_ON(priv->num_stations < 0);
|
||||
|
||||
station = &priv->stations[sta_id];
|
||||
spin_unlock_irqrestore(&priv->sta_lock, flags);
|
||||
|
||||
ret = iwl_send_remove_station(priv, addr, CMD_ASYNC);
|
||||
ret = iwl_send_remove_station(priv, station);
|
||||
return ret;
|
||||
out:
|
||||
spin_unlock_irqrestore(&priv->sta_lock, flags);
|
||||
|
@ -495,37 +630,122 @@ out:
|
|||
}
|
||||
|
||||
/**
|
||||
* iwl_clear_stations_table - Clear the driver's station table
|
||||
*
|
||||
* NOTE: This does not clear or otherwise alter the device's station table.
|
||||
* iwl_clear_ucode_stations() - clear entire station table driver and/or ucode
|
||||
* @priv:
|
||||
* @force: If set then the uCode station table needs to be cleared here. If
|
||||
* not set then the uCode station table has already been cleared,
|
||||
* for example after sending it a RXON command without ASSOC bit
|
||||
* set, and we just need to change driver state here.
|
||||
*/
|
||||
void iwl_clear_stations_table(struct iwl_priv *priv)
|
||||
void iwl_clear_ucode_stations(struct iwl_priv *priv, bool force)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
unsigned long flags_spin;
|
||||
bool cleared = false;
|
||||
|
||||
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) &&
|
||||
!test_bit(STATUS_EXIT_PENDING, &priv->status) &&
|
||||
iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL))
|
||||
IWL_ERR(priv, "Couldn't clear the station table\n");
|
||||
|
||||
priv->num_stations = 0;
|
||||
memset(priv->stations, 0, sizeof(priv->stations));
|
||||
|
||||
/* clean ucode key table bit map */
|
||||
priv->ucode_key_table = 0;
|
||||
|
||||
/* 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);
|
||||
if (force) {
|
||||
if (!iwl_is_ready(priv)) {
|
||||
/*
|
||||
* If device is not ready at this point the station
|
||||
* table is likely already empty (uCode not ready
|
||||
* to receive station requests) or will soon be
|
||||
* due to interface going down.
|
||||
*/
|
||||
IWL_DEBUG_INFO(priv, "Unable to remove stations from device - device not ready\n");
|
||||
} else {
|
||||
iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
spin_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)
|
||||
{
|
||||
|
@ -948,9 +1168,22 @@ static inline void iwl_dump_lq_cmd(struct iwl_priv *priv,
|
|||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* iwl_send_lq_cmd() - Send link quality command
|
||||
* @init: This command is sent as part of station initialization right
|
||||
* after station has been added.
|
||||
*
|
||||
* The link quality command is sent as the last step of station creation.
|
||||
* This is the special case in which init is set and we call a callback in
|
||||
* this case to clear the state indicating that station creation is in
|
||||
* progress.
|
||||
*/
|
||||
int iwl_send_lq_cmd(struct iwl_priv *priv,
|
||||
struct iwl_link_quality_cmd *lq, u8 flags)
|
||||
struct iwl_link_quality_cmd *lq, u8 flags, bool init)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags_spin;
|
||||
|
||||
struct iwl_host_cmd cmd = {
|
||||
.id = REPLY_TX_LINK_QUALITY_CMD,
|
||||
.len = sizeof(struct iwl_link_quality_cmd),
|
||||
|
@ -966,167 +1199,31 @@ int iwl_send_lq_cmd(struct iwl_priv *priv,
|
|||
lq->sta_id = IWL_AP_ID;
|
||||
|
||||
iwl_dump_lq_cmd(priv, lq);
|
||||
BUG_ON(init && (cmd.flags & CMD_ASYNC));
|
||||
|
||||
if (iwl_is_associated(priv) && priv->assoc_station_added)
|
||||
return iwl_send_cmd(priv, &cmd);
|
||||
iwl_dump_lq_cmd(priv, lq);
|
||||
ret = iwl_send_cmd(priv, &cmd);
|
||||
if (ret || (cmd.flags & CMD_ASYNC))
|
||||
return ret;
|
||||
|
||||
if (init) {
|
||||
IWL_DEBUG_INFO(priv, "init LQ command complete, clearing sta addition status for sta %d \n",
|
||||
lq->sta_id);
|
||||
spin_lock_irqsave(&priv->sta_lock, flags_spin);
|
||||
priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
|
||||
spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_send_lq_cmd);
|
||||
|
||||
/**
|
||||
* iwl_sta_init_lq - Initialize a station's hardware rate table
|
||||
*
|
||||
* The uCode's station table contains a table of fallback rates
|
||||
* for automatic fallback during transmission.
|
||||
*
|
||||
* NOTE: This sets up a default set of values. These will be replaced later
|
||||
* if the driver's iwl-agn-rs rate scaling algorithm is used, instead of
|
||||
* rc80211_simple.
|
||||
*
|
||||
* NOTE: Run REPLY_ADD_STA command to set up station table entry, before
|
||||
* calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
|
||||
* which requires station table entry to exist).
|
||||
*/
|
||||
static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, bool is_ap)
|
||||
{
|
||||
int i, r;
|
||||
struct iwl_link_quality_cmd link_cmd = {
|
||||
.reserved1 = 0,
|
||||
};
|
||||
u32 rate_flags;
|
||||
|
||||
/* Set up the rate scaling to start at selected rate, fall back
|
||||
* all the way down to 1M in IEEE order, and then spin on 1M */
|
||||
if (is_ap)
|
||||
r = IWL_RATE_54M_INDEX;
|
||||
else if (priv->band == IEEE80211_BAND_5GHZ)
|
||||
r = IWL_RATE_6M_INDEX;
|
||||
else
|
||||
r = IWL_RATE_1M_INDEX;
|
||||
|
||||
for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
|
||||
rate_flags = 0;
|
||||
if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
|
||||
rate_flags |= RATE_MCS_CCK_MSK;
|
||||
|
||||
rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
|
||||
RATE_MCS_ANT_POS;
|
||||
|
||||
link_cmd.rs_table[i].rate_n_flags =
|
||||
iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
|
||||
r = iwl_get_prev_ieee_rate(r);
|
||||
}
|
||||
|
||||
link_cmd.general_params.single_stream_ant_msk =
|
||||
first_antenna(priv->hw_params.valid_tx_ant);
|
||||
link_cmd.general_params.dual_stream_ant_msk = 3;
|
||||
link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
|
||||
link_cmd.agg_params.agg_time_limit =
|
||||
cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
|
||||
|
||||
/* Update the rate scaling for control frame Tx to AP */
|
||||
link_cmd.sta_id = is_ap ? IWL_AP_ID : priv->hw_params.bcast_sta_id;
|
||||
|
||||
iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD,
|
||||
sizeof(link_cmd), &link_cmd, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* iwl_rxon_add_station - add station into station table.
|
||||
*
|
||||
* there is only one AP station with id= IWL_AP_ID
|
||||
* NOTE: mutex must be held before calling this function
|
||||
*/
|
||||
int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap)
|
||||
{
|
||||
struct ieee80211_sta *sta;
|
||||
struct ieee80211_sta_ht_cap ht_config;
|
||||
struct ieee80211_sta_ht_cap *cur_ht_config = NULL;
|
||||
u8 sta_id;
|
||||
|
||||
/*
|
||||
* Set HT capabilities. It is ok to set this struct even if not using
|
||||
* HT config: the priv->current_ht_config.is_ht flag will just be false
|
||||
*/
|
||||
rcu_read_lock();
|
||||
sta = ieee80211_find_sta(priv->vif, addr);
|
||||
if (sta) {
|
||||
memcpy(&ht_config, &sta->ht_cap, sizeof(ht_config));
|
||||
cur_ht_config = &ht_config;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
/* Add station to device's station table */
|
||||
sta_id = iwl_add_station(priv, addr, is_ap, CMD_SYNC, cur_ht_config);
|
||||
|
||||
/* Set up default rate scaling table in device's station table */
|
||||
iwl_sta_init_lq(priv, addr, is_ap);
|
||||
|
||||
return sta_id;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_rxon_add_station);
|
||||
|
||||
/**
|
||||
* iwl_sta_init_bcast_lq - Initialize a bcast station's hardware rate table
|
||||
*
|
||||
* NOTE: Run REPLY_ADD_STA command to set up station table entry, before
|
||||
* calling this function (which runs REPLY_TX_LINK_QUALITY_CMD,
|
||||
* which requires station table entry to exist).
|
||||
*/
|
||||
static void iwl_sta_init_bcast_lq(struct iwl_priv *priv)
|
||||
{
|
||||
int i, r;
|
||||
struct iwl_link_quality_cmd link_cmd = {
|
||||
.reserved1 = 0,
|
||||
};
|
||||
u32 rate_flags;
|
||||
|
||||
/* Set up the rate scaling to start at selected rate, fall back
|
||||
* all the way down to 1M in IEEE order, and then spin on 1M */
|
||||
if (priv->band == IEEE80211_BAND_5GHZ)
|
||||
r = IWL_RATE_6M_INDEX;
|
||||
else
|
||||
r = IWL_RATE_1M_INDEX;
|
||||
|
||||
for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) {
|
||||
rate_flags = 0;
|
||||
if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE)
|
||||
rate_flags |= RATE_MCS_CCK_MSK;
|
||||
|
||||
rate_flags |= first_antenna(priv->hw_params.valid_tx_ant) <<
|
||||
RATE_MCS_ANT_POS;
|
||||
|
||||
link_cmd.rs_table[i].rate_n_flags =
|
||||
iwl_hw_set_rate_n_flags(iwl_rates[r].plcp, rate_flags);
|
||||
r = iwl_get_prev_ieee_rate(r);
|
||||
}
|
||||
|
||||
link_cmd.general_params.single_stream_ant_msk =
|
||||
first_antenna(priv->hw_params.valid_tx_ant);
|
||||
link_cmd.general_params.dual_stream_ant_msk = 3;
|
||||
link_cmd.agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF;
|
||||
link_cmd.agg_params.agg_time_limit =
|
||||
cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF);
|
||||
|
||||
/* Update the rate scaling for control frame Tx to AP */
|
||||
link_cmd.sta_id = priv->hw_params.bcast_sta_id;
|
||||
|
||||
iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD,
|
||||
sizeof(link_cmd), &link_cmd, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* iwl_add_bcast_station - add broadcast station into station table.
|
||||
*/
|
||||
void iwl_add_bcast_station(struct iwl_priv *priv)
|
||||
{
|
||||
IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n");
|
||||
iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL);
|
||||
|
||||
/* Set up default rate scaling table in device's station table */
|
||||
iwl_sta_init_bcast_lq(priv);
|
||||
iwl_add_local_station(priv, iwl_bcast_addr, true);
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_add_bcast_station);
|
||||
|
||||
|
@ -1136,7 +1233,14 @@ EXPORT_SYMBOL(iwl_add_bcast_station);
|
|||
void iwl3945_add_bcast_station(struct iwl_priv *priv)
|
||||
{
|
||||
IWL_DEBUG_INFO(priv, "Adding broadcast station to station table\n");
|
||||
iwl_add_station(priv, iwl_bcast_addr, false, CMD_SYNC, NULL);
|
||||
iwl_add_local_station(priv, iwl_bcast_addr, false);
|
||||
/*
|
||||
* It is assumed that when station is added more initialization
|
||||
* needs to be done, but for 3945 it is not the case and we can
|
||||
* just release station table access right here.
|
||||
*/
|
||||
priv->stations[priv->hw_params.bcast_sta_id].used &= ~IWL_STA_UCODE_INPROGRESS;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(iwl3945_add_bcast_station);
|
||||
|
||||
|
@ -1159,6 +1263,13 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr)
|
|||
/* If we are a client station in a BSS network, use the special
|
||||
* AP station entry (that's the only station we communicate with) */
|
||||
case NL80211_IFTYPE_STATION:
|
||||
/*
|
||||
* If addition of station not complete yet, which means
|
||||
* that rate scaling has not been initialized, then return
|
||||
* the broadcast station.
|
||||
*/
|
||||
if (!(priv->stations[IWL_AP_ID].used & IWL_STA_UCODE_ACTIVE))
|
||||
return priv->hw_params.bcast_sta_id;
|
||||
return IWL_AP_ID;
|
||||
|
||||
/* If we are an AP, then find the station, or use BCAST */
|
||||
|
@ -1175,13 +1286,6 @@ int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr)
|
|||
if (sta_id != IWL_INVALID_STATION)
|
||||
return sta_id;
|
||||
|
||||
/* Create new station table entry */
|
||||
sta_id = iwl_add_station(priv, hdr->addr1, false,
|
||||
CMD_ASYNC, NULL);
|
||||
|
||||
if (sta_id != IWL_INVALID_STATION)
|
||||
return sta_id;
|
||||
|
||||
IWL_DEBUG_DROP(priv, "Station %pM not in station map. "
|
||||
"Defaulting to broadcast...\n",
|
||||
hdr->addr1);
|
||||
|
@ -1291,3 +1395,19 @@ void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt)
|
|||
|
||||
iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
|
||||
}
|
||||
|
||||
int iwl_mac_sta_remove(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
int ret;
|
||||
struct iwl_priv *priv = hw->priv;
|
||||
IWL_DEBUG_INFO(priv, "received request to remove station %pM\n",
|
||||
sta->addr);
|
||||
ret = iwl_remove_station(priv, sta);
|
||||
if (ret)
|
||||
IWL_ERR(priv, "Error removing station %pM\n",
|
||||
sta->addr);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(iwl_mac_sta_remove);
|
||||
|
|
|
@ -32,6 +32,12 @@
|
|||
#define HW_KEY_DYNAMIC 0
|
||||
#define HW_KEY_DEFAULT 1
|
||||
|
||||
#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
|
||||
#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */
|
||||
#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of
|
||||
being activated */
|
||||
|
||||
|
||||
/**
|
||||
* iwl_find_station - Find station id for a given BSSID
|
||||
* @bssid: MAC address of station ID to find
|
||||
|
@ -51,18 +57,22 @@ void iwl_update_tkip_key(struct iwl_priv *priv,
|
|||
struct ieee80211_key_conf *keyconf,
|
||||
const u8 *addr, u32 iv32, u16 *phase1key);
|
||||
|
||||
int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap);
|
||||
void iwl_add_bcast_station(struct iwl_priv *priv);
|
||||
void iwl3945_add_bcast_station(struct iwl_priv *priv);
|
||||
int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, bool is_ap);
|
||||
void iwl_clear_stations_table(struct iwl_priv *priv);
|
||||
void iwl_restore_stations(struct iwl_priv *priv);
|
||||
void iwl_clear_ucode_stations(struct iwl_priv *priv, bool force);
|
||||
int iwl_get_free_ucode_key_index(struct iwl_priv *priv);
|
||||
int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
|
||||
int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
|
||||
int iwl_send_add_sta(struct iwl_priv *priv,
|
||||
struct iwl_addsta_cmd *sta, u8 flags);
|
||||
u8 iwl_add_station(struct iwl_priv *priv, const u8 *addr, bool is_ap, u8 flags,
|
||||
struct ieee80211_sta_ht_cap *ht_info);
|
||||
int iwl_add_local_station(struct iwl_priv *priv, const u8 *addr, bool init_rs);
|
||||
int iwl_add_station_common(struct iwl_priv *priv, const u8 *addr,
|
||||
bool is_ap,
|
||||
struct ieee80211_sta_ht_cap *ht_info,
|
||||
u8 *sta_id_r);
|
||||
int iwl_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta);
|
||||
void iwl_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid);
|
||||
int iwl_sta_rx_agg_start(struct iwl_priv *priv,
|
||||
const u8 *addr, int tid, u16 ssn);
|
||||
|
|
|
@ -322,6 +322,8 @@ static int iwl_queue_init(struct iwl_priv *priv, struct iwl_queue *q,
|
|||
q->high_mark = 2;
|
||||
|
||||
q->write_ptr = q->read_ptr = 0;
|
||||
q->last_read_ptr = 0;
|
||||
q->repeat_same_read_ptr = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -2480,8 +2480,6 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
|
|||
goto restart;
|
||||
}
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
|
||||
rfkill = iwl_read_prph(priv, APMG_RFKILL_REG);
|
||||
IWL_DEBUG_INFO(priv, "RFKILL status: 0x%x\n", rfkill);
|
||||
|
||||
|
@ -2503,6 +2501,13 @@ static void iwl3945_alive_start(struct iwl_priv *priv)
|
|||
/* After the ALIVE response, we can send commands to 3945 uCode */
|
||||
set_bit(STATUS_ALIVE, &priv->status);
|
||||
|
||||
if (priv->cfg->ops->lib->recover_from_tx_stall) {
|
||||
/* Enable timer to monitor the driver queues */
|
||||
mod_timer(&priv->monitor_recover,
|
||||
jiffies +
|
||||
msecs_to_jiffies(priv->cfg->monitor_recover_period));
|
||||
}
|
||||
|
||||
if (iwl_is_rfkill(priv))
|
||||
return;
|
||||
|
||||
|
@ -2558,7 +2563,8 @@ static void __iwl3945_down(struct iwl_priv *priv)
|
|||
if (!exit_pending)
|
||||
set_bit(STATUS_EXIT_PENDING, &priv->status);
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
/* Station information will now be cleared in device */
|
||||
iwl_clear_ucode_stations(priv, true);
|
||||
|
||||
/* Unblock any waiting calls */
|
||||
wake_up_interruptible_all(&priv->wait_command_queue);
|
||||
|
@ -2692,8 +2698,6 @@ static int __iwl3945_up(struct iwl_priv *priv)
|
|||
|
||||
for (i = 0; i < MAX_HW_RESTARTS; i++) {
|
||||
|
||||
iwl_clear_stations_table(priv);
|
||||
|
||||
/* load bootstrap state machine,
|
||||
* load bootstrap program into processor's memory,
|
||||
* prepare to load the "initialize" uCode */
|
||||
|
@ -3119,12 +3123,13 @@ void iwl3945_post_associate(struct iwl_priv *priv)
|
|||
case NL80211_IFTYPE_ADHOC:
|
||||
|
||||
priv->assoc_id = 1;
|
||||
iwl_add_station(priv, priv->bssid, 0, CMD_SYNC, NULL);
|
||||
iwl_add_local_station(priv, priv->bssid, false);
|
||||
iwl3945_sync_sta(priv, IWL_STA_ID,
|
||||
(priv->band == IEEE80211_BAND_5GHZ) ?
|
||||
IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP,
|
||||
(priv->band == IEEE80211_BAND_5GHZ) ?
|
||||
IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP,
|
||||
CMD_ASYNC);
|
||||
iwl3945_rate_scale_init(priv->hw, IWL_STA_ID);
|
||||
|
||||
iwl3945_send_beacon_cmd(priv);
|
||||
|
||||
break;
|
||||
|
@ -3309,7 +3314,7 @@ void iwl3945_config_ap(struct iwl_priv *priv)
|
|||
/* restore RXON assoc */
|
||||
priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
|
||||
iwlcore_commit_rxon(priv);
|
||||
iwl_add_station(priv, iwl_bcast_addr, 0, CMD_SYNC, NULL);
|
||||
iwl_add_local_station(priv, iwl_bcast_addr, false);
|
||||
}
|
||||
iwl3945_send_beacon_cmd(priv);
|
||||
|
||||
|
@ -3376,6 +3381,38 @@ static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int iwl3945_mac_sta_add(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
struct iwl_priv *priv = hw->priv;
|
||||
int ret;
|
||||
bool is_ap = priv->iw_mode == NL80211_IFTYPE_STATION;
|
||||
u8 sta_id;
|
||||
|
||||
IWL_DEBUG_INFO(priv, "received request to add station %pM\n",
|
||||
sta->addr);
|
||||
|
||||
ret = iwl_add_station_common(priv, sta->addr, is_ap, &sta->ht_cap,
|
||||
&sta_id);
|
||||
if (ret) {
|
||||
IWL_ERR(priv, "Unable to add station %pM (%d)\n",
|
||||
sta->addr, ret);
|
||||
/* Should we return success if return code is EEXIST ? */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialize rate scaling */
|
||||
IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM \n",
|
||||
sta->addr);
|
||||
iwl3945_rs_rate_init(priv, sta, sta_id);
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
/*****************************************************************************
|
||||
*
|
||||
* sysfs attributes
|
||||
|
@ -3766,6 +3803,13 @@ static void iwl3945_setup_deferred_work(struct iwl_priv *priv)
|
|||
|
||||
iwl3945_hw_setup_deferred_work(priv);
|
||||
|
||||
if (priv->cfg->ops->lib->recover_from_tx_stall) {
|
||||
init_timer(&priv->monitor_recover);
|
||||
priv->monitor_recover.data = (unsigned long)priv;
|
||||
priv->monitor_recover.function =
|
||||
priv->cfg->ops->lib->recover_from_tx_stall;
|
||||
}
|
||||
|
||||
tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
|
||||
iwl3945_irq_tasklet, (unsigned long)priv);
|
||||
}
|
||||
|
@ -3778,6 +3822,8 @@ static void iwl3945_cancel_deferred_work(struct iwl_priv *priv)
|
|||
cancel_delayed_work(&priv->scan_check);
|
||||
cancel_delayed_work(&priv->alive_start);
|
||||
cancel_work_sync(&priv->beacon_update);
|
||||
if (priv->cfg->ops->lib->recover_from_tx_stall)
|
||||
del_timer_sync(&priv->monitor_recover);
|
||||
}
|
||||
|
||||
static struct attribute *iwl3945_sysfs_entries[] = {
|
||||
|
@ -3815,7 +3861,9 @@ static struct ieee80211_ops iwl3945_hw_ops = {
|
|||
.conf_tx = iwl_mac_conf_tx,
|
||||
.reset_tsf = iwl_mac_reset_tsf,
|
||||
.bss_info_changed = iwl_bss_info_changed,
|
||||
.hw_scan = iwl_mac_hw_scan
|
||||
.hw_scan = iwl_mac_hw_scan,
|
||||
.sta_add = iwl3945_mac_sta_add,
|
||||
.sta_remove = iwl_mac_sta_remove,
|
||||
};
|
||||
|
||||
static int iwl3945_init_drv(struct iwl_priv *priv)
|
||||
|
@ -3834,9 +3882,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv)
|
|||
mutex_init(&priv->mutex);
|
||||
mutex_init(&priv->sync_cmd_mutex);
|
||||
|
||||
/* Clear the driver's (not device's) station table */
|
||||
iwl_clear_stations_table(priv);
|
||||
|
||||
priv->ieee_channels = NULL;
|
||||
priv->ieee_rates = NULL;
|
||||
priv->band = IEEE80211_BAND_2GHZ;
|
||||
|
@ -4196,7 +4241,6 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev)
|
|||
iwl3945_hw_txq_ctx_free(priv);
|
||||
|
||||
iwl3945_unset_hw_params(priv);
|
||||
iwl_clear_stations_table(priv);
|
||||
|
||||
/*netif_stop_queue(dev); */
|
||||
flush_workqueue(priv->workqueue);
|
||||
|
|
|
@ -31,6 +31,9 @@ u8 lbs_bg_rates[MAX_RATES] =
|
|||
0x00, 0x00 };
|
||||
|
||||
|
||||
static int assoc_helper_wep_keys(struct lbs_private *priv,
|
||||
struct assoc_request *assoc_req);
|
||||
|
||||
/**
|
||||
* @brief This function finds common rates between rates and card rates.
|
||||
*
|
||||
|
@ -610,7 +613,7 @@ static int lbs_assoc_post(struct lbs_private *priv,
|
|||
|
||||
if (status_code) {
|
||||
lbs_mac_event_disconnected(priv);
|
||||
ret = -1;
|
||||
ret = status_code;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -813,7 +816,24 @@ static int lbs_try_associate(struct lbs_private *priv,
|
|||
goto out;
|
||||
|
||||
ret = lbs_associate(priv, assoc_req, CMD_802_11_ASSOCIATE);
|
||||
/* If the association fails with current auth mode, let's
|
||||
* try by changing the auth mode
|
||||
*/
|
||||
if ((priv->authtype_auto) &&
|
||||
(ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) &&
|
||||
(assoc_req->secinfo.wep_enabled) &&
|
||||
(priv->connect_status != LBS_CONNECTED)) {
|
||||
if (priv->secinfo.auth_mode == IW_AUTH_ALG_OPEN_SYSTEM)
|
||||
priv->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
|
||||
else
|
||||
priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
|
||||
if (!assoc_helper_wep_keys(priv, assoc_req))
|
||||
ret = lbs_associate(priv, assoc_req,
|
||||
CMD_802_11_ASSOCIATE);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
ret = -1;
|
||||
out:
|
||||
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
||||
return ret;
|
||||
|
|
|
@ -133,6 +133,7 @@ struct lbs_private {
|
|||
u8 wpa_ie_len;
|
||||
u16 wep_tx_keyidx;
|
||||
struct enc_key wep_keys[4];
|
||||
u8 authtype_auto;
|
||||
|
||||
/* Wake On LAN */
|
||||
uint32_t wol_criteria;
|
||||
|
|
|
@ -835,6 +835,7 @@ static int lbs_init_adapter(struct lbs_private *priv)
|
|||
priv->is_auto_deep_sleep_enabled = 0;
|
||||
priv->wakeup_dev_required = 0;
|
||||
init_waitqueue_head(&priv->ds_awake_q);
|
||||
priv->authtype_auto = 1;
|
||||
|
||||
mutex_init(&priv->lock);
|
||||
|
||||
|
|
|
@ -1440,8 +1440,10 @@ static int lbs_set_encode(struct net_device *dev,
|
|||
set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
|
||||
|
||||
if (dwrq->flags & IW_ENCODE_RESTRICTED) {
|
||||
priv->authtype_auto = 0;
|
||||
assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
|
||||
} else if (dwrq->flags & IW_ENCODE_OPEN) {
|
||||
priv->authtype_auto = 0;
|
||||
assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
|
||||
}
|
||||
|
||||
|
@ -1620,8 +1622,10 @@ static int lbs_set_encodeext(struct net_device *dev,
|
|||
goto out;
|
||||
|
||||
if (dwrq->flags & IW_ENCODE_RESTRICTED) {
|
||||
priv->authtype_auto = 0;
|
||||
assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
|
||||
} else if (dwrq->flags & IW_ENCODE_OPEN) {
|
||||
priv->authtype_auto = 0;
|
||||
assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
|
||||
}
|
||||
|
||||
|
|
|
@ -1505,46 +1505,44 @@ static const struct iw_priv_args orinoco_privtab[] = {
|
|||
* Structures to export the Wireless Handlers
|
||||
*/
|
||||
|
||||
#define STD_IW_HANDLER(id, func) \
|
||||
[IW_IOCTL_IDX(id)] = (iw_handler) func
|
||||
static const iw_handler orinoco_handler[] = {
|
||||
STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit),
|
||||
STD_IW_HANDLER(SIOCGIWNAME, cfg80211_wext_giwname),
|
||||
STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq),
|
||||
STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq),
|
||||
STD_IW_HANDLER(SIOCSIWMODE, cfg80211_wext_siwmode),
|
||||
STD_IW_HANDLER(SIOCGIWMODE, cfg80211_wext_giwmode),
|
||||
STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens),
|
||||
STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens),
|
||||
STD_IW_HANDLER(SIOCGIWRANGE, cfg80211_wext_giwrange),
|
||||
STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
|
||||
STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
|
||||
STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
|
||||
STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
|
||||
STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap),
|
||||
STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap),
|
||||
STD_IW_HANDLER(SIOCSIWSCAN, cfg80211_wext_siwscan),
|
||||
STD_IW_HANDLER(SIOCGIWSCAN, cfg80211_wext_giwscan),
|
||||
STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid),
|
||||
STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid),
|
||||
STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate),
|
||||
STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate),
|
||||
STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts),
|
||||
STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts),
|
||||
STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag),
|
||||
STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag),
|
||||
STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry),
|
||||
STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode),
|
||||
STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode),
|
||||
STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower),
|
||||
STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower),
|
||||
STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie),
|
||||
STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie),
|
||||
STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme),
|
||||
STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth),
|
||||
STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth),
|
||||
STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
|
||||
STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext),
|
||||
IW_HANDLER(SIOCSIWCOMMIT, (iw_handler)orinoco_ioctl_commit),
|
||||
IW_HANDLER(SIOCGIWNAME, (iw_handler)cfg80211_wext_giwname),
|
||||
IW_HANDLER(SIOCSIWFREQ, (iw_handler)orinoco_ioctl_setfreq),
|
||||
IW_HANDLER(SIOCGIWFREQ, (iw_handler)orinoco_ioctl_getfreq),
|
||||
IW_HANDLER(SIOCSIWMODE, (iw_handler)cfg80211_wext_siwmode),
|
||||
IW_HANDLER(SIOCGIWMODE, (iw_handler)cfg80211_wext_giwmode),
|
||||
IW_HANDLER(SIOCSIWSENS, (iw_handler)orinoco_ioctl_setsens),
|
||||
IW_HANDLER(SIOCGIWSENS, (iw_handler)orinoco_ioctl_getsens),
|
||||
IW_HANDLER(SIOCGIWRANGE, (iw_handler)cfg80211_wext_giwrange),
|
||||
IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
|
||||
IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
|
||||
IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
|
||||
IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
|
||||
IW_HANDLER(SIOCSIWAP, (iw_handler)orinoco_ioctl_setwap),
|
||||
IW_HANDLER(SIOCGIWAP, (iw_handler)orinoco_ioctl_getwap),
|
||||
IW_HANDLER(SIOCSIWSCAN, (iw_handler)cfg80211_wext_siwscan),
|
||||
IW_HANDLER(SIOCGIWSCAN, (iw_handler)cfg80211_wext_giwscan),
|
||||
IW_HANDLER(SIOCSIWESSID, (iw_handler)orinoco_ioctl_setessid),
|
||||
IW_HANDLER(SIOCGIWESSID, (iw_handler)orinoco_ioctl_getessid),
|
||||
IW_HANDLER(SIOCSIWRATE, (iw_handler)orinoco_ioctl_setrate),
|
||||
IW_HANDLER(SIOCGIWRATE, (iw_handler)orinoco_ioctl_getrate),
|
||||
IW_HANDLER(SIOCSIWRTS, (iw_handler)orinoco_ioctl_setrts),
|
||||
IW_HANDLER(SIOCGIWRTS, (iw_handler)orinoco_ioctl_getrts),
|
||||
IW_HANDLER(SIOCSIWFRAG, (iw_handler)orinoco_ioctl_setfrag),
|
||||
IW_HANDLER(SIOCGIWFRAG, (iw_handler)orinoco_ioctl_getfrag),
|
||||
IW_HANDLER(SIOCGIWRETRY, (iw_handler)orinoco_ioctl_getretry),
|
||||
IW_HANDLER(SIOCSIWENCODE, (iw_handler)orinoco_ioctl_setiwencode),
|
||||
IW_HANDLER(SIOCGIWENCODE, (iw_handler)orinoco_ioctl_getiwencode),
|
||||
IW_HANDLER(SIOCSIWPOWER, (iw_handler)orinoco_ioctl_setpower),
|
||||
IW_HANDLER(SIOCGIWPOWER, (iw_handler)orinoco_ioctl_getpower),
|
||||
IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie),
|
||||
IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie),
|
||||
IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme),
|
||||
IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth),
|
||||
IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth),
|
||||
IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
|
||||
IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext),
|
||||
};
|
||||
|
||||
|
||||
|
@ -1552,15 +1550,15 @@ static const iw_handler orinoco_handler[] = {
|
|||
Added typecasting since we no longer use iwreq_data -- Moustafa
|
||||
*/
|
||||
static const iw_handler orinoco_private_handler[] = {
|
||||
[0] = (iw_handler) orinoco_ioctl_reset,
|
||||
[1] = (iw_handler) orinoco_ioctl_reset,
|
||||
[2] = (iw_handler) orinoco_ioctl_setport3,
|
||||
[3] = (iw_handler) orinoco_ioctl_getport3,
|
||||
[4] = (iw_handler) orinoco_ioctl_setpreamble,
|
||||
[5] = (iw_handler) orinoco_ioctl_getpreamble,
|
||||
[6] = (iw_handler) orinoco_ioctl_setibssport,
|
||||
[7] = (iw_handler) orinoco_ioctl_getibssport,
|
||||
[9] = (iw_handler) orinoco_ioctl_getrid,
|
||||
[0] = (iw_handler)orinoco_ioctl_reset,
|
||||
[1] = (iw_handler)orinoco_ioctl_reset,
|
||||
[2] = (iw_handler)orinoco_ioctl_setport3,
|
||||
[3] = (iw_handler)orinoco_ioctl_getport3,
|
||||
[4] = (iw_handler)orinoco_ioctl_setpreamble,
|
||||
[5] = (iw_handler)orinoco_ioctl_getpreamble,
|
||||
[6] = (iw_handler)orinoco_ioctl_setibssport,
|
||||
[7] = (iw_handler)orinoco_ioctl_getibssport,
|
||||
[9] = (iw_handler)orinoco_ioctl_getrid,
|
||||
};
|
||||
|
||||
const struct iw_handler_def orinoco_handler_def = {
|
||||
|
|
|
@ -1113,10 +1113,10 @@ static const struct ethtool_ops netdev_ethtool_ops = {
|
|||
/*
|
||||
* Wireless Handler : get protocol name
|
||||
*/
|
||||
static int ray_get_name(struct net_device *dev,
|
||||
struct iw_request_info *info, char *cwrq, char *extra)
|
||||
static int ray_get_name(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
strcpy(cwrq, "IEEE 802.11-FH");
|
||||
strcpy(wrqu->name, "IEEE 802.11-FH");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1124,9 +1124,8 @@ static int ray_get_name(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : set frequency
|
||||
*/
|
||||
static int ray_set_freq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *fwrq, char *extra)
|
||||
static int ray_set_freq(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
int err = -EINPROGRESS; /* Call commit handler */
|
||||
|
@ -1136,10 +1135,10 @@ static int ray_set_freq(struct net_device *dev,
|
|||
return -EBUSY;
|
||||
|
||||
/* Setting by channel number */
|
||||
if ((fwrq->m > USA_HOP_MOD) || (fwrq->e > 0))
|
||||
if ((wrqu->freq.m > USA_HOP_MOD) || (wrqu->freq.e > 0))
|
||||
err = -EOPNOTSUPP;
|
||||
else
|
||||
local->sparm.b5.a_hop_pattern = fwrq->m;
|
||||
local->sparm.b5.a_hop_pattern = wrqu->freq.m;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -1148,14 +1147,13 @@ static int ray_set_freq(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : get frequency
|
||||
*/
|
||||
static int ray_get_freq(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_freq *fwrq, char *extra)
|
||||
static int ray_get_freq(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
|
||||
fwrq->m = local->sparm.b5.a_hop_pattern;
|
||||
fwrq->e = 0;
|
||||
wrqu->freq.m = local->sparm.b5.a_hop_pattern;
|
||||
wrqu->freq.e = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1163,9 +1161,8 @@ static int ray_get_freq(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : set ESSID
|
||||
*/
|
||||
static int ray_set_essid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *dwrq, char *extra)
|
||||
static int ray_set_essid(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
|
||||
|
@ -1174,19 +1171,17 @@ static int ray_set_essid(struct net_device *dev,
|
|||
return -EBUSY;
|
||||
|
||||
/* Check if we asked for `any' */
|
||||
if (dwrq->flags == 0) {
|
||||
if (wrqu->essid.flags == 0)
|
||||
/* Corey : can you do that ? */
|
||||
return -EOPNOTSUPP;
|
||||
} else {
|
||||
/* Check the size of the string */
|
||||
if (dwrq->length > IW_ESSID_MAX_SIZE) {
|
||||
return -E2BIG;
|
||||
}
|
||||
|
||||
/* Set the ESSID in the card */
|
||||
memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE);
|
||||
memcpy(local->sparm.b5.a_current_ess_id, extra, dwrq->length);
|
||||
}
|
||||
/* Check the size of the string */
|
||||
if (wrqu->essid.length > IW_ESSID_MAX_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
/* Set the ESSID in the card */
|
||||
memset(local->sparm.b5.a_current_ess_id, 0, IW_ESSID_MAX_SIZE);
|
||||
memcpy(local->sparm.b5.a_current_ess_id, extra, wrqu->essid.length);
|
||||
|
||||
return -EINPROGRESS; /* Call commit handler */
|
||||
}
|
||||
|
@ -1195,9 +1190,8 @@ static int ray_set_essid(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : get ESSID
|
||||
*/
|
||||
static int ray_get_essid(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *dwrq, char *extra)
|
||||
static int ray_get_essid(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
|
||||
|
@ -1205,8 +1199,8 @@ static int ray_get_essid(struct net_device *dev,
|
|||
memcpy(extra, local->sparm.b5.a_current_ess_id, IW_ESSID_MAX_SIZE);
|
||||
|
||||
/* Push it out ! */
|
||||
dwrq->length = strlen(extra);
|
||||
dwrq->flags = 1; /* active */
|
||||
wrqu->essid.length = strlen(extra);
|
||||
wrqu->essid.flags = 1; /* active */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1215,14 +1209,13 @@ static int ray_get_essid(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : get AP address
|
||||
*/
|
||||
static int ray_get_wap(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct sockaddr *awrq, char *extra)
|
||||
static int ray_get_wap(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
|
||||
memcpy(awrq->sa_data, local->bss_id, ETH_ALEN);
|
||||
awrq->sa_family = ARPHRD_ETHER;
|
||||
memcpy(wrqu->ap_addr.sa_data, local->bss_id, ETH_ALEN);
|
||||
wrqu->ap_addr.sa_family = ARPHRD_ETHER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1231,9 +1224,8 @@ static int ray_get_wap(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : set Bit-Rate
|
||||
*/
|
||||
static int ray_set_rate(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *vwrq, char *extra)
|
||||
static int ray_set_rate(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
|
||||
|
@ -1242,15 +1234,15 @@ static int ray_set_rate(struct net_device *dev,
|
|||
return -EBUSY;
|
||||
|
||||
/* Check if rate is in range */
|
||||
if ((vwrq->value != 1000000) && (vwrq->value != 2000000))
|
||||
if ((wrqu->bitrate.value != 1000000) && (wrqu->bitrate.value != 2000000))
|
||||
return -EINVAL;
|
||||
|
||||
/* Hack for 1.5 Mb/s instead of 2 Mb/s */
|
||||
if ((local->fw_ver == 0x55) && /* Please check */
|
||||
(vwrq->value == 2000000))
|
||||
(wrqu->bitrate.value == 2000000))
|
||||
local->net_default_tx_rate = 3;
|
||||
else
|
||||
local->net_default_tx_rate = vwrq->value / 500000;
|
||||
local->net_default_tx_rate = wrqu->bitrate.value / 500000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1259,17 +1251,16 @@ static int ray_set_rate(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : get Bit-Rate
|
||||
*/
|
||||
static int ray_get_rate(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *vwrq, char *extra)
|
||||
static int ray_get_rate(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
|
||||
if (local->net_default_tx_rate == 3)
|
||||
vwrq->value = 2000000; /* Hum... */
|
||||
wrqu->bitrate.value = 2000000; /* Hum... */
|
||||
else
|
||||
vwrq->value = local->net_default_tx_rate * 500000;
|
||||
vwrq->fixed = 0; /* We are in auto mode */
|
||||
wrqu->bitrate.value = local->net_default_tx_rate * 500000;
|
||||
wrqu->bitrate.fixed = 0; /* We are in auto mode */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1278,19 +1269,18 @@ static int ray_get_rate(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : set RTS threshold
|
||||
*/
|
||||
static int ray_set_rts(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *vwrq, char *extra)
|
||||
static int ray_set_rts(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
int rthr = vwrq->value;
|
||||
int rthr = wrqu->rts.value;
|
||||
|
||||
/* Reject if card is already initialised */
|
||||
if (local->card_status != CARD_AWAITING_PARAM)
|
||||
return -EBUSY;
|
||||
|
||||
/* if(wrq->u.rts.fixed == 0) we should complain */
|
||||
if (vwrq->disabled)
|
||||
if (wrqu->rts.disabled)
|
||||
rthr = 32767;
|
||||
else {
|
||||
if ((rthr < 0) || (rthr > 2347)) /* What's the max packet size ??? */
|
||||
|
@ -1306,16 +1296,15 @@ static int ray_set_rts(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : get RTS threshold
|
||||
*/
|
||||
static int ray_get_rts(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *vwrq, char *extra)
|
||||
static int ray_get_rts(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
|
||||
vwrq->value = (local->sparm.b5.a_rts_threshold[0] << 8)
|
||||
wrqu->rts.value = (local->sparm.b5.a_rts_threshold[0] << 8)
|
||||
+ local->sparm.b5.a_rts_threshold[1];
|
||||
vwrq->disabled = (vwrq->value == 32767);
|
||||
vwrq->fixed = 1;
|
||||
wrqu->rts.disabled = (wrqu->rts.value == 32767);
|
||||
wrqu->rts.fixed = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1324,19 +1313,18 @@ static int ray_get_rts(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : set Fragmentation threshold
|
||||
*/
|
||||
static int ray_set_frag(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *vwrq, char *extra)
|
||||
static int ray_set_frag(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
int fthr = vwrq->value;
|
||||
int fthr = wrqu->frag.value;
|
||||
|
||||
/* Reject if card is already initialised */
|
||||
if (local->card_status != CARD_AWAITING_PARAM)
|
||||
return -EBUSY;
|
||||
|
||||
/* if(wrq->u.frag.fixed == 0) should complain */
|
||||
if (vwrq->disabled)
|
||||
if (wrqu->frag.disabled)
|
||||
fthr = 32767;
|
||||
else {
|
||||
if ((fthr < 256) || (fthr > 2347)) /* To check out ! */
|
||||
|
@ -1352,16 +1340,15 @@ static int ray_set_frag(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : get Fragmentation threshold
|
||||
*/
|
||||
static int ray_get_frag(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_param *vwrq, char *extra)
|
||||
static int ray_get_frag(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
|
||||
vwrq->value = (local->sparm.b5.a_frag_threshold[0] << 8)
|
||||
wrqu->frag.value = (local->sparm.b5.a_frag_threshold[0] << 8)
|
||||
+ local->sparm.b5.a_frag_threshold[1];
|
||||
vwrq->disabled = (vwrq->value == 32767);
|
||||
vwrq->fixed = 1;
|
||||
wrqu->frag.disabled = (wrqu->frag.value == 32767);
|
||||
wrqu->frag.fixed = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1370,8 +1357,8 @@ static int ray_get_frag(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : set Mode of Operation
|
||||
*/
|
||||
static int ray_set_mode(struct net_device *dev,
|
||||
struct iw_request_info *info, __u32 *uwrq, char *extra)
|
||||
static int ray_set_mode(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
int err = -EINPROGRESS; /* Call commit handler */
|
||||
|
@ -1381,7 +1368,7 @@ static int ray_set_mode(struct net_device *dev,
|
|||
if (local->card_status != CARD_AWAITING_PARAM)
|
||||
return -EBUSY;
|
||||
|
||||
switch (*uwrq) {
|
||||
switch (wrqu->mode) {
|
||||
case IW_MODE_ADHOC:
|
||||
card_mode = 0;
|
||||
/* Fall through */
|
||||
|
@ -1399,15 +1386,15 @@ static int ray_set_mode(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : get Mode of Operation
|
||||
*/
|
||||
static int ray_get_mode(struct net_device *dev,
|
||||
struct iw_request_info *info, __u32 *uwrq, char *extra)
|
||||
static int ray_get_mode(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
ray_dev_t *local = netdev_priv(dev);
|
||||
|
||||
if (local->sparm.b5.a_network_type)
|
||||
*uwrq = IW_MODE_INFRA;
|
||||
wrqu->mode = IW_MODE_INFRA;
|
||||
else
|
||||
*uwrq = IW_MODE_ADHOC;
|
||||
wrqu->mode = IW_MODE_ADHOC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1416,16 +1403,15 @@ static int ray_get_mode(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Handler : get range info
|
||||
*/
|
||||
static int ray_get_range(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
struct iw_point *dwrq, char *extra)
|
||||
static int ray_get_range(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
struct iw_range *range = (struct iw_range *)extra;
|
||||
|
||||
memset((char *)range, 0, sizeof(struct iw_range));
|
||||
memset(range, 0, sizeof(struct iw_range));
|
||||
|
||||
/* Set the length (very important for backward compatibility) */
|
||||
dwrq->length = sizeof(struct iw_range);
|
||||
wrqu->data.length = sizeof(struct iw_range);
|
||||
|
||||
/* Set the Wireless Extension versions */
|
||||
range->we_version_compiled = WIRELESS_EXT;
|
||||
|
@ -1448,8 +1434,7 @@ static int ray_get_range(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Private Handler : set framing mode
|
||||
*/
|
||||
static int ray_set_framing(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
static int ray_set_framing(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
translate = *(extra); /* Set framing mode */
|
||||
|
@ -1461,8 +1446,7 @@ static int ray_set_framing(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Private Handler : get framing mode
|
||||
*/
|
||||
static int ray_get_framing(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
static int ray_get_framing(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
*(extra) = translate;
|
||||
|
@ -1474,8 +1458,7 @@ static int ray_get_framing(struct net_device *dev,
|
|||
/*
|
||||
* Wireless Private Handler : get country
|
||||
*/
|
||||
static int ray_get_country(struct net_device *dev,
|
||||
struct iw_request_info *info,
|
||||
static int ray_get_country(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
*(extra) = country;
|
||||
|
@ -1487,10 +1470,9 @@ static int ray_get_country(struct net_device *dev,
|
|||
/*
|
||||
* Commit handler : called after a bunch of SET operations
|
||||
*/
|
||||
static int ray_commit(struct net_device *dev, struct iw_request_info *info, /* NULL */
|
||||
void *zwrq, /* NULL */
|
||||
char *extra)
|
||||
{ /* NULL */
|
||||
static int ray_commit(struct net_device *dev, struct iw_request_info *info,
|
||||
union iwreq_data *wrqu, char *extra)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1531,28 +1513,28 @@ static iw_stats *ray_get_wireless_stats(struct net_device *dev)
|
|||
*/
|
||||
|
||||
static const iw_handler ray_handler[] = {
|
||||
[SIOCSIWCOMMIT - SIOCIWFIRST] = (iw_handler) ray_commit,
|
||||
[SIOCGIWNAME - SIOCIWFIRST] = (iw_handler) ray_get_name,
|
||||
[SIOCSIWFREQ - SIOCIWFIRST] = (iw_handler) ray_set_freq,
|
||||
[SIOCGIWFREQ - SIOCIWFIRST] = (iw_handler) ray_get_freq,
|
||||
[SIOCSIWMODE - SIOCIWFIRST] = (iw_handler) ray_set_mode,
|
||||
[SIOCGIWMODE - SIOCIWFIRST] = (iw_handler) ray_get_mode,
|
||||
[SIOCGIWRANGE - SIOCIWFIRST] = (iw_handler) ray_get_range,
|
||||
IW_HANDLER(SIOCSIWCOMMIT, ray_commit),
|
||||
IW_HANDLER(SIOCGIWNAME, ray_get_name),
|
||||
IW_HANDLER(SIOCSIWFREQ, ray_set_freq),
|
||||
IW_HANDLER(SIOCGIWFREQ, ray_get_freq),
|
||||
IW_HANDLER(SIOCSIWMODE, ray_set_mode),
|
||||
IW_HANDLER(SIOCGIWMODE, ray_get_mode),
|
||||
IW_HANDLER(SIOCGIWRANGE, ray_get_range),
|
||||
#ifdef WIRELESS_SPY
|
||||
[SIOCSIWSPY - SIOCIWFIRST] = (iw_handler) iw_handler_set_spy,
|
||||
[SIOCGIWSPY - SIOCIWFIRST] = (iw_handler) iw_handler_get_spy,
|
||||
[SIOCSIWTHRSPY - SIOCIWFIRST] = (iw_handler) iw_handler_set_thrspy,
|
||||
[SIOCGIWTHRSPY - SIOCIWFIRST] = (iw_handler) iw_handler_get_thrspy,
|
||||
IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
|
||||
IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
|
||||
IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
|
||||
IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
|
||||
#endif /* WIRELESS_SPY */
|
||||
[SIOCGIWAP - SIOCIWFIRST] = (iw_handler) ray_get_wap,
|
||||
[SIOCSIWESSID - SIOCIWFIRST] = (iw_handler) ray_set_essid,
|
||||
[SIOCGIWESSID - SIOCIWFIRST] = (iw_handler) ray_get_essid,
|
||||
[SIOCSIWRATE - SIOCIWFIRST] = (iw_handler) ray_set_rate,
|
||||
[SIOCGIWRATE - SIOCIWFIRST] = (iw_handler) ray_get_rate,
|
||||
[SIOCSIWRTS - SIOCIWFIRST] = (iw_handler) ray_set_rts,
|
||||
[SIOCGIWRTS - SIOCIWFIRST] = (iw_handler) ray_get_rts,
|
||||
[SIOCSIWFRAG - SIOCIWFIRST] = (iw_handler) ray_set_frag,
|
||||
[SIOCGIWFRAG - SIOCIWFIRST] = (iw_handler) ray_get_frag,
|
||||
IW_HANDLER(SIOCGIWAP, ray_get_wap),
|
||||
IW_HANDLER(SIOCSIWESSID, ray_set_essid),
|
||||
IW_HANDLER(SIOCGIWESSID, ray_get_essid),
|
||||
IW_HANDLER(SIOCSIWRATE, ray_set_rate),
|
||||
IW_HANDLER(SIOCGIWRATE, ray_get_rate),
|
||||
IW_HANDLER(SIOCSIWRTS, ray_set_rts),
|
||||
IW_HANDLER(SIOCGIWRTS, ray_get_rts),
|
||||
IW_HANDLER(SIOCSIWFRAG, ray_set_frag),
|
||||
IW_HANDLER(SIOCGIWFRAG, ray_get_frag),
|
||||
};
|
||||
|
||||
#define SIOCSIPFRAMING SIOCIWFIRSTPRIV /* Set framing mode */
|
||||
|
@ -1560,9 +1542,9 @@ static const iw_handler ray_handler[] = {
|
|||
#define SIOCGIPCOUNTRY SIOCIWFIRSTPRIV + 3 /* Get country code */
|
||||
|
||||
static const iw_handler ray_private_handler[] = {
|
||||
[0] = (iw_handler) ray_set_framing,
|
||||
[1] = (iw_handler) ray_get_framing,
|
||||
[3] = (iw_handler) ray_get_country,
|
||||
[0] = ray_set_framing,
|
||||
[1] = ray_get_framing,
|
||||
[3] = ray_get_country,
|
||||
};
|
||||
|
||||
static const struct iw_priv_args ray_private_args[] = {
|
||||
|
|
|
@ -53,6 +53,8 @@ enum {
|
|||
DEBUG_MAC80211 = BIT(11),
|
||||
DEBUG_CMD = BIT(12),
|
||||
DEBUG_ACX = BIT(13),
|
||||
DEBUG_SDIO = BIT(14),
|
||||
DEBUG_FILTERS = BIT(15),
|
||||
DEBUG_ALL = ~0,
|
||||
};
|
||||
|
||||
|
@ -344,12 +346,14 @@ struct wl1271_if_operations {
|
|||
bool fixed);
|
||||
void (*reset)(struct wl1271 *wl);
|
||||
void (*init)(struct wl1271 *wl);
|
||||
void (*power)(struct wl1271 *wl, bool enable);
|
||||
struct device* (*dev)(struct wl1271 *wl);
|
||||
void (*enable_irq)(struct wl1271 *wl);
|
||||
void (*disable_irq)(struct wl1271 *wl);
|
||||
};
|
||||
|
||||
struct wl1271 {
|
||||
struct platform_device *plat_dev;
|
||||
struct ieee80211_hw *hw;
|
||||
bool mac80211_registered;
|
||||
|
||||
|
@ -456,6 +460,7 @@ struct wl1271 {
|
|||
/* Default key (for WEP) */
|
||||
u32 default_key;
|
||||
|
||||
unsigned int filters;
|
||||
unsigned int rx_config;
|
||||
unsigned int rx_filter;
|
||||
|
||||
|
@ -483,6 +488,8 @@ struct wl1271 {
|
|||
/* Current chipset configuration */
|
||||
struct conf_drv_settings conf;
|
||||
|
||||
bool sg_enabled;
|
||||
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
|
|
|
@ -534,7 +534,7 @@ out:
|
|||
}
|
||||
|
||||
|
||||
int wl1271_acx_sg_enable(struct wl1271 *wl)
|
||||
int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable)
|
||||
{
|
||||
struct acx_bt_wlan_coex *pta;
|
||||
int ret;
|
||||
|
@ -547,7 +547,10 @@ int wl1271_acx_sg_enable(struct wl1271 *wl)
|
|||
goto out;
|
||||
}
|
||||
|
||||
pta->enable = SG_ENABLE;
|
||||
if (enable)
|
||||
pta->enable = wl->conf.sg.state;
|
||||
else
|
||||
pta->enable = CONF_SG_DISABLE;
|
||||
|
||||
ret = wl1271_cmd_configure(wl, ACX_SG_ENABLE, pta, sizeof(*pta));
|
||||
if (ret < 0) {
|
||||
|
@ -564,7 +567,7 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl)
|
|||
{
|
||||
struct acx_bt_wlan_coex_param *param;
|
||||
struct conf_sg_settings *c = &wl->conf.sg;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
wl1271_debug(DEBUG_ACX, "acx sg cfg");
|
||||
|
||||
|
@ -575,19 +578,9 @@ int wl1271_acx_sg_cfg(struct wl1271 *wl)
|
|||
}
|
||||
|
||||
/* BT-WLAN coext parameters */
|
||||
param->per_threshold = cpu_to_le32(c->per_threshold);
|
||||
param->max_scan_compensation_time =
|
||||
cpu_to_le32(c->max_scan_compensation_time);
|
||||
param->nfs_sample_interval = cpu_to_le16(c->nfs_sample_interval);
|
||||
param->load_ratio = c->load_ratio;
|
||||
param->auto_ps_mode = c->auto_ps_mode;
|
||||
param->probe_req_compensation = c->probe_req_compensation;
|
||||
param->scan_window_compensation = c->scan_window_compensation;
|
||||
param->antenna_config = c->antenna_config;
|
||||
param->beacon_miss_threshold = c->beacon_miss_threshold;
|
||||
param->rate_adaptation_threshold =
|
||||
cpu_to_le32(c->rate_adaptation_threshold);
|
||||
param->rate_adaptation_snr = c->rate_adaptation_snr;
|
||||
for (i = 0; i < CONF_SG_PARAMS_MAX; i++)
|
||||
param->params[i] = c->params[i];
|
||||
param->param_idx = CONF_SG_PARAMS_ALL;
|
||||
|
||||
ret = wl1271_cmd_configure(wl, ACX_SG_CFG, param, sizeof(*param));
|
||||
if (ret < 0) {
|
||||
|
|
|
@ -392,29 +392,21 @@ struct acx_conn_monit_params {
|
|||
__le32 bss_lose_timeout; /* number of TU's from synch fail */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum {
|
||||
SG_ENABLE = 0,
|
||||
SG_DISABLE,
|
||||
SG_SENSE_NO_ACTIVITY,
|
||||
SG_SENSE_ACTIVE
|
||||
};
|
||||
|
||||
struct acx_bt_wlan_coex {
|
||||
struct acx_header header;
|
||||
|
||||
/*
|
||||
* 0 -> PTA enabled
|
||||
* 1 -> PTA disabled
|
||||
* 2 -> sense no active mode, i.e.
|
||||
* an interrupt is sent upon
|
||||
* BT activity.
|
||||
* 3 -> PTA is switched on in response
|
||||
* to the interrupt sending.
|
||||
*/
|
||||
u8 enable;
|
||||
u8 pad[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct acx_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_header header;
|
||||
|
||||
|
@ -423,52 +415,6 @@ struct acx_dco_itrim_params {
|
|||
__le32 timeout;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define PTA_ANTENNA_TYPE_DEF (0)
|
||||
#define PTA_BT_HP_MAXTIME_DEF (2000)
|
||||
#define PTA_WLAN_HP_MAX_TIME_DEF (5000)
|
||||
#define PTA_SENSE_DISABLE_TIMER_DEF (1350)
|
||||
#define PTA_PROTECTIVE_RX_TIME_DEF (1500)
|
||||
#define PTA_PROTECTIVE_TX_TIME_DEF (1500)
|
||||
#define PTA_TIMEOUT_NEXT_BT_LP_PACKET_DEF (3000)
|
||||
#define PTA_SIGNALING_TYPE_DEF (1)
|
||||
#define PTA_AFH_LEVERAGE_ON_DEF (0)
|
||||
#define PTA_NUMBER_QUIET_CYCLE_DEF (0)
|
||||
#define PTA_MAX_NUM_CTS_DEF (3)
|
||||
#define PTA_NUMBER_OF_WLAN_PACKETS_DEF (2)
|
||||
#define PTA_NUMBER_OF_BT_PACKETS_DEF (2)
|
||||
#define PTA_PROTECTIVE_RX_TIME_FAST_DEF (1500)
|
||||
#define PTA_PROTECTIVE_TX_TIME_FAST_DEF (3000)
|
||||
#define PTA_CYCLE_TIME_FAST_DEF (8700)
|
||||
#define PTA_RX_FOR_AVALANCHE_DEF (5)
|
||||
#define PTA_ELP_HP_DEF (0)
|
||||
#define PTA_ANTI_STARVE_PERIOD_DEF (500)
|
||||
#define PTA_ANTI_STARVE_NUM_CYCLE_DEF (4)
|
||||
#define PTA_ALLOW_PA_SD_DEF (1)
|
||||
#define PTA_TIME_BEFORE_BEACON_DEF (6300)
|
||||
#define PTA_HPDM_MAX_TIME_DEF (1600)
|
||||
#define PTA_TIME_OUT_NEXT_WLAN_DEF (2550)
|
||||
#define PTA_AUTO_MODE_NO_CTS_DEF (0)
|
||||
#define PTA_BT_HP_RESPECTED_DEF (3)
|
||||
#define PTA_WLAN_RX_MIN_RATE_DEF (24)
|
||||
#define PTA_ACK_MODE_DEF (1)
|
||||
|
||||
struct acx_bt_wlan_coex_param {
|
||||
struct acx_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_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_table(struct wl1271 *wl);
|
||||
int wl1271_acx_conn_monit_params(struct wl1271 *wl);
|
||||
int wl1271_acx_sg_enable(struct wl1271 *wl);
|
||||
int wl1271_acx_sg_enable(struct wl1271 *wl, bool enable);
|
||||
int wl1271_acx_sg_cfg(struct wl1271 *wl);
|
||||
int wl1271_acx_cca_threshold(struct wl1271 *wl);
|
||||
int wl1271_acx_bcn_dtim_options(struct wl1271 *wl);
|
||||
|
|
|
@ -228,6 +228,14 @@ static int wl1271_boot_upload_nvs(struct wl1271 *wl)
|
|||
nvs_len = sizeof(wl->nvs->nvs);
|
||||
nvs_ptr = (u8 *)wl->nvs->nvs;
|
||||
|
||||
/* update current MAC address to NVS */
|
||||
nvs_ptr[11] = wl->mac_addr[0];
|
||||
nvs_ptr[10] = wl->mac_addr[1];
|
||||
nvs_ptr[6] = wl->mac_addr[2];
|
||||
nvs_ptr[5] = wl->mac_addr[3];
|
||||
nvs_ptr[4] = wl->mac_addr[4];
|
||||
nvs_ptr[3] = wl->mac_addr[5];
|
||||
|
||||
/*
|
||||
* Layout before the actual NVS tables:
|
||||
* 1 byte : burst length.
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/crc7.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ieee80211.h>
|
||||
|
||||
#include "wl1271.h"
|
||||
#include "wl1271_reg.h"
|
||||
|
@ -280,15 +281,6 @@ int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)
|
|||
join->rx_filter_options = cpu_to_le32(wl->rx_filter);
|
||||
join->bss_type = bss_type;
|
||||
|
||||
/*
|
||||
* FIXME: disable temporarily all filters because after commit
|
||||
* 9cef8737 "mac80211: fix managed mode BSSID handling" broke
|
||||
* association. The filter logic needs to be implemented properly
|
||||
* and once that is done, this hack can be removed.
|
||||
*/
|
||||
join->rx_config_options = cpu_to_le32(0);
|
||||
join->rx_filter_options = cpu_to_le32(WL1271_DEFAULT_RX_FILTER);
|
||||
|
||||
if (wl->band == IEEE80211_BAND_2GHZ)
|
||||
join->basic_rate_set = cpu_to_le32(CONF_HW_BIT_RATE_1MBPS |
|
||||
CONF_HW_BIT_RATE_2MBPS |
|
||||
|
@ -546,9 +538,9 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
|
||||
u8 active_scan, u8 high_prio, u8 band,
|
||||
u8 probe_requests)
|
||||
int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len, u8 active_scan,
|
||||
u8 high_prio, u8 band, u8 probe_requests)
|
||||
{
|
||||
|
||||
struct wl1271_cmd_trigger_scan_to *trigger = NULL;
|
||||
|
@ -619,12 +611,13 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
|
|||
|
||||
params->params.num_channels = j;
|
||||
|
||||
if (len && ssid) {
|
||||
params->params.ssid_len = len;
|
||||
memcpy(params->params.ssid, ssid, len);
|
||||
if (ssid_len && ssid) {
|
||||
params->params.ssid_len = ssid_len;
|
||||
memcpy(params->params.ssid, ssid, ssid_len);
|
||||
}
|
||||
|
||||
ret = wl1271_cmd_build_probe_req(wl, ssid, len, ieee_band);
|
||||
ret = wl1271_cmd_build_probe_req(wl, ssid, ssid_len,
|
||||
ie, ie_len, ieee_band);
|
||||
if (ret < 0) {
|
||||
wl1271_error("PROBE request template failed");
|
||||
goto out;
|
||||
|
@ -655,9 +648,9 @@ int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
|
|||
wl->scan.active = active_scan;
|
||||
wl->scan.high_prio = high_prio;
|
||||
wl->scan.probe_requests = probe_requests;
|
||||
if (len && ssid) {
|
||||
wl->scan.ssid_len = len;
|
||||
memcpy(wl->scan.ssid, ssid, len);
|
||||
if (ssid_len && ssid) {
|
||||
wl->scan.ssid_len = ssid_len;
|
||||
memcpy(wl->scan.ssid, ssid, ssid_len);
|
||||
} else
|
||||
wl->scan.ssid_len = 0;
|
||||
}
|
||||
|
@ -714,155 +707,102 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int wl1271_build_basic_rates(u8 *rates, u8 band)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
memcpy(template.header.bssid, wl->bssid, ETH_ALEN);
|
||||
|
||||
if (wl->bss_type == BSS_TYPE_IBSS) {
|
||||
size = sizeof(struct wl12xx_null_data_template);
|
||||
ptr = NULL;
|
||||
} else {
|
||||
memset(template.header.da, 0xff, ETH_ALEN);
|
||||
memset(template.header.bssid, 0xff, ETH_ALEN);
|
||||
skb = ieee80211_nullfunc_get(wl->hw, wl->vif);
|
||||
if (!skb)
|
||||
goto out;
|
||||
size = skb->len;
|
||||
ptr = skb->data;
|
||||
}
|
||||
|
||||
memcpy(template.header.sa, wl->mac_addr, ETH_ALEN);
|
||||
template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA |
|
||||
IEEE80211_STYPE_NULLFUNC |
|
||||
IEEE80211_FCTL_TODS);
|
||||
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, ptr, size);
|
||||
|
||||
return wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, &template,
|
||||
sizeof(template));
|
||||
out:
|
||||
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)
|
||||
{
|
||||
struct wl12xx_ps_poll_template template;
|
||||
struct sk_buff *skb;
|
||||
int ret = 0;
|
||||
|
||||
memcpy(template.bssid, wl->bssid, ETH_ALEN);
|
||||
memcpy(template.ta, wl->mac_addr, ETH_ALEN);
|
||||
skb = ieee80211_pspoll_get(wl->hw, wl->vif);
|
||||
if (!skb)
|
||||
goto out;
|
||||
|
||||
/* aid in PS-Poll has its two MSBs each set to 1 */
|
||||
template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid);
|
||||
|
||||
template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL);
|
||||
|
||||
return wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, &template,
|
||||
sizeof(template));
|
||||
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data,
|
||||
skb->len);
|
||||
|
||||
out:
|
||||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len,
|
||||
u8 band)
|
||||
int wl1271_cmd_build_probe_req(struct wl1271 *wl,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len, u8 band)
|
||||
{
|
||||
struct wl12xx_probe_req_template template;
|
||||
struct wl12xx_ie_rates *rates;
|
||||
char *ptr;
|
||||
u16 size;
|
||||
struct sk_buff *skb;
|
||||
int ret;
|
||||
|
||||
ptr = (char *)&template;
|
||||
size = sizeof(struct ieee80211_header);
|
||||
skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len,
|
||||
ie, ie_len);
|
||||
if (!skb) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memset(template.header.da, 0xff, ETH_ALEN);
|
||||
memset(template.header.bssid, 0xff, ETH_ALEN);
|
||||
memcpy(template.header.sa, wl->mac_addr, ETH_ALEN);
|
||||
template.header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
|
||||
|
||||
/* IEs */
|
||||
/* SSID */
|
||||
template.ssid.header.id = WLAN_EID_SSID;
|
||||
template.ssid.header.len = ssid_len;
|
||||
if (ssid_len && ssid)
|
||||
memcpy(template.ssid.ssid, ssid, ssid_len);
|
||||
size += sizeof(struct wl12xx_ie_header) + ssid_len;
|
||||
ptr += size;
|
||||
|
||||
/* Basic Rates */
|
||||
rates = (struct wl12xx_ie_rates *)ptr;
|
||||
rates->header.id = WLAN_EID_SUPP_RATES;
|
||||
rates->header.len = wl1271_build_basic_rates(rates->rates, band);
|
||||
size += sizeof(struct wl12xx_ie_header) + rates->header.len;
|
||||
ptr += sizeof(struct wl12xx_ie_header) + rates->header.len;
|
||||
|
||||
/* Extended rates */
|
||||
rates = (struct wl12xx_ie_rates *)ptr;
|
||||
rates->header.id = WLAN_EID_EXT_SUPP_RATES;
|
||||
rates->header.len = wl1271_build_extended_rates(rates->rates, band);
|
||||
size += sizeof(struct wl12xx_ie_header) + rates->header.len;
|
||||
|
||||
wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size);
|
||||
wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len);
|
||||
|
||||
if (band == IEEE80211_BAND_2GHZ)
|
||||
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4,
|
||||
&template, size);
|
||||
skb->data, skb->len);
|
||||
else
|
||||
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5,
|
||||
&template, size);
|
||||
skb->data, skb->len);
|
||||
|
||||
out:
|
||||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wl1271_build_qos_null_data(struct wl1271 *wl)
|
||||
{
|
||||
struct ieee80211_qos_hdr template;
|
||||
|
||||
memset(&template, 0, sizeof(template));
|
||||
|
||||
memcpy(template.addr1, wl->bssid, ETH_ALEN);
|
||||
memcpy(template.addr2, wl->mac_addr, ETH_ALEN);
|
||||
memcpy(template.addr3, wl->bssid, ETH_ALEN);
|
||||
|
||||
template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
|
||||
IEEE80211_STYPE_QOS_NULLFUNC |
|
||||
IEEE80211_FCTL_TODS);
|
||||
|
||||
/* FIXME: not sure what priority to use here */
|
||||
template.qos_ctrl = cpu_to_le16(0);
|
||||
|
||||
return wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, &template,
|
||||
sizeof(template));
|
||||
}
|
||||
|
||||
int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id)
|
||||
{
|
||||
struct wl1271_cmd_set_keys *cmd;
|
||||
|
|
|
@ -41,15 +41,17 @@ int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
|
|||
int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send);
|
||||
int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
|
||||
size_t len);
|
||||
int wl1271_cmd_scan(struct wl1271 *wl, u8 *ssid, size_t len,
|
||||
u8 active_scan, u8 high_prio, u8 band,
|
||||
u8 probe_requests);
|
||||
int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len, u8 active_scan,
|
||||
u8 high_prio, u8 band, u8 probe_requests);
|
||||
int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
|
||||
void *buf, size_t buf_len);
|
||||
int wl1271_cmd_build_null_data(struct wl1271 *wl);
|
||||
int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid);
|
||||
int wl1271_cmd_build_probe_req(struct wl1271 *wl, u8 *ssid, size_t ssid_len,
|
||||
u8 band);
|
||||
int wl1271_cmd_build_probe_req(struct wl1271 *wl,
|
||||
const u8 *ssid, size_t ssid_len,
|
||||
const u8 *ie, size_t ie_len, u8 band);
|
||||
int wl1271_build_qos_null_data(struct wl1271 *wl);
|
||||
int wl1271_cmd_set_default_wep_key(struct wl1271 *wl, u8 id);
|
||||
int wl1271_cmd_set_key(struct wl1271 *wl, u16 action, u8 id, u8 key_type,
|
||||
u8 key_size, const u8 *key, const u8 *addr,
|
||||
|
|
|
@ -65,110 +65,318 @@ enum {
|
|||
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 {
|
||||
/*
|
||||
* Defines the PER threshold in PPM of the BT voice of which reaching
|
||||
* this value will trigger raising the priority of the BT voice by
|
||||
* the BT IP until next NFS sample interval time as defined in
|
||||
* nfs_sample_interval.
|
||||
*
|
||||
* 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;
|
||||
__le32 params[CONF_SG_PARAMS_MAX];
|
||||
u8 state;
|
||||
};
|
||||
|
||||
enum conf_rx_queue_type {
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "wl1271.h"
|
||||
#include "wl1271_acx.h"
|
||||
#include "wl1271_ps.h"
|
||||
#include "wl1271_io.h"
|
||||
|
||||
/* ms */
|
||||
#define WL1271_DEBUGFS_STATS_LIFETIME 1000
|
||||
|
@ -276,13 +277,10 @@ static ssize_t gpio_power_write(struct file *file,
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
wl->set_power(true);
|
||||
set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
|
||||
} else {
|
||||
wl->set_power(false);
|
||||
clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
|
||||
}
|
||||
if (value)
|
||||
wl1271_power_on(wl);
|
||||
else
|
||||
wl1271_power_off(wl);
|
||||
|
||||
out:
|
||||
mutex_unlock(&wl->mutex);
|
||||
|
|
|
@ -44,7 +44,9 @@ static int wl1271_event_scan_complete(struct wl1271 *wl,
|
|||
* scanning as it checks that.
|
||||
*/
|
||||
clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
|
||||
/* FIXME: ie missing! */
|
||||
wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len,
|
||||
NULL, 0,
|
||||
wl->scan.active,
|
||||
wl->scan.high_prio,
|
||||
WL1271_SCAN_BAND_5_GHZ,
|
||||
|
|
|
@ -160,11 +160,11 @@ int wl1271_init_pta(struct wl1271 *wl)
|
|||
{
|
||||
int ret;
|
||||
|
||||
ret = wl1271_acx_sg_enable(wl);
|
||||
ret = wl1271_acx_sg_cfg(wl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = wl1271_acx_sg_cfg(wl);
|
||||
ret = wl1271_acx_sg_enable(wl, wl->sg_enabled);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -138,6 +138,18 @@ static inline void wl1271_write32(struct wl1271 *wl, int addr, u32 val)
|
|||
wl1271_raw_write32(wl, wl1271_translate_addr(wl, addr), val);
|
||||
}
|
||||
|
||||
static inline void wl1271_power_off(struct wl1271 *wl)
|
||||
{
|
||||
wl->if_ops->power(wl, false);
|
||||
clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
|
||||
}
|
||||
|
||||
static inline void wl1271_power_on(struct wl1271 *wl)
|
||||
{
|
||||
wl->if_ops->power(wl, true);
|
||||
set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
|
||||
}
|
||||
|
||||
|
||||
/* Top Register IO */
|
||||
void wl1271_top_reg_write(struct wl1271 *wl, int addr, u16 val);
|
||||
|
@ -149,6 +161,7 @@ int wl1271_set_partition(struct wl1271 *wl,
|
|||
/* Functions from wl1271_main.c */
|
||||
|
||||
int wl1271_register_hw(struct wl1271 *wl);
|
||||
void wl1271_unregister_hw(struct wl1271 *wl);
|
||||
int wl1271_init_ieee80211(struct wl1271 *wl);
|
||||
struct ieee80211_hw *wl1271_alloc_hw(void);
|
||||
int wl1271_free_hw(struct wl1271 *wl);
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <linux/etherdevice.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/inetdevice.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "wl1271.h"
|
||||
#include "wl12xx_80211.h"
|
||||
|
@ -48,17 +49,57 @@
|
|||
|
||||
static struct conf_drv_settings default_conf = {
|
||||
.sg = {
|
||||
.per_threshold = 7500,
|
||||
.max_scan_compensation_time = 120000,
|
||||
.nfs_sample_interval = 400,
|
||||
.load_ratio = 50,
|
||||
.auto_ps_mode = 0,
|
||||
.probe_req_compensation = 170,
|
||||
.scan_window_compensation = 50,
|
||||
.antenna_config = 0,
|
||||
.beacon_miss_threshold = 60,
|
||||
.rate_adaptation_threshold = CONF_HW_BIT_RATE_12MBPS,
|
||||
.rate_adaptation_snr = 0
|
||||
.params = {
|
||||
[CONF_SG_BT_PER_THRESHOLD] = 7500,
|
||||
[CONF_SG_HV3_MAX_OVERRIDE] = 0,
|
||||
[CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
|
||||
[CONF_SG_BT_LOAD_RATIO] = 50,
|
||||
[CONF_SG_AUTO_PS_MODE] = 0,
|
||||
[CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
|
||||
[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
|
||||
[CONF_SG_ANTENNA_CONFIGURATION] = 0,
|
||||
[CONF_SG_BEACON_MISS_PERCENT] = 60,
|
||||
[CONF_SG_RATE_ADAPT_THRESH] = 12,
|
||||
[CONF_SG_RATE_ADAPT_SNR] = 0,
|
||||
[CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_BR] = 10,
|
||||
[CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_BR] = 30,
|
||||
[CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_BR] = 8,
|
||||
[CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_BR] = 20,
|
||||
[CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_BR] = 50,
|
||||
/* Note: with UPSD, this should be 4 */
|
||||
[CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_BR] = 8,
|
||||
[CONF_SG_WLAN_PS_BT_ACL_MASTER_MIN_EDR] = 7,
|
||||
[CONF_SG_WLAN_PS_BT_ACL_MASTER_MAX_EDR] = 25,
|
||||
[CONF_SG_WLAN_PS_MAX_BT_ACL_MASTER_EDR] = 20,
|
||||
/* Note: with UPDS, this should be 15 */
|
||||
[CONF_SG_WLAN_PS_BT_ACL_SLAVE_MIN_EDR] = 8,
|
||||
/* Note: with UPDS, this should be 50 */
|
||||
[CONF_SG_WLAN_PS_BT_ACL_SLAVE_MAX_EDR] = 40,
|
||||
/* Note: with UPDS, this should be 10 */
|
||||
[CONF_SG_WLAN_PS_MAX_BT_ACL_SLAVE_EDR] = 20,
|
||||
[CONF_SG_RXT] = 1200,
|
||||
[CONF_SG_TXT] = 1000,
|
||||
[CONF_SG_ADAPTIVE_RXT_TXT] = 1,
|
||||
[CONF_SG_PS_POLL_TIMEOUT] = 10,
|
||||
[CONF_SG_UPSD_TIMEOUT] = 10,
|
||||
[CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MIN_EDR] = 7,
|
||||
[CONF_SG_WLAN_ACTIVE_BT_ACL_MASTER_MAX_EDR] = 15,
|
||||
[CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_MASTER_EDR] = 15,
|
||||
[CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MIN_EDR] = 8,
|
||||
[CONF_SG_WLAN_ACTIVE_BT_ACL_SLAVE_MAX_EDR] = 20,
|
||||
[CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_SLAVE_EDR] = 15,
|
||||
[CONF_SG_WLAN_ACTIVE_BT_ACL_MIN_BR] = 20,
|
||||
[CONF_SG_WLAN_ACTIVE_BT_ACL_MAX_BR] = 50,
|
||||
[CONF_SG_WLAN_ACTIVE_MAX_BT_ACL_BR] = 10,
|
||||
[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_HV3] = 200,
|
||||
[CONF_SG_PASSIVE_SCAN_DURATION_FACTOR_A2DP] = 800,
|
||||
[CONF_SG_PASSIVE_SCAN_A2DP_BT_TIME] = 75,
|
||||
[CONF_SG_PASSIVE_SCAN_A2DP_WLAN_TIME] = 15,
|
||||
[CONF_SG_HV3_MAX_SERVED] = 6,
|
||||
[CONF_SG_DHCP_TIME] = 5000,
|
||||
[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_A2DP] = 100,
|
||||
},
|
||||
.state = CONF_SG_PROTECTIVE,
|
||||
},
|
||||
.rx = {
|
||||
.rx_msdu_life_time = 512000,
|
||||
|
@ -240,6 +281,21 @@ static struct conf_drv_settings default_conf = {
|
|||
}
|
||||
};
|
||||
|
||||
static void wl1271_device_release(struct device *dev)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static struct platform_device wl1271_device = {
|
||||
.name = "wl1271",
|
||||
.id = -1,
|
||||
|
||||
/* device model insists to have a release function */
|
||||
.dev = {
|
||||
.release = wl1271_device_release,
|
||||
},
|
||||
};
|
||||
|
||||
static LIST_HEAD(wl_list);
|
||||
|
||||
static void wl1271_conf_init(struct wl1271 *wl)
|
||||
|
@ -359,18 +415,6 @@ static int wl1271_plt_init(struct wl1271 *wl)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void wl1271_power_off(struct wl1271 *wl)
|
||||
{
|
||||
wl->set_power(false);
|
||||
clear_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
|
||||
}
|
||||
|
||||
static void wl1271_power_on(struct wl1271 *wl)
|
||||
{
|
||||
wl->set_power(true);
|
||||
set_bit(WL1271_FLAG_GPIO_POWER, &wl->flags);
|
||||
}
|
||||
|
||||
static void wl1271_fw_status(struct wl1271 *wl,
|
||||
struct wl1271_fw_status *status)
|
||||
{
|
||||
|
@ -526,40 +570,6 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int wl1271_update_mac_addr(struct wl1271 *wl)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 *nvs_ptr = (u8 *)wl->nvs->nvs;
|
||||
|
||||
/* get mac address from the NVS */
|
||||
wl->mac_addr[0] = nvs_ptr[11];
|
||||
wl->mac_addr[1] = nvs_ptr[10];
|
||||
wl->mac_addr[2] = nvs_ptr[6];
|
||||
wl->mac_addr[3] = nvs_ptr[5];
|
||||
wl->mac_addr[4] = nvs_ptr[4];
|
||||
wl->mac_addr[5] = nvs_ptr[3];
|
||||
|
||||
/* FIXME: if it is a zero-address, we should bail out. Now, instead,
|
||||
we randomize an address */
|
||||
if (is_zero_ether_addr(wl->mac_addr)) {
|
||||
static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
|
||||
memcpy(wl->mac_addr, nokia_oui, 3);
|
||||
get_random_bytes(wl->mac_addr + 3, 3);
|
||||
|
||||
/* update this address to the NVS */
|
||||
nvs_ptr[11] = wl->mac_addr[0];
|
||||
nvs_ptr[10] = wl->mac_addr[1];
|
||||
nvs_ptr[6] = wl->mac_addr[2];
|
||||
nvs_ptr[5] = wl->mac_addr[3];
|
||||
nvs_ptr[4] = wl->mac_addr[4];
|
||||
nvs_ptr[3] = wl->mac_addr[5];
|
||||
}
|
||||
|
||||
SET_IEEE80211_PERM_ADDR(wl->hw, wl->mac_addr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wl1271_fetch_nvs(struct wl1271 *wl)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
|
@ -589,8 +599,6 @@ static int wl1271_fetch_nvs(struct wl1271 *wl)
|
|||
|
||||
memcpy(wl->nvs, fw->data, sizeof(struct wl1271_nvs_file));
|
||||
|
||||
ret = wl1271_update_mac_addr(wl);
|
||||
|
||||
out:
|
||||
release_firmware(fw);
|
||||
|
||||
|
@ -907,14 +915,59 @@ static struct notifier_block wl1271_dev_notifier = {
|
|||
|
||||
|
||||
static int wl1271_op_start(struct ieee80211_hw *hw)
|
||||
{
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 start");
|
||||
|
||||
/*
|
||||
* We have to delay the booting of the hardware because
|
||||
* we need to know the local MAC address before downloading and
|
||||
* initializing the firmware. The MAC address cannot be changed
|
||||
* after boot, and without the proper MAC address, the firmware
|
||||
* will not function properly.
|
||||
*
|
||||
* The MAC address is first known when the corresponding interface
|
||||
* is added. That is where we will initialize the hardware.
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wl1271_op_stop(struct ieee80211_hw *hw)
|
||||
{
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
|
||||
}
|
||||
|
||||
static int wl1271_op_add_interface(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
int retries = WL1271_BOOT_RETRIES;
|
||||
int ret = 0;
|
||||
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 start");
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
|
||||
vif->type, vif->addr);
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
if (wl->vif) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wl->vif = vif;
|
||||
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
wl->bss_type = BSS_TYPE_STA_BSS;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
wl->bss_type = BSS_TYPE_IBSS;
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(wl->mac_addr, vif->addr, ETH_ALEN);
|
||||
|
||||
if (wl->state != WL1271_STATE_OFF) {
|
||||
wl1271_error("cannot start because not in off state: %d",
|
||||
|
@ -970,19 +1023,20 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void wl1271_op_stop(struct ieee80211_hw *hw)
|
||||
static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
int i;
|
||||
|
||||
wl1271_info("down");
|
||||
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 stop");
|
||||
|
||||
unregister_inetaddr_notifier(&wl1271_dev_notifier);
|
||||
list_del(&wl->list);
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
|
||||
|
||||
wl1271_info("down");
|
||||
|
||||
list_del(&wl->list);
|
||||
|
||||
WARN_ON(wl->state != WL1271_STATE_ON);
|
||||
|
||||
|
@ -1026,6 +1080,8 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
|
|||
wl->rate_set = CONF_TX_RATE_MASK_BASIC;
|
||||
wl->sta_rate_set = 0;
|
||||
wl->flags = 0;
|
||||
wl->vif = NULL;
|
||||
wl->filters = 0;
|
||||
|
||||
for (i = 0; i < NUM_TX_QUEUES; i++)
|
||||
wl->tx_blocks_freed[i] = 0;
|
||||
|
@ -1034,120 +1090,40 @@ static void wl1271_op_stop(struct ieee80211_hw *hw)
|
|||
mutex_unlock(&wl->mutex);
|
||||
}
|
||||
|
||||
static int wl1271_op_add_interface(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif)
|
||||
static void wl1271_configure_filters(struct wl1271 *wl, unsigned int filters)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
int ret = 0;
|
||||
wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
|
||||
wl->rx_filter = WL1271_DEFAULT_RX_FILTER;
|
||||
|
||||
wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM",
|
||||
vif->type, vif->addr);
|
||||
/* combine requested filters with current filter config */
|
||||
filters = wl->filters | filters;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
if (wl->vif) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
wl1271_debug(DEBUG_FILTERS, "RX filters set: ");
|
||||
|
||||
if (filters & FIF_PROMISC_IN_BSS) {
|
||||
wl1271_debug(DEBUG_FILTERS, " - FIF_PROMISC_IN_BSS");
|
||||
wl->rx_config &= ~CFG_UNI_FILTER_EN;
|
||||
wl->rx_config |= CFG_BSSID_FILTER_EN;
|
||||
}
|
||||
|
||||
wl->vif = vif;
|
||||
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
wl->bss_type = BSS_TYPE_STA_BSS;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
wl->bss_type = BSS_TYPE_IBSS;
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
goto out;
|
||||
if (filters & FIF_BCN_PRBRESP_PROMISC) {
|
||||
wl1271_debug(DEBUG_FILTERS, " - FIF_BCN_PRBRESP_PROMISC");
|
||||
wl->rx_config &= ~CFG_BSSID_FILTER_EN;
|
||||
wl->rx_config &= ~CFG_SSID_FILTER_EN;
|
||||
}
|
||||
if (filters & FIF_OTHER_BSS) {
|
||||
wl1271_debug(DEBUG_FILTERS, " - FIF_OTHER_BSS");
|
||||
wl->rx_config &= ~CFG_BSSID_FILTER_EN;
|
||||
}
|
||||
if (filters & FIF_CONTROL) {
|
||||
wl1271_debug(DEBUG_FILTERS, " - FIF_CONTROL");
|
||||
wl->rx_filter |= CFG_RX_CTL_EN;
|
||||
}
|
||||
if (filters & FIF_FCSFAIL) {
|
||||
wl1271_debug(DEBUG_FILTERS, " - FIF_FCSFAIL");
|
||||
wl->rx_filter |= CFG_RX_FCS_ERROR;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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,
|
||||
0xad, 0xbe, 0xef };
|
||||
|
||||
/* disable mac filter, so we hear everything */
|
||||
wl->rx_config &= ~CFG_BSSID_FILTER_EN;
|
||||
|
||||
wl->channel = channel;
|
||||
memcpy(wl->bssid, dummy_bssid, ETH_ALEN);
|
||||
|
||||
/* pass through frames from all BSS */
|
||||
wl1271_configure_filters(wl, FIF_OTHER_BSS);
|
||||
|
||||
/* the dummy join is performed always with STATION BSS type to allow
|
||||
also ad-hoc mode to listen to the surroundings without sending any
|
||||
beacons yet. */
|
||||
|
@ -1186,7 +1162,9 @@ static int wl1271_unjoin_channel(struct wl1271 *wl)
|
|||
clear_bit(WL1271_FLAG_JOINED, &wl->flags);
|
||||
wl->channel = 0;
|
||||
memset(wl->bssid, 0, ETH_ALEN);
|
||||
wl->rx_config = WL1271_DEFAULT_RX_CONFIG;
|
||||
|
||||
/* stop filterting packets based on bssid */
|
||||
wl1271_configure_filters(wl, FIF_OTHER_BSS);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
|
@ -1359,14 +1337,14 @@ static void wl1271_op_configure_filter(struct ieee80211_hw *hw,
|
|||
if (ret < 0)
|
||||
goto out_sleep;
|
||||
|
||||
kfree(fp);
|
||||
|
||||
/* FIXME: We still need to set our filters properly */
|
||||
|
||||
/* determine, whether supported filter values have changed */
|
||||
if (changed == 0)
|
||||
goto out_sleep;
|
||||
|
||||
/* configure filters */
|
||||
wl->filters = *total;
|
||||
wl1271_configure_filters(wl, 0);
|
||||
|
||||
/* apply configured filters */
|
||||
ret = wl1271_acx_rx_config(wl, wl->rx_config, wl->rx_filter);
|
||||
if (ret < 0)
|
||||
|
@ -1377,6 +1355,7 @@ out_sleep:
|
|||
|
||||
out:
|
||||
mutex_unlock(&wl->mutex);
|
||||
kfree(fp);
|
||||
}
|
||||
|
||||
static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
|
@ -1522,10 +1501,12 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
|
|||
goto out;
|
||||
|
||||
if (wl1271_11a_enabled())
|
||||
ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
|
||||
ret = wl1271_cmd_scan(hw->priv, ssid, len,
|
||||
req->ie, req->ie_len, 1, 0,
|
||||
WL1271_SCAN_BAND_DUAL, 3);
|
||||
else
|
||||
ret = wl1271_cmd_scan(hw->priv, ssid, len, 1, 0,
|
||||
ret = wl1271_cmd_scan(hw->priv, ssid, len,
|
||||
req->ie, req->ie_len, 1, 0,
|
||||
WL1271_SCAN_BAND_2_4_GHZ, 3);
|
||||
|
||||
wl1271_ps_elp_sleep(wl);
|
||||
|
@ -1638,14 +1619,14 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
|
|||
* and enable the BSSID filter
|
||||
*/
|
||||
memcmp(wl->bssid, bss_conf->bssid, ETH_ALEN)) {
|
||||
wl->rx_config |= CFG_BSSID_FILTER_EN;
|
||||
memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
|
||||
|
||||
ret = wl1271_cmd_build_null_data(wl);
|
||||
if (ret < 0) {
|
||||
wl1271_warning("cmd buld null data failed %d",
|
||||
ret);
|
||||
if (ret < 0)
|
||||
goto out_sleep;
|
||||
}
|
||||
|
||||
/* filter out all packets not from this BSSID */
|
||||
wl1271_configure_filters(wl, 0);
|
||||
|
||||
/* Need to update the BSSID (for filtering etc) */
|
||||
do_join = true;
|
||||
|
@ -1735,6 +1716,7 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
|
|||
const struct ieee80211_tx_queue_params *params)
|
||||
{
|
||||
struct wl1271 *wl = hw->priv;
|
||||
u8 ps_scheme;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
|
@ -1745,17 +1727,22 @@ static int wl1271_op_conf_tx(struct ieee80211_hw *hw, u16 queue,
|
|||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
/* the txop is confed in units of 32us by the mac80211, we need us */
|
||||
ret = wl1271_acx_ac_cfg(wl, wl1271_tx_get_queue(queue),
|
||||
params->cw_min, params->cw_max,
|
||||
params->aifs, params->txop);
|
||||
params->aifs, params->txop << 5);
|
||||
if (ret < 0)
|
||||
goto out_sleep;
|
||||
|
||||
if (params->uapsd)
|
||||
ps_scheme = CONF_PS_SCHEME_UPSD_TRIGGER;
|
||||
else
|
||||
ps_scheme = CONF_PS_SCHEME_LEGACY;
|
||||
|
||||
ret = wl1271_acx_tid_cfg(wl, wl1271_tx_get_queue(queue),
|
||||
CONF_CHANNEL_TYPE_EDCF,
|
||||
wl1271_tx_get_queue(queue),
|
||||
CONF_PS_SCHEME_LEGACY_PSPOLL,
|
||||
CONF_ACK_POLICY_LEGACY, 0, 0);
|
||||
ps_scheme, CONF_ACK_POLICY_LEGACY, 0, 0);
|
||||
if (ret < 0)
|
||||
goto out_sleep;
|
||||
|
||||
|
@ -1925,7 +1912,6 @@ static const struct ieee80211_ops wl1271_ops = {
|
|||
.add_interface = wl1271_op_add_interface,
|
||||
.remove_interface = wl1271_op_remove_interface,
|
||||
.config = wl1271_op_config,
|
||||
/* .config_interface = wl1271_op_config_interface, */
|
||||
.prepare_multicast = wl1271_op_prepare_multicast,
|
||||
.configure_filter = wl1271_op_configure_filter,
|
||||
.tx = wl1271_op_tx,
|
||||
|
@ -1937,6 +1923,68 @@ static const struct ieee80211_ops wl1271_ops = {
|
|||
CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
|
||||
};
|
||||
|
||||
static ssize_t wl1271_sysfs_show_bt_coex_state(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct wl1271 *wl = dev_get_drvdata(dev);
|
||||
ssize_t len;
|
||||
|
||||
/* FIXME: what's the maximum length of buf? page size?*/
|
||||
len = 500;
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
len = snprintf(buf, len, "%d\n\n0 - off\n1 - on\n",
|
||||
wl->sg_enabled);
|
||||
mutex_unlock(&wl->mutex);
|
||||
|
||||
return len;
|
||||
|
||||
}
|
||||
|
||||
static ssize_t wl1271_sysfs_store_bt_coex_state(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct wl1271 *wl = dev_get_drvdata(dev);
|
||||
unsigned long res;
|
||||
int ret;
|
||||
|
||||
ret = strict_strtoul(buf, 10, &res);
|
||||
|
||||
if (ret < 0) {
|
||||
wl1271_warning("incorrect value written to bt_coex_mode");
|
||||
return count;
|
||||
}
|
||||
|
||||
mutex_lock(&wl->mutex);
|
||||
|
||||
res = !!res;
|
||||
|
||||
if (res == wl->sg_enabled)
|
||||
goto out;
|
||||
|
||||
wl->sg_enabled = res;
|
||||
|
||||
if (wl->state == WL1271_STATE_OFF)
|
||||
goto out;
|
||||
|
||||
ret = wl1271_ps_elp_wakeup(wl, false);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
wl1271_acx_sg_enable(wl, wl->sg_enabled);
|
||||
wl1271_ps_elp_sleep(wl);
|
||||
|
||||
out:
|
||||
mutex_unlock(&wl->mutex);
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(bt_coex_state, S_IRUGO | S_IWUSR,
|
||||
wl1271_sysfs_show_bt_coex_state,
|
||||
wl1271_sysfs_store_bt_coex_state);
|
||||
|
||||
int wl1271_register_hw(struct wl1271 *wl)
|
||||
{
|
||||
int ret;
|
||||
|
@ -1960,6 +2008,14 @@ int wl1271_register_hw(struct wl1271 *wl)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(wl1271_register_hw);
|
||||
|
||||
void wl1271_unregister_hw(struct wl1271 *wl)
|
||||
{
|
||||
ieee80211_unregister_hw(wl->hw);
|
||||
wl->mac80211_registered = false;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wl1271_unregister_hw);
|
||||
|
||||
int wl1271_init_ieee80211(struct wl1271 *wl)
|
||||
{
|
||||
/* The tx descriptor buffer and the TKIP space. */
|
||||
|
@ -1974,6 +2030,7 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
|
|||
IEEE80211_HW_NOISE_DBM |
|
||||
IEEE80211_HW_BEACON_FILTER |
|
||||
IEEE80211_HW_SUPPORTS_PS |
|
||||
IEEE80211_HW_SUPPORTS_UAPSD |
|
||||
IEEE80211_HW_HAS_RATE_CONTROL;
|
||||
|
||||
wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
|
||||
|
@ -1984,6 +2041,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl)
|
|||
if (wl1271_11a_enabled())
|
||||
wl->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &wl1271_band_5ghz;
|
||||
|
||||
wl->hw->queues = 4;
|
||||
|
||||
SET_IEEE80211_DEV(wl->hw, wl1271_wl_to_dev(wl));
|
||||
|
||||
return 0;
|
||||
|
@ -1995,21 +2054,34 @@ EXPORT_SYMBOL_GPL(wl1271_init_ieee80211);
|
|||
struct ieee80211_hw *wl1271_alloc_hw(void)
|
||||
{
|
||||
struct ieee80211_hw *hw;
|
||||
struct platform_device *plat_dev = NULL;
|
||||
struct wl1271 *wl;
|
||||
int i;
|
||||
int i, ret;
|
||||
static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
|
||||
|
||||
hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops);
|
||||
if (!hw) {
|
||||
wl1271_error("could not alloc ieee80211_hw");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ret = -ENOMEM;
|
||||
goto err_hw_alloc;
|
||||
}
|
||||
|
||||
plat_dev = kmalloc(sizeof(wl1271_device), GFP_KERNEL);
|
||||
if (!plat_dev) {
|
||||
wl1271_error("could not allocate platform_device");
|
||||
ret = -ENOMEM;
|
||||
goto err_plat_alloc;
|
||||
}
|
||||
|
||||
memcpy(plat_dev, &wl1271_device, sizeof(wl1271_device));
|
||||
|
||||
wl = hw->priv;
|
||||
memset(wl, 0, sizeof(*wl));
|
||||
|
||||
INIT_LIST_HEAD(&wl->list);
|
||||
|
||||
wl->hw = hw;
|
||||
wl->plat_dev = plat_dev;
|
||||
|
||||
skb_queue_head_init(&wl->tx_queue);
|
||||
|
||||
|
@ -2027,6 +2099,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
|
|||
wl->band = IEEE80211_BAND_2GHZ;
|
||||
wl->vif = NULL;
|
||||
wl->flags = 0;
|
||||
wl->sg_enabled = true;
|
||||
|
||||
for (i = 0; i < ACX_TX_DESCRIPTORS; i++)
|
||||
wl->tx_frames[i] = NULL;
|
||||
|
@ -2036,18 +2109,55 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
|
|||
wl->state = WL1271_STATE_OFF;
|
||||
mutex_init(&wl->mutex);
|
||||
|
||||
/*
|
||||
* FIXME: we should use a zero MAC address here, but for now we
|
||||
* generate a random Nokia address.
|
||||
*/
|
||||
memcpy(wl->mac_addr, nokia_oui, 3);
|
||||
get_random_bytes(wl->mac_addr + 3, 3);
|
||||
|
||||
/* Apply default driver configuration. */
|
||||
wl1271_conf_init(wl);
|
||||
|
||||
wl1271_debugfs_init(wl);
|
||||
|
||||
/* Register platform device */
|
||||
ret = platform_device_register(wl->plat_dev);
|
||||
if (ret) {
|
||||
wl1271_error("couldn't register platform device");
|
||||
goto err_hw;
|
||||
}
|
||||
dev_set_drvdata(&wl->plat_dev->dev, wl);
|
||||
|
||||
/* Create sysfs file to control bt coex state */
|
||||
ret = device_create_file(&wl->plat_dev->dev, &dev_attr_bt_coex_state);
|
||||
if (ret < 0) {
|
||||
wl1271_error("failed to create sysfs file bt_coex_state");
|
||||
goto err_platform;
|
||||
}
|
||||
|
||||
return hw;
|
||||
|
||||
err_platform:
|
||||
platform_device_unregister(wl->plat_dev);
|
||||
|
||||
err_hw:
|
||||
wl1271_debugfs_exit(wl);
|
||||
kfree(plat_dev);
|
||||
|
||||
err_plat_alloc:
|
||||
ieee80211_free_hw(hw);
|
||||
|
||||
err_hw_alloc:
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wl1271_alloc_hw);
|
||||
|
||||
int wl1271_free_hw(struct wl1271 *wl)
|
||||
{
|
||||
ieee80211_unregister_hw(wl->hw);
|
||||
platform_device_unregister(wl->plat_dev);
|
||||
kfree(wl->plat_dev);
|
||||
|
||||
wl1271_debugfs_exit(wl);
|
||||
|
||||
|
|
|
@ -102,15 +102,14 @@ static void wl1271_sdio_init(struct wl1271 *wl)
|
|||
}
|
||||
|
||||
static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
|
||||
size_t len, bool fixed)
|
||||
size_t len, bool fixed)
|
||||
{
|
||||
int ret;
|
||||
struct sdio_func *func = wl_to_func(wl);
|
||||
|
||||
sdio_claim_host(func);
|
||||
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
|
||||
((u8 *)buf)[0] = sdio_f0_readb(func, addr, &ret);
|
||||
wl1271_debug(DEBUG_SPI, "sdio read 52 addr 0x%x, byte 0x%02x",
|
||||
wl1271_debug(DEBUG_SDIO, "sdio read 52 addr 0x%x, byte 0x%02x",
|
||||
addr, ((u8 *)buf)[0]);
|
||||
} else {
|
||||
if (fixed)
|
||||
|
@ -118,32 +117,30 @@ static void wl1271_sdio_raw_read(struct wl1271 *wl, int addr, void *buf,
|
|||
else
|
||||
ret = sdio_memcpy_fromio(func, buf, addr, len);
|
||||
|
||||
wl1271_debug(DEBUG_SPI, "sdio read 53 addr 0x%x, %d bytes",
|
||||
wl1271_debug(DEBUG_SDIO, "sdio read 53 addr 0x%x, %d bytes",
|
||||
addr, len);
|
||||
wl1271_dump_ascii(DEBUG_SPI, "data: ", buf, len);
|
||||
wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
wl1271_error("sdio read failed (%d)", ret);
|
||||
|
||||
sdio_release_host(func);
|
||||
}
|
||||
|
||||
static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
|
||||
size_t len, bool fixed)
|
||||
size_t len, bool fixed)
|
||||
{
|
||||
int ret;
|
||||
struct sdio_func *func = wl_to_func(wl);
|
||||
|
||||
sdio_claim_host(func);
|
||||
if (unlikely(addr == HW_ACCESS_ELP_CTRL_REG_ADDR)) {
|
||||
sdio_f0_writeb(func, ((u8 *)buf)[0], addr, &ret);
|
||||
wl1271_debug(DEBUG_SPI, "sdio write 52 addr 0x%x, byte 0x%02x",
|
||||
wl1271_debug(DEBUG_SDIO, "sdio write 52 addr 0x%x, byte 0x%02x",
|
||||
addr, ((u8 *)buf)[0]);
|
||||
} else {
|
||||
wl1271_debug(DEBUG_SPI, "sdio write 53 addr 0x%x, %d bytes",
|
||||
wl1271_debug(DEBUG_SDIO, "sdio write 53 addr 0x%x, %d bytes",
|
||||
addr, len);
|
||||
wl1271_dump_ascii(DEBUG_SPI, "data: ", buf, len);
|
||||
wl1271_dump_ascii(DEBUG_SDIO, "data: ", buf, len);
|
||||
|
||||
if (fixed)
|
||||
ret = sdio_writesb(func, addr, buf, len);
|
||||
|
@ -153,7 +150,23 @@ static void wl1271_sdio_raw_write(struct wl1271 *wl, int addr, void *buf,
|
|||
if (ret)
|
||||
wl1271_error("sdio write failed (%d)", ret);
|
||||
|
||||
sdio_release_host(func);
|
||||
}
|
||||
|
||||
static void wl1271_sdio_set_power(struct wl1271 *wl, bool enable)
|
||||
{
|
||||
struct sdio_func *func = wl_to_func(wl);
|
||||
|
||||
/* Let the SDIO stack handle wlan_enable control, so we
|
||||
* keep host claimed while wlan is in use to keep wl1271
|
||||
* alive.
|
||||
*/
|
||||
if (enable) {
|
||||
sdio_claim_host(func);
|
||||
sdio_enable_func(func);
|
||||
} else {
|
||||
sdio_disable_func(func);
|
||||
sdio_release_host(func);
|
||||
}
|
||||
}
|
||||
|
||||
static struct wl1271_if_operations sdio_ops = {
|
||||
|
@ -161,15 +174,12 @@ static struct wl1271_if_operations sdio_ops = {
|
|||
.write = wl1271_sdio_raw_write,
|
||||
.reset = wl1271_sdio_reset,
|
||||
.init = wl1271_sdio_init,
|
||||
.power = wl1271_sdio_set_power,
|
||||
.dev = wl1271_sdio_wl_to_dev,
|
||||
.enable_irq = wl1271_sdio_enable_interrupts,
|
||||
.disable_irq = wl1271_sdio_disable_interrupts
|
||||
};
|
||||
|
||||
static void wl1271_sdio_set_power(bool enable)
|
||||
{
|
||||
}
|
||||
|
||||
static int __devinit wl1271_probe(struct sdio_func *func,
|
||||
const struct sdio_device_id *id)
|
||||
{
|
||||
|
@ -190,8 +200,6 @@ static int __devinit wl1271_probe(struct sdio_func *func,
|
|||
wl->if_priv = func;
|
||||
wl->if_ops = &sdio_ops;
|
||||
|
||||
wl->set_power = wl1271_sdio_set_power;
|
||||
|
||||
/* Grab access to FN0 for ELP reg. */
|
||||
func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
|
||||
|
||||
|
@ -220,28 +228,18 @@ static int __devinit wl1271_probe(struct sdio_func *func,
|
|||
if (ret)
|
||||
goto out_irq;
|
||||
|
||||
sdio_claim_host(func);
|
||||
sdio_set_drvdata(func, wl);
|
||||
|
||||
ret = sdio_enable_func(func);
|
||||
if (ret)
|
||||
goto out_release;
|
||||
|
||||
sdio_release_host(func);
|
||||
|
||||
wl1271_notice("initialized");
|
||||
|
||||
return 0;
|
||||
|
||||
out_release:
|
||||
sdio_release_host(func);
|
||||
|
||||
out_irq:
|
||||
free_irq(wl->irq, wl);
|
||||
|
||||
|
||||
out_free:
|
||||
ieee80211_free_hw(hw);
|
||||
wl1271_free_hw(wl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -250,24 +248,10 @@ static void __devexit wl1271_remove(struct sdio_func *func)
|
|||
{
|
||||
struct wl1271 *wl = sdio_get_drvdata(func);
|
||||
|
||||
ieee80211_unregister_hw(wl->hw);
|
||||
|
||||
sdio_claim_host(func);
|
||||
sdio_disable_func(func);
|
||||
sdio_release_host(func);
|
||||
|
||||
free_irq(wl->irq, wl);
|
||||
|
||||
kfree(wl->target_mem_map);
|
||||
vfree(wl->fw);
|
||||
wl->fw = NULL;
|
||||
kfree(wl->nvs);
|
||||
wl->nvs = NULL;
|
||||
|
||||
kfree(wl->fw_status);
|
||||
kfree(wl->tx_res_if);
|
||||
|
||||
ieee80211_free_hw(wl->hw);
|
||||
wl1271_unregister_hw(wl);
|
||||
wl1271_free_hw(wl);
|
||||
}
|
||||
|
||||
static struct sdio_driver wl1271_sdio_driver = {
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/crc7.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/wl12xx.h>
|
||||
|
@ -332,26 +331,18 @@ static irqreturn_t wl1271_irq(int irq, void *cookie)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void wl1271_device_release(struct device *dev)
|
||||
static void wl1271_spi_set_power(struct wl1271 *wl, bool enable)
|
||||
{
|
||||
|
||||
if (wl->set_power)
|
||||
wl->set_power(enable);
|
||||
}
|
||||
|
||||
static struct platform_device wl1271_device = {
|
||||
.name = "wl1271",
|
||||
.id = -1,
|
||||
|
||||
/* device model insists to have a release function */
|
||||
.dev = {
|
||||
.release = wl1271_device_release,
|
||||
},
|
||||
};
|
||||
|
||||
static struct wl1271_if_operations spi_ops = {
|
||||
.read = wl1271_spi_raw_read,
|
||||
.write = wl1271_spi_raw_write,
|
||||
.reset = wl1271_spi_reset,
|
||||
.init = wl1271_spi_init,
|
||||
.power = wl1271_spi_set_power,
|
||||
.dev = wl1271_spi_wl_to_dev,
|
||||
.enable_irq = wl1271_spi_enable_interrupts,
|
||||
.disable_irq = wl1271_spi_disable_interrupts
|
||||
|
@ -415,33 +406,23 @@ static int __devinit wl1271_probe(struct spi_device *spi)
|
|||
|
||||
disable_irq(wl->irq);
|
||||
|
||||
ret = platform_device_register(&wl1271_device);
|
||||
if (ret) {
|
||||
wl1271_error("couldn't register platform device");
|
||||
goto out_irq;
|
||||
}
|
||||
dev_set_drvdata(&wl1271_device.dev, wl);
|
||||
|
||||
ret = wl1271_init_ieee80211(wl);
|
||||
if (ret)
|
||||
goto out_platform;
|
||||
goto out_irq;
|
||||
|
||||
ret = wl1271_register_hw(wl);
|
||||
if (ret)
|
||||
goto out_platform;
|
||||
goto out_irq;
|
||||
|
||||
wl1271_notice("initialized");
|
||||
|
||||
return 0;
|
||||
|
||||
out_platform:
|
||||
platform_device_unregister(&wl1271_device);
|
||||
|
||||
out_irq:
|
||||
free_irq(wl->irq, wl);
|
||||
|
||||
out_free:
|
||||
ieee80211_free_hw(hw);
|
||||
wl1271_free_hw(wl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -450,9 +431,9 @@ static int __devexit wl1271_remove(struct spi_device *spi)
|
|||
{
|
||||
struct wl1271 *wl = dev_get_drvdata(&spi->dev);
|
||||
|
||||
platform_device_unregister(&wl1271_device);
|
||||
free_irq(wl->irq, wl);
|
||||
|
||||
wl1271_unregister_hw(wl);
|
||||
wl1271_free_hw(wl);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -125,9 +125,6 @@ struct wl1271_tx_hw_res_if {
|
|||
|
||||
static inline int wl1271_tx_get_queue(int queue)
|
||||
{
|
||||
/* FIXME: use best effort until WMM is enabled */
|
||||
return CONF_TX_AC_BE;
|
||||
|
||||
switch (queue) {
|
||||
case 0:
|
||||
return CONF_TX_AC_VO;
|
||||
|
|
|
@ -1834,32 +1834,32 @@ out:
|
|||
}
|
||||
|
||||
static const iw_handler wl3501_handler[] = {
|
||||
[SIOCGIWNAME - SIOCIWFIRST] = wl3501_get_name,
|
||||
[SIOCSIWFREQ - SIOCIWFIRST] = wl3501_set_freq,
|
||||
[SIOCGIWFREQ - SIOCIWFIRST] = wl3501_get_freq,
|
||||
[SIOCSIWMODE - SIOCIWFIRST] = wl3501_set_mode,
|
||||
[SIOCGIWMODE - SIOCIWFIRST] = wl3501_get_mode,
|
||||
[SIOCGIWSENS - SIOCIWFIRST] = wl3501_get_sens,
|
||||
[SIOCGIWRANGE - SIOCIWFIRST] = wl3501_get_range,
|
||||
[SIOCSIWSPY - SIOCIWFIRST] = iw_handler_set_spy,
|
||||
[SIOCGIWSPY - SIOCIWFIRST] = iw_handler_get_spy,
|
||||
[SIOCSIWTHRSPY - SIOCIWFIRST] = iw_handler_set_thrspy,
|
||||
[SIOCGIWTHRSPY - SIOCIWFIRST] = iw_handler_get_thrspy,
|
||||
[SIOCSIWAP - SIOCIWFIRST] = wl3501_set_wap,
|
||||
[SIOCGIWAP - SIOCIWFIRST] = wl3501_get_wap,
|
||||
[SIOCSIWSCAN - SIOCIWFIRST] = wl3501_set_scan,
|
||||
[SIOCGIWSCAN - SIOCIWFIRST] = wl3501_get_scan,
|
||||
[SIOCSIWESSID - SIOCIWFIRST] = wl3501_set_essid,
|
||||
[SIOCGIWESSID - SIOCIWFIRST] = wl3501_get_essid,
|
||||
[SIOCSIWNICKN - SIOCIWFIRST] = wl3501_set_nick,
|
||||
[SIOCGIWNICKN - SIOCIWFIRST] = wl3501_get_nick,
|
||||
[SIOCGIWRATE - SIOCIWFIRST] = wl3501_get_rate,
|
||||
[SIOCGIWRTS - SIOCIWFIRST] = wl3501_get_rts_threshold,
|
||||
[SIOCGIWFRAG - SIOCIWFIRST] = wl3501_get_frag_threshold,
|
||||
[SIOCGIWTXPOW - SIOCIWFIRST] = wl3501_get_txpow,
|
||||
[SIOCGIWRETRY - SIOCIWFIRST] = wl3501_get_retry,
|
||||
[SIOCGIWENCODE - SIOCIWFIRST] = wl3501_get_encode,
|
||||
[SIOCGIWPOWER - SIOCIWFIRST] = wl3501_get_power,
|
||||
IW_HANDLER(SIOCGIWNAME, wl3501_get_name),
|
||||
IW_HANDLER(SIOCSIWFREQ, wl3501_set_freq),
|
||||
IW_HANDLER(SIOCGIWFREQ, wl3501_get_freq),
|
||||
IW_HANDLER(SIOCSIWMODE, wl3501_set_mode),
|
||||
IW_HANDLER(SIOCGIWMODE, wl3501_get_mode),
|
||||
IW_HANDLER(SIOCGIWSENS, wl3501_get_sens),
|
||||
IW_HANDLER(SIOCGIWRANGE, wl3501_get_range),
|
||||
IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
|
||||
IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
|
||||
IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
|
||||
IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
|
||||
IW_HANDLER(SIOCSIWAP, wl3501_set_wap),
|
||||
IW_HANDLER(SIOCGIWAP, wl3501_get_wap),
|
||||
IW_HANDLER(SIOCSIWSCAN, wl3501_set_scan),
|
||||
IW_HANDLER(SIOCGIWSCAN, wl3501_get_scan),
|
||||
IW_HANDLER(SIOCSIWESSID, wl3501_set_essid),
|
||||
IW_HANDLER(SIOCGIWESSID, wl3501_get_essid),
|
||||
IW_HANDLER(SIOCSIWNICKN, wl3501_set_nick),
|
||||
IW_HANDLER(SIOCGIWNICKN, wl3501_get_nick),
|
||||
IW_HANDLER(SIOCGIWRATE, wl3501_get_rate),
|
||||
IW_HANDLER(SIOCGIWRTS, wl3501_get_rts_threshold),
|
||||
IW_HANDLER(SIOCGIWFRAG, wl3501_get_frag_threshold),
|
||||
IW_HANDLER(SIOCGIWTXPOW, wl3501_get_txpow),
|
||||
IW_HANDLER(SIOCGIWRETRY, wl3501_get_retry),
|
||||
IW_HANDLER(SIOCGIWENCODE, wl3501_get_encode),
|
||||
IW_HANDLER(SIOCGIWPOWER, wl3501_get_power),
|
||||
};
|
||||
|
||||
static const struct iw_handler_def wl3501_handler_def = {
|
||||
|
|
|
@ -323,6 +323,12 @@
|
|||
* the TX command and %NL80211_ATTR_FRAME includes the contents of the
|
||||
* frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
|
||||
* the frame.
|
||||
* @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
|
||||
* is used to configure connection quality monitoring notification trigger
|
||||
* levels.
|
||||
* @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
|
||||
* command is used as an event to indicate the that a trigger level was
|
||||
* reached.
|
||||
*
|
||||
* @NL80211_CMD_MAX: highest used command number
|
||||
* @__NL80211_CMD_AFTER_LAST: internal use
|
||||
|
@ -419,6 +425,9 @@ enum nl80211_commands {
|
|||
NL80211_CMD_SET_POWER_SAVE,
|
||||
NL80211_CMD_GET_POWER_SAVE,
|
||||
|
||||
NL80211_CMD_SET_CQM,
|
||||
NL80211_CMD_NOTIFY_CQM,
|
||||
|
||||
/* add new commands above here */
|
||||
|
||||
/* used to define NL80211_CMD_MAX below */
|
||||
|
@ -691,6 +700,9 @@ enum nl80211_commands {
|
|||
* @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
|
||||
* acknowledged by the recipient.
|
||||
*
|
||||
* @NL80211_ATTR_CQM: connection quality monitor configuration in a
|
||||
* nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
|
||||
*
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
*/
|
||||
|
@ -842,6 +854,8 @@ enum nl80211_attrs {
|
|||
|
||||
NL80211_ATTR_PS_STATE,
|
||||
|
||||
NL80211_ATTR_CQM,
|
||||
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
|
@ -1583,4 +1597,40 @@ enum nl80211_ps_state {
|
|||
NL80211_PS_ENABLED,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_attr_cqm - connection quality monitor attributes
|
||||
* @__NL80211_ATTR_CQM_INVALID: invalid
|
||||
* @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
|
||||
* the threshold for the RSSI level at which an event will be sent. Zero
|
||||
* to disable.
|
||||
* @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
|
||||
* the minimum amount the RSSI level must change after an event before a
|
||||
* new event may be issued (to reduce effects of RSSI oscillation).
|
||||
* @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
|
||||
* @__NL80211_ATTR_CQM_AFTER_LAST: internal
|
||||
* @NL80211_ATTR_CQM_MAX: highest key attribute
|
||||
*/
|
||||
enum nl80211_attr_cqm {
|
||||
__NL80211_ATTR_CQM_INVALID,
|
||||
NL80211_ATTR_CQM_RSSI_THOLD,
|
||||
NL80211_ATTR_CQM_RSSI_HYST,
|
||||
NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
|
||||
|
||||
/* keep last */
|
||||
__NL80211_ATTR_CQM_AFTER_LAST,
|
||||
NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
|
||||
* @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the
|
||||
* configured threshold
|
||||
* @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the
|
||||
* configured threshold
|
||||
*/
|
||||
enum nl80211_cqm_rssi_threshold_event {
|
||||
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
|
||||
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
|
||||
};
|
||||
|
||||
#endif /* __LINUX_NL80211_H */
|
||||
|
|
|
@ -346,6 +346,8 @@
|
|||
#define SIOCIWFIRST 0x8B00
|
||||
#define SIOCIWLAST SIOCIWLASTPRIV /* 0x8BFF */
|
||||
#define IW_IOCTL_IDX(cmd) ((cmd) - SIOCIWFIRST)
|
||||
#define IW_HANDLER(id, func) \
|
||||
[IW_IOCTL_IDX(id)] = func
|
||||
|
||||
/* Odd : get (world access), even : set (root access) */
|
||||
#define IW_IS_SET(cmd) (!((cmd) & 0x1))
|
||||
|
@ -648,7 +650,7 @@
|
|||
* 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
|
||||
#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \
|
||||
(cmd - SIOCIWFIRSTPRIV + 0x60) : \
|
||||
(cmd - SIOCSIWCOMMIT))
|
||||
(cmd - SIOCIWFIRST))
|
||||
#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5)
|
||||
#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F))
|
||||
/* Event capability constants - event autogenerated by the kernel
|
||||
|
|
|
@ -1007,6 +1007,7 @@ struct cfg80211_pmksa {
|
|||
* RSN IE. It allows for faster roaming between WPA2 BSSIDs.
|
||||
* @del_pmksa: Delete a cached PMKID.
|
||||
* @flush_pmksa: Flush all cached PMKIDs.
|
||||
* @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
|
||||
*
|
||||
*/
|
||||
struct cfg80211_ops {
|
||||
|
@ -1152,6 +1153,10 @@ struct cfg80211_ops {
|
|||
|
||||
int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
|
||||
bool enabled, int timeout);
|
||||
|
||||
int (*set_cqm_rssi_config)(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
s32 rssi_thold, u32 rssi_hyst);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -2337,4 +2342,18 @@ bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf,
|
|||
void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
|
||||
const u8 *buf, size_t len, bool ack, gfp_t gfp);
|
||||
|
||||
|
||||
/**
|
||||
* cfg80211_cqm_rssi_notify - connection quality monitoring rssi event
|
||||
* @dev: network device
|
||||
* @rssi_event: the triggered RSSI event
|
||||
* @gfp: context flags
|
||||
*
|
||||
* This function is called when a configured connection quality monitoring
|
||||
* rssi threshold reached event occurs.
|
||||
*/
|
||||
void cfg80211_cqm_rssi_notify(struct net_device *dev,
|
||||
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
||||
gfp_t gfp);
|
||||
|
||||
#endif /* __NET_CFG80211_H */
|
||||
|
|
|
@ -144,6 +144,7 @@ struct ieee80211_low_level_stats {
|
|||
* new beacon (beaconing modes)
|
||||
* @BSS_CHANGED_BEACON_ENABLED: Beaconing should be
|
||||
* enabled/disabled (beaconing modes)
|
||||
* @BSS_CHANGED_CQM: Connection quality monitor config changed
|
||||
*/
|
||||
enum ieee80211_bss_change {
|
||||
BSS_CHANGED_ASSOC = 1<<0,
|
||||
|
@ -156,6 +157,7 @@ enum ieee80211_bss_change {
|
|||
BSS_CHANGED_BSSID = 1<<7,
|
||||
BSS_CHANGED_BEACON = 1<<8,
|
||||
BSS_CHANGED_BEACON_ENABLED = 1<<9,
|
||||
BSS_CHANGED_CQM = 1<<10,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -185,6 +187,9 @@ enum ieee80211_bss_change {
|
|||
* @enable_beacon: whether beaconing should be enabled or not
|
||||
* @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info).
|
||||
* This field is only valid when the channel type is one of the HT types.
|
||||
* @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
|
||||
* implies disabled
|
||||
* @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis
|
||||
*/
|
||||
struct ieee80211_bss_conf {
|
||||
const u8 *bssid;
|
||||
|
@ -202,6 +207,8 @@ struct ieee80211_bss_conf {
|
|||
u64 timestamp;
|
||||
u32 basic_rates;
|
||||
u16 ht_operation_mode;
|
||||
s32 cqm_rssi_thold;
|
||||
u32 cqm_rssi_hyst;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -954,6 +961,17 @@ enum ieee80211_tkip_key_type {
|
|||
* Hardware can provide ack status reports of Tx frames to
|
||||
* the stack.
|
||||
*
|
||||
* @IEEE80211_HW_CONNECTION_MONITOR:
|
||||
* The hardware performs its own connection monitoring, including
|
||||
* periodic keep-alives to the AP and probing the AP on beacon loss.
|
||||
* When this flag is set, signaling beacon-loss will cause an immediate
|
||||
* change to disassociated state.
|
||||
*
|
||||
* @IEEE80211_HW_SUPPORTS_CQM_RSSI:
|
||||
* Hardware can do connection quality monitoring - i.e. it can monitor
|
||||
* connection quality related parameters, such as the RSSI level and
|
||||
* provide notifications if configured trigger levels are reached.
|
||||
*
|
||||
*/
|
||||
enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
|
||||
|
@ -975,6 +993,8 @@ enum ieee80211_hw_flags {
|
|||
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
|
||||
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
|
||||
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
|
||||
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
|
||||
IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -2364,12 +2384,42 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
|
|||
*
|
||||
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
|
||||
*
|
||||
* When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and
|
||||
* IEEE80211_CONF_PS is set, the driver needs to inform whenever the
|
||||
* When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING and
|
||||
* %IEEE80211_CONF_PS is set, the driver needs to inform whenever the
|
||||
* hardware is not receiving beacons with this function.
|
||||
*/
|
||||
void ieee80211_beacon_loss(struct ieee80211_vif *vif);
|
||||
|
||||
/**
|
||||
* ieee80211_connection_loss - inform hardware has lost connection to the AP
|
||||
*
|
||||
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
|
||||
*
|
||||
* When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING, and
|
||||
* %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver
|
||||
* needs to inform if the connection to the AP has been lost.
|
||||
*
|
||||
* This function will cause immediate change to disassociated state,
|
||||
* without connection recovery attempts.
|
||||
*/
|
||||
void ieee80211_connection_loss(struct ieee80211_vif *vif);
|
||||
|
||||
/**
|
||||
* ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring
|
||||
* rssi threshold triggered
|
||||
*
|
||||
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
|
||||
* @rssi_event: the RSSI trigger event type
|
||||
* @gfp: context flags
|
||||
*
|
||||
* When the %IEEE80211_HW_SUPPORTS_CQM_RSSI is set, and a connection quality
|
||||
* monitoring is configured with an rssi threshold, the driver will inform
|
||||
* whenever the rssi level reaches the threshold.
|
||||
*/
|
||||
void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
|
||||
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
||||
gfp_t gfp);
|
||||
|
||||
/* Rate control API */
|
||||
|
||||
/**
|
||||
|
|
|
@ -1402,6 +1402,32 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
s32 rssi_thold, u32 rssi_hyst)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
||||
struct ieee80211_vif *vif = &sdata->vif;
|
||||
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
|
||||
|
||||
if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (rssi_thold == bss_conf->cqm_rssi_thold &&
|
||||
rssi_hyst == bss_conf->cqm_rssi_hyst)
|
||||
return 0;
|
||||
|
||||
bss_conf->cqm_rssi_thold = rssi_thold;
|
||||
bss_conf->cqm_rssi_hyst = rssi_hyst;
|
||||
|
||||
/* tell the driver upon association, unless already associated */
|
||||
if (sdata->u.mgd.associated)
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
|
||||
struct net_device *dev,
|
||||
const u8 *addr,
|
||||
|
@ -1506,4 +1532,5 @@ struct cfg80211_ops mac80211_config_ops = {
|
|||
.remain_on_channel = ieee80211_remain_on_channel,
|
||||
.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
|
||||
.action = ieee80211_action,
|
||||
.set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
|
||||
};
|
||||
|
|
|
@ -327,7 +327,7 @@ struct ieee80211_if_managed {
|
|||
struct work_struct work;
|
||||
struct work_struct monitor_work;
|
||||
struct work_struct chswitch_work;
|
||||
struct work_struct beacon_loss_work;
|
||||
struct work_struct beacon_connection_loss_work;
|
||||
|
||||
unsigned long probe_timeout;
|
||||
int probe_send_count;
|
||||
|
@ -1156,7 +1156,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
|
|||
int powersave);
|
||||
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_hdr *hdr);
|
||||
void ieee80211_beacon_loss_work(struct work_struct *work);
|
||||
void ieee80211_beacon_connection_loss_work(struct work_struct *work);
|
||||
|
||||
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
|
||||
enum queue_stop_reason reason);
|
||||
|
|
|
@ -486,7 +486,7 @@ static int ieee80211_stop(struct net_device *dev)
|
|||
cancel_work_sync(&sdata->u.mgd.work);
|
||||
cancel_work_sync(&sdata->u.mgd.chswitch_work);
|
||||
cancel_work_sync(&sdata->u.mgd.monitor_work);
|
||||
cancel_work_sync(&sdata->u.mgd.beacon_loss_work);
|
||||
cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work);
|
||||
|
||||
/*
|
||||
* When we get here, the interface is marked down.
|
||||
|
|
|
@ -753,6 +753,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
|
|||
/* And the BSSID changed - we're associated now */
|
||||
bss_info_changed |= BSS_CHANGED_BSSID;
|
||||
|
||||
/* Tell the driver to monitor connection quality (if supported) */
|
||||
if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) &&
|
||||
sdata->vif.bss_conf.cqm_rssi_thold)
|
||||
bss_info_changed |= BSS_CHANGED_CQM;
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
|
@ -854,6 +859,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
|
|||
if (is_multicast_ether_addr(hdr->addr1))
|
||||
return;
|
||||
|
||||
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
|
||||
return;
|
||||
|
||||
mod_timer(&sdata->u.mgd.conn_mon_timer,
|
||||
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
|
||||
}
|
||||
|
@ -931,23 +939,68 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
|
|||
mutex_unlock(&ifmgd->mtx);
|
||||
}
|
||||
|
||||
void ieee80211_beacon_loss_work(struct work_struct *work)
|
||||
static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
u8 bssid[ETH_ALEN];
|
||||
|
||||
mutex_lock(&ifmgd->mtx);
|
||||
if (!ifmgd->associated) {
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
|
||||
|
||||
printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
|
||||
|
||||
ieee80211_set_disassoc(sdata);
|
||||
ieee80211_recalc_idle(local);
|
||||
mutex_unlock(&ifmgd->mtx);
|
||||
/*
|
||||
* must be outside lock due to cfg80211,
|
||||
* but that's not a problem.
|
||||
*/
|
||||
ieee80211_send_deauth_disassoc(sdata, bssid,
|
||||
IEEE80211_STYPE_DEAUTH,
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void ieee80211_beacon_connection_loss_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata =
|
||||
container_of(work, struct ieee80211_sub_if_data,
|
||||
u.mgd.beacon_loss_work);
|
||||
u.mgd.beacon_connection_loss_work);
|
||||
|
||||
ieee80211_mgd_probe_ap(sdata, true);
|
||||
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
|
||||
__ieee80211_connection_loss(sdata);
|
||||
else
|
||||
ieee80211_mgd_probe_ap(sdata, true);
|
||||
}
|
||||
|
||||
void ieee80211_beacon_loss(struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
struct ieee80211_hw *hw = &sdata->local->hw;
|
||||
|
||||
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
|
||||
WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
|
||||
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_beacon_loss);
|
||||
|
||||
void ieee80211_connection_loss(struct ieee80211_vif *vif)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
struct ieee80211_hw *hw = &sdata->local->hw;
|
||||
|
||||
WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
|
||||
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_connection_loss);
|
||||
|
||||
|
||||
static enum rx_mgmt_action __must_check
|
||||
ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len)
|
||||
|
@ -1637,7 +1690,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
|
|||
if (local->quiescing)
|
||||
return;
|
||||
|
||||
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&sdata->u.mgd.beacon_connection_loss_work);
|
||||
}
|
||||
|
||||
static void ieee80211_sta_conn_mon_timer(unsigned long data)
|
||||
|
@ -1689,7 +1743,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
|
|||
*/
|
||||
|
||||
cancel_work_sync(&ifmgd->work);
|
||||
cancel_work_sync(&ifmgd->beacon_loss_work);
|
||||
cancel_work_sync(&ifmgd->beacon_connection_loss_work);
|
||||
if (del_timer_sync(&ifmgd->timer))
|
||||
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
|
||||
|
||||
|
@ -1723,7 +1777,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
|||
INIT_WORK(&ifmgd->work, ieee80211_sta_work);
|
||||
INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
|
||||
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
|
||||
INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work);
|
||||
INIT_WORK(&ifmgd->beacon_connection_loss_work,
|
||||
ieee80211_beacon_connection_loss_work);
|
||||
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
|
||||
(unsigned long) sdata);
|
||||
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
|
||||
|
@ -2135,3 +2190,13 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
|
|||
*cookie = (unsigned long) skb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
|
||||
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
||||
|
||||
cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
|
||||
|
|
|
@ -2010,14 +2010,12 @@ void ieee80211_tx_pending(unsigned long data)
|
|||
while (!skb_queue_empty(&local->pending[i])) {
|
||||
struct sk_buff *skb = __skb_dequeue(&local->pending[i]);
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
if (WARN_ON(!info->control.vif)) {
|
||||
kfree_skb(skb);
|
||||
continue;
|
||||
}
|
||||
|
||||
sdata = vif_to_sdata(info->control.vif);
|
||||
spin_unlock_irqrestore(&local->queue_stop_reason_lock,
|
||||
flags);
|
||||
|
||||
|
|
|
@ -894,3 +894,16 @@ void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
|
|||
nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_action_tx_status);
|
||||
|
||||
void cfg80211_cqm_rssi_notify(struct net_device *dev,
|
||||
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct wireless_dev *wdev = dev->ieee80211_ptr;
|
||||
struct wiphy *wiphy = wdev->wiphy;
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
|
||||
|
||||
/* Indicate roaming trigger event to user space */
|
||||
nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
|
||||
|
|
|
@ -149,6 +149,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
|
|||
.len = IEEE80211_MAX_DATA_LEN },
|
||||
[NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
|
||||
[NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
|
||||
};
|
||||
|
||||
/* policy for the attributes */
|
||||
|
@ -4778,6 +4779,84 @@ unlock_rtnl:
|
|||
return err;
|
||||
}
|
||||
|
||||
static struct nla_policy
|
||||
nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
|
||||
[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
static int nl80211_set_cqm_rssi(struct genl_info *info,
|
||||
s32 threshold, u32 hysteresis)
|
||||
{
|
||||
struct cfg80211_registered_device *rdev;
|
||||
struct wireless_dev *wdev;
|
||||
struct net_device *dev;
|
||||
int err;
|
||||
|
||||
if (threshold > 0)
|
||||
return -EINVAL;
|
||||
|
||||
rtnl_lock();
|
||||
|
||||
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
|
||||
if (err)
|
||||
goto unlock_rdev;
|
||||
|
||||
wdev = dev->ieee80211_ptr;
|
||||
|
||||
if (!rdev->ops->set_cqm_rssi_config) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto unlock_rdev;
|
||||
}
|
||||
|
||||
if (wdev->iftype != NL80211_IFTYPE_STATION) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto unlock_rdev;
|
||||
}
|
||||
|
||||
err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
|
||||
threshold, hysteresis);
|
||||
|
||||
unlock_rdev:
|
||||
cfg80211_unlock_rdev(rdev);
|
||||
dev_put(dev);
|
||||
rtnl_unlock();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
|
||||
struct nlattr *cqm;
|
||||
int err;
|
||||
|
||||
cqm = info->attrs[NL80211_ATTR_CQM];
|
||||
if (!cqm) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
|
||||
nl80211_attr_cqm_policy);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
|
||||
attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
|
||||
s32 threshold;
|
||||
u32 hysteresis;
|
||||
threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
|
||||
hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
|
||||
err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
|
||||
} else
|
||||
err = -EINVAL;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct genl_ops nl80211_ops[] = {
|
||||
{
|
||||
.cmd = NL80211_CMD_GET_WIPHY,
|
||||
|
@ -5082,6 +5161,12 @@ static struct genl_ops nl80211_ops[] = {
|
|||
.policy = nl80211_policy,
|
||||
/* can be retrieved by unprivileged users */
|
||||
},
|
||||
{
|
||||
.cmd = NL80211_CMD_SET_CQM,
|
||||
.doit = nl80211_set_cqm,
|
||||
.policy = nl80211_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_multicast_group nl80211_mlme_mcgrp = {
|
||||
|
@ -5832,6 +5917,52 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
|
|||
nlmsg_free(msg);
|
||||
}
|
||||
|
||||
void
|
||||
nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct sk_buff *msg;
|
||||
struct nlattr *pinfoattr;
|
||||
void *hdr;
|
||||
|
||||
msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
|
||||
if (!hdr) {
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
|
||||
|
||||
pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
|
||||
if (!pinfoattr)
|
||||
goto nla_put_failure;
|
||||
|
||||
NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
|
||||
rssi_event);
|
||||
|
||||
nla_nest_end(msg, pinfoattr);
|
||||
|
||||
if (genlmsg_end(msg, hdr) < 0) {
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
|
||||
nl80211_mlme_mcgrp.id, gfp);
|
||||
return;
|
||||
|
||||
nla_put_failure:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
nlmsg_free(msg);
|
||||
}
|
||||
|
||||
static int nl80211_netlink_notify(struct notifier_block * nb,
|
||||
unsigned long state,
|
||||
void *_notify)
|
||||
|
|
|
@ -82,4 +82,10 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
|
|||
const u8 *buf, size_t len, bool ack,
|
||||
gfp_t gfp);
|
||||
|
||||
void
|
||||
nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
|
||||
struct net_device *netdev,
|
||||
enum nl80211_cqm_rssi_threshold_event rssi_event,
|
||||
gfp_t gfp);
|
||||
|
||||
#endif /* __NET_WIRELESS_NL80211_H */
|
||||
|
|
|
@ -28,226 +28,226 @@ typedef int (*wext_ioctl_func)(struct net_device *, struct iwreq *,
|
|||
* know about.
|
||||
*/
|
||||
static const struct iw_ioctl_description standard_ioctl[] = {
|
||||
[SIOCSIWCOMMIT - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWCOMMIT)] = {
|
||||
.header_type = IW_HEADER_TYPE_NULL,
|
||||
},
|
||||
[SIOCGIWNAME - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWNAME)] = {
|
||||
.header_type = IW_HEADER_TYPE_CHAR,
|
||||
.flags = IW_DESCR_FLAG_DUMP,
|
||||
},
|
||||
[SIOCSIWNWID - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWNWID)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
.flags = IW_DESCR_FLAG_EVENT,
|
||||
},
|
||||
[SIOCGIWNWID - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWNWID)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
.flags = IW_DESCR_FLAG_DUMP,
|
||||
},
|
||||
[SIOCSIWFREQ - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWFREQ)] = {
|
||||
.header_type = IW_HEADER_TYPE_FREQ,
|
||||
.flags = IW_DESCR_FLAG_EVENT,
|
||||
},
|
||||
[SIOCGIWFREQ - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWFREQ)] = {
|
||||
.header_type = IW_HEADER_TYPE_FREQ,
|
||||
.flags = IW_DESCR_FLAG_DUMP,
|
||||
},
|
||||
[SIOCSIWMODE - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWMODE)] = {
|
||||
.header_type = IW_HEADER_TYPE_UINT,
|
||||
.flags = IW_DESCR_FLAG_EVENT,
|
||||
},
|
||||
[SIOCGIWMODE - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWMODE)] = {
|
||||
.header_type = IW_HEADER_TYPE_UINT,
|
||||
.flags = IW_DESCR_FLAG_DUMP,
|
||||
},
|
||||
[SIOCSIWSENS - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWSENS)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCGIWSENS - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWSENS)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCSIWRANGE - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWRANGE)] = {
|
||||
.header_type = IW_HEADER_TYPE_NULL,
|
||||
},
|
||||
[SIOCGIWRANGE - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWRANGE)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = sizeof(struct iw_range),
|
||||
.flags = IW_DESCR_FLAG_DUMP,
|
||||
},
|
||||
[SIOCSIWPRIV - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWPRIV)] = {
|
||||
.header_type = IW_HEADER_TYPE_NULL,
|
||||
},
|
||||
[SIOCGIWPRIV - SIOCIWFIRST] = { /* (handled directly by us) */
|
||||
[IW_IOCTL_IDX(SIOCGIWPRIV)] = { /* (handled directly by us) */
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = sizeof(struct iw_priv_args),
|
||||
.max_tokens = 16,
|
||||
.flags = IW_DESCR_FLAG_NOMAX,
|
||||
},
|
||||
[SIOCSIWSTATS - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWSTATS)] = {
|
||||
.header_type = IW_HEADER_TYPE_NULL,
|
||||
},
|
||||
[SIOCGIWSTATS - SIOCIWFIRST] = { /* (handled directly by us) */
|
||||
[IW_IOCTL_IDX(SIOCGIWSTATS)] = { /* (handled directly by us) */
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = sizeof(struct iw_statistics),
|
||||
.flags = IW_DESCR_FLAG_DUMP,
|
||||
},
|
||||
[SIOCSIWSPY - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWSPY)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = sizeof(struct sockaddr),
|
||||
.max_tokens = IW_MAX_SPY,
|
||||
},
|
||||
[SIOCGIWSPY - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWSPY)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = sizeof(struct sockaddr) +
|
||||
sizeof(struct iw_quality),
|
||||
.max_tokens = IW_MAX_SPY,
|
||||
},
|
||||
[SIOCSIWTHRSPY - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWTHRSPY)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = sizeof(struct iw_thrspy),
|
||||
.min_tokens = 1,
|
||||
.max_tokens = 1,
|
||||
},
|
||||
[SIOCGIWTHRSPY - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWTHRSPY)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = sizeof(struct iw_thrspy),
|
||||
.min_tokens = 1,
|
||||
.max_tokens = 1,
|
||||
},
|
||||
[SIOCSIWAP - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWAP)] = {
|
||||
.header_type = IW_HEADER_TYPE_ADDR,
|
||||
},
|
||||
[SIOCGIWAP - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWAP)] = {
|
||||
.header_type = IW_HEADER_TYPE_ADDR,
|
||||
.flags = IW_DESCR_FLAG_DUMP,
|
||||
},
|
||||
[SIOCSIWMLME - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWMLME)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.min_tokens = sizeof(struct iw_mlme),
|
||||
.max_tokens = sizeof(struct iw_mlme),
|
||||
},
|
||||
[SIOCGIWAPLIST - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWAPLIST)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = sizeof(struct sockaddr) +
|
||||
sizeof(struct iw_quality),
|
||||
.max_tokens = IW_MAX_AP,
|
||||
.flags = IW_DESCR_FLAG_NOMAX,
|
||||
},
|
||||
[SIOCSIWSCAN - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWSCAN)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.min_tokens = 0,
|
||||
.max_tokens = sizeof(struct iw_scan_req),
|
||||
},
|
||||
[SIOCGIWSCAN - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWSCAN)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_SCAN_MAX_DATA,
|
||||
.flags = IW_DESCR_FLAG_NOMAX,
|
||||
},
|
||||
[SIOCSIWESSID - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWESSID)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_ESSID_MAX_SIZE,
|
||||
.flags = IW_DESCR_FLAG_EVENT,
|
||||
},
|
||||
[SIOCGIWESSID - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWESSID)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_ESSID_MAX_SIZE,
|
||||
.flags = IW_DESCR_FLAG_DUMP,
|
||||
},
|
||||
[SIOCSIWNICKN - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWNICKN)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_ESSID_MAX_SIZE,
|
||||
},
|
||||
[SIOCGIWNICKN - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWNICKN)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_ESSID_MAX_SIZE,
|
||||
},
|
||||
[SIOCSIWRATE - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWRATE)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCGIWRATE - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWRATE)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCSIWRTS - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWRTS)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCGIWRTS - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWRTS)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCSIWFRAG - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWFRAG)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCGIWFRAG - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWFRAG)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCSIWTXPOW - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWTXPOW)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCGIWTXPOW - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWTXPOW)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCSIWRETRY - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWRETRY)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCGIWRETRY - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWRETRY)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCSIWENCODE - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWENCODE)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_ENCODING_TOKEN_MAX,
|
||||
.flags = IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT,
|
||||
},
|
||||
[SIOCGIWENCODE - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWENCODE)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_ENCODING_TOKEN_MAX,
|
||||
.flags = IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT,
|
||||
},
|
||||
[SIOCSIWPOWER - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWPOWER)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCGIWPOWER - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWPOWER)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCSIWGENIE - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWGENIE)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_GENERIC_IE_MAX,
|
||||
},
|
||||
[SIOCGIWGENIE - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWGENIE)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_GENERIC_IE_MAX,
|
||||
},
|
||||
[SIOCSIWAUTH - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWAUTH)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCGIWAUTH - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWAUTH)] = {
|
||||
.header_type = IW_HEADER_TYPE_PARAM,
|
||||
},
|
||||
[SIOCSIWENCODEEXT - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWENCODEEXT)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.min_tokens = sizeof(struct iw_encode_ext),
|
||||
.max_tokens = sizeof(struct iw_encode_ext) +
|
||||
IW_ENCODING_TOKEN_MAX,
|
||||
},
|
||||
[SIOCGIWENCODEEXT - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCGIWENCODEEXT)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.min_tokens = sizeof(struct iw_encode_ext),
|
||||
.max_tokens = sizeof(struct iw_encode_ext) +
|
||||
IW_ENCODING_TOKEN_MAX,
|
||||
},
|
||||
[SIOCSIWPMKSA - SIOCIWFIRST] = {
|
||||
[IW_IOCTL_IDX(SIOCSIWPMKSA)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.min_tokens = sizeof(struct iw_pmksa),
|
||||
|
@ -261,44 +261,44 @@ static const unsigned standard_ioctl_num = ARRAY_SIZE(standard_ioctl);
|
|||
* we know about.
|
||||
*/
|
||||
static const struct iw_ioctl_description standard_event[] = {
|
||||
[IWEVTXDROP - IWEVFIRST] = {
|
||||
[IW_EVENT_IDX(IWEVTXDROP)] = {
|
||||
.header_type = IW_HEADER_TYPE_ADDR,
|
||||
},
|
||||
[IWEVQUAL - IWEVFIRST] = {
|
||||
[IW_EVENT_IDX(IWEVQUAL)] = {
|
||||
.header_type = IW_HEADER_TYPE_QUAL,
|
||||
},
|
||||
[IWEVCUSTOM - IWEVFIRST] = {
|
||||
[IW_EVENT_IDX(IWEVCUSTOM)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_CUSTOM_MAX,
|
||||
},
|
||||
[IWEVREGISTERED - IWEVFIRST] = {
|
||||
[IW_EVENT_IDX(IWEVREGISTERED)] = {
|
||||
.header_type = IW_HEADER_TYPE_ADDR,
|
||||
},
|
||||
[IWEVEXPIRED - IWEVFIRST] = {
|
||||
[IW_EVENT_IDX(IWEVEXPIRED)] = {
|
||||
.header_type = IW_HEADER_TYPE_ADDR,
|
||||
},
|
||||
[IWEVGENIE - IWEVFIRST] = {
|
||||
[IW_EVENT_IDX(IWEVGENIE)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_GENERIC_IE_MAX,
|
||||
},
|
||||
[IWEVMICHAELMICFAILURE - IWEVFIRST] = {
|
||||
[IW_EVENT_IDX(IWEVMICHAELMICFAILURE)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = sizeof(struct iw_michaelmicfailure),
|
||||
},
|
||||
[IWEVASSOCREQIE - IWEVFIRST] = {
|
||||
[IW_EVENT_IDX(IWEVASSOCREQIE)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_GENERIC_IE_MAX,
|
||||
},
|
||||
[IWEVASSOCRESPIE - IWEVFIRST] = {
|
||||
[IW_EVENT_IDX(IWEVASSOCRESPIE)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = IW_GENERIC_IE_MAX,
|
||||
},
|
||||
[IWEVPMKIDCAND - IWEVFIRST] = {
|
||||
[IW_EVENT_IDX(IWEVPMKIDCAND)] = {
|
||||
.header_type = IW_HEADER_TYPE_POINT,
|
||||
.token_size = 1,
|
||||
.max_tokens = sizeof(struct iw_pmkid_cand),
|
||||
|
@ -449,11 +449,11 @@ void wireless_send_event(struct net_device * dev,
|
|||
|
||||
/* Get the description of the Event */
|
||||
if (cmd <= SIOCIWLAST) {
|
||||
cmd_index = cmd - SIOCIWFIRST;
|
||||
cmd_index = IW_IOCTL_IDX(cmd);
|
||||
if (cmd_index < standard_ioctl_num)
|
||||
descr = &(standard_ioctl[cmd_index]);
|
||||
} else {
|
||||
cmd_index = cmd - IWEVFIRST;
|
||||
cmd_index = IW_EVENT_IDX(cmd);
|
||||
if (cmd_index < standard_event_num)
|
||||
descr = &(standard_event[cmd_index]);
|
||||
}
|
||||
|
@ -662,7 +662,7 @@ static iw_handler get_handler(struct net_device *dev, unsigned int cmd)
|
|||
return NULL;
|
||||
|
||||
/* Try as a standard command */
|
||||
index = cmd - SIOCIWFIRST;
|
||||
index = IW_IOCTL_IDX(cmd);
|
||||
if (index < handlers->num_standard)
|
||||
return handlers->standard[index];
|
||||
|
||||
|
@ -954,9 +954,9 @@ static int ioctl_standard_call(struct net_device * dev,
|
|||
int ret = -EINVAL;
|
||||
|
||||
/* Get the description of the IOCTL */
|
||||
if ((cmd - SIOCIWFIRST) >= standard_ioctl_num)
|
||||
if (IW_IOCTL_IDX(cmd) >= standard_ioctl_num)
|
||||
return -EOPNOTSUPP;
|
||||
descr = &(standard_ioctl[cmd - SIOCIWFIRST]);
|
||||
descr = &(standard_ioctl[IW_IOCTL_IDX(cmd)]);
|
||||
|
||||
/* Check if we have a pointer to user space data or not */
|
||||
if (descr->header_type != IW_HEADER_TYPE_POINT) {
|
||||
|
@ -1012,7 +1012,7 @@ static int compat_standard_call(struct net_device *dev,
|
|||
struct iw_point iwp;
|
||||
int err;
|
||||
|
||||
descr = standard_ioctl + (cmd - SIOCIWFIRST);
|
||||
descr = standard_ioctl + IW_IOCTL_IDX(cmd);
|
||||
|
||||
if (descr->header_type != IW_HEADER_TYPE_POINT)
|
||||
return ioctl_standard_call(dev, iwr, cmd, info, handler);
|
||||
|
|
Загрузка…
Ссылка в новой задаче