forked from Minki/linux
Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next
John W. Linville says: ==================== This is a batch of updates intended for the 3.13 stream... The biggest item of interest in here is wcn36xx, the new mac80211 driver for Qualcomm WCN3660/WCN3680 hardware. Regarding the mac80211 bits, Johannes says: "We have an assortment of cleanups and new features, of which the biggest one is probably the channel-switch support in IBSS. Nothing else really stands out much." On top of that, the ath9k and rt2x00 get a lot of update action from Felix Fietkau and Gabor Juhos, respectively. There are a handful of updates to other drivers here and there as well. Please let me know if there are problems! ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
5cda73b68e
@ -6830,6 +6830,14 @@ L: linux-hexagon@vger.kernel.org
|
||||
S: Supported
|
||||
F: arch/hexagon/
|
||||
|
||||
QUALCOMM WCN36XX WIRELESS DRIVER
|
||||
M: Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
L: wcn36xx@lists.infradead.org
|
||||
W: http://wireless.kernel.org/en/users/Drivers/wcn36xx
|
||||
T: git git://github.com/KrasnikovEugene/wcn36xx.git
|
||||
S: Supported
|
||||
F: drivers/net/wireless/ath/wcn36xx/
|
||||
|
||||
QUICKCAM PARALLEL PORT WEBCAMS
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -188,8 +188,11 @@ static int bcma_host_pci_probe(struct pci_dev *dev,
|
||||
pci_write_config_dword(dev, 0x40, val & 0xffff00ff);
|
||||
|
||||
/* SSB needed additional powering up, do we have any AMBA PCI cards? */
|
||||
if (!pci_is_pcie(dev))
|
||||
bcma_err(bus, "PCI card detected, report problems.\n");
|
||||
if (!pci_is_pcie(dev)) {
|
||||
bcma_err(bus, "PCI card detected, they are not supported.\n");
|
||||
err = -ENXIO;
|
||||
goto err_pci_release_regions;
|
||||
}
|
||||
|
||||
/* Map MMIO */
|
||||
err = -ENOMEM;
|
||||
@ -269,6 +272,7 @@ static SIMPLE_DEV_PM_OPS(bcma_pm_ops, bcma_host_pci_suspend,
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4313) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 43224) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
|
||||
|
@ -32,5 +32,6 @@ source "drivers/net/wireless/ath/ath6kl/Kconfig"
|
||||
source "drivers/net/wireless/ath/ar5523/Kconfig"
|
||||
source "drivers/net/wireless/ath/wil6210/Kconfig"
|
||||
source "drivers/net/wireless/ath/ath10k/Kconfig"
|
||||
source "drivers/net/wireless/ath/wcn36xx/Kconfig"
|
||||
|
||||
endif
|
||||
|
@ -5,6 +5,7 @@ obj-$(CONFIG_ATH6KL) += ath6kl/
|
||||
obj-$(CONFIG_AR5523) += ar5523/
|
||||
obj-$(CONFIG_WIL6210) += wil6210/
|
||||
obj-$(CONFIG_ATH10K) += ath10k/
|
||||
obj-$(CONFIG_WCN36XX) += wcn36xx/
|
||||
|
||||
obj-$(CONFIG_ATH_COMMON) += ath.o
|
||||
|
||||
|
@ -338,10 +338,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
|
||||
aniState->cckNoiseImmunityLevel !=
|
||||
ATH9K_ANI_CCK_DEF_LEVEL) {
|
||||
ath_dbg(common, ANI,
|
||||
"Restore defaults: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n",
|
||||
"Restore defaults: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",
|
||||
ah->opmode,
|
||||
chan->channel,
|
||||
chan->channelFlags,
|
||||
is_scanning,
|
||||
aniState->ofdmNoiseImmunityLevel,
|
||||
aniState->cckNoiseImmunityLevel);
|
||||
@ -354,10 +353,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
|
||||
* restore historical levels for this channel
|
||||
*/
|
||||
ath_dbg(common, ANI,
|
||||
"Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n",
|
||||
"Restore history: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",
|
||||
ah->opmode,
|
||||
chan->channel,
|
||||
chan->channelFlags,
|
||||
is_scanning,
|
||||
aniState->ofdmNoiseImmunityLevel,
|
||||
aniState->cckNoiseImmunityLevel);
|
||||
|
@ -666,14 +666,13 @@ static void ar5008_hw_set_channel_regs(struct ath_hw *ah,
|
||||
if (IS_CHAN_HT40(chan)) {
|
||||
phymode |= AR_PHY_FC_DYN2040_EN;
|
||||
|
||||
if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
|
||||
(chan->chanmode == CHANNEL_G_HT40PLUS))
|
||||
if (IS_CHAN_HT40PLUS(chan))
|
||||
phymode |= AR_PHY_FC_DYN2040_PRI_CH;
|
||||
|
||||
}
|
||||
REG_WRITE(ah, AR_PHY_TURBO, phymode);
|
||||
|
||||
ath9k_hw_set11nmac2040(ah);
|
||||
ath9k_hw_set11nmac2040(ah, chan);
|
||||
|
||||
ENABLE_REGWRITE_BUFFER(ah);
|
||||
|
||||
@ -691,31 +690,12 @@ static int ar5008_hw_process_ini(struct ath_hw *ah,
|
||||
int i, regWrites = 0;
|
||||
u32 modesIndex, freqIndex;
|
||||
|
||||
switch (chan->chanmode) {
|
||||
case CHANNEL_A:
|
||||
case CHANNEL_A_HT20:
|
||||
modesIndex = 1;
|
||||
if (IS_CHAN_5GHZ(chan)) {
|
||||
freqIndex = 1;
|
||||
break;
|
||||
case CHANNEL_A_HT40PLUS:
|
||||
case CHANNEL_A_HT40MINUS:
|
||||
modesIndex = 2;
|
||||
freqIndex = 1;
|
||||
break;
|
||||
case CHANNEL_G:
|
||||
case CHANNEL_G_HT20:
|
||||
case CHANNEL_B:
|
||||
modesIndex = 4;
|
||||
modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
|
||||
} else {
|
||||
freqIndex = 2;
|
||||
break;
|
||||
case CHANNEL_G_HT40PLUS:
|
||||
case CHANNEL_G_HT40MINUS:
|
||||
modesIndex = 3;
|
||||
freqIndex = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -814,8 +794,10 @@ static void ar5008_hw_set_rfmode(struct ath_hw *ah, struct ath9k_channel *chan)
|
||||
if (chan == NULL)
|
||||
return;
|
||||
|
||||
rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
|
||||
? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
|
||||
if (IS_CHAN_2GHZ(chan))
|
||||
rfMode |= AR_PHY_MODE_DYNAMIC;
|
||||
else
|
||||
rfMode |= AR_PHY_MODE_OFDM;
|
||||
|
||||
if (!AR_SREV_9280_20_OR_LATER(ah))
|
||||
rfMode |= (IS_CHAN_5GHZ(chan)) ?
|
||||
@ -1218,12 +1200,11 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
|
||||
|
||||
iniDef = &aniState->iniDef;
|
||||
|
||||
ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
|
||||
ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n",
|
||||
ah->hw_version.macVersion,
|
||||
ah->hw_version.macRev,
|
||||
ah->opmode,
|
||||
chan->channel,
|
||||
chan->channelFlags);
|
||||
chan->channel);
|
||||
|
||||
val = REG_READ(ah, AR_PHY_SFCORR);
|
||||
iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);
|
||||
|
@ -33,15 +33,12 @@ static bool ar9002_hw_is_cal_supported(struct ath_hw *ah,
|
||||
bool supported = false;
|
||||
switch (ah->supp_cals & cal_type) {
|
||||
case IQ_MISMATCH_CAL:
|
||||
/* Run IQ Mismatch for non-CCK only */
|
||||
if (!IS_CHAN_B(chan))
|
||||
supported = true;
|
||||
supported = true;
|
||||
break;
|
||||
case ADC_GAIN_CAL:
|
||||
case ADC_DC_CAL:
|
||||
/* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */
|
||||
if (!IS_CHAN_B(chan) &&
|
||||
!((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) &&
|
||||
if (!((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) &&
|
||||
IS_CHAN_HT20(chan)))
|
||||
supported = true;
|
||||
break;
|
||||
|
@ -419,28 +419,10 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan)
|
||||
u32 modesIndex;
|
||||
int i;
|
||||
|
||||
switch (chan->chanmode) {
|
||||
case CHANNEL_A:
|
||||
case CHANNEL_A_HT20:
|
||||
modesIndex = 1;
|
||||
break;
|
||||
case CHANNEL_A_HT40PLUS:
|
||||
case CHANNEL_A_HT40MINUS:
|
||||
modesIndex = 2;
|
||||
break;
|
||||
case CHANNEL_G:
|
||||
case CHANNEL_G_HT20:
|
||||
case CHANNEL_B:
|
||||
modesIndex = 4;
|
||||
break;
|
||||
case CHANNEL_G_HT40PLUS:
|
||||
case CHANNEL_G_HT40MINUS:
|
||||
modesIndex = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (IS_CHAN_5GHZ(chan))
|
||||
modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
|
||||
else
|
||||
modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
|
||||
|
||||
ENABLE_REGWRITE_BUFFER(ah);
|
||||
|
||||
|
@ -551,8 +551,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah,
|
||||
if (IS_CHAN_HT40(chan)) {
|
||||
phymode |= AR_PHY_GC_DYN2040_EN;
|
||||
/* Configure control (primary) channel at +-10MHz */
|
||||
if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
|
||||
(chan->chanmode == CHANNEL_G_HT40PLUS))
|
||||
if (IS_CHAN_HT40PLUS(chan))
|
||||
phymode |= AR_PHY_GC_DYN2040_PRI_CH;
|
||||
|
||||
}
|
||||
@ -565,7 +564,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah,
|
||||
REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode);
|
||||
|
||||
/* Configure MAC for 20/40 operation */
|
||||
ath9k_hw_set11nmac2040(ah);
|
||||
ath9k_hw_set11nmac2040(ah, chan);
|
||||
|
||||
/* global transmit timeout (25 TUs default)*/
|
||||
REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
|
||||
@ -682,42 +681,23 @@ static int ar9550_hw_get_modes_txgain_index(struct ath_hw *ah,
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (chan->chanmode) {
|
||||
case CHANNEL_A:
|
||||
case CHANNEL_A_HT20:
|
||||
if (chan->channel <= 5350)
|
||||
ret = 1;
|
||||
else if ((chan->channel > 5350) && (chan->channel <= 5600))
|
||||
ret = 3;
|
||||
if (IS_CHAN_2GHZ(chan)) {
|
||||
if (IS_CHAN_HT40(chan))
|
||||
return 7;
|
||||
else
|
||||
ret = 5;
|
||||
break;
|
||||
|
||||
case CHANNEL_A_HT40PLUS:
|
||||
case CHANNEL_A_HT40MINUS:
|
||||
if (chan->channel <= 5350)
|
||||
ret = 2;
|
||||
else if ((chan->channel > 5350) && (chan->channel <= 5600))
|
||||
ret = 4;
|
||||
else
|
||||
ret = 6;
|
||||
break;
|
||||
|
||||
case CHANNEL_G:
|
||||
case CHANNEL_G_HT20:
|
||||
case CHANNEL_B:
|
||||
ret = 8;
|
||||
break;
|
||||
|
||||
case CHANNEL_G_HT40PLUS:
|
||||
case CHANNEL_G_HT40MINUS:
|
||||
ret = 7;
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
return 8;
|
||||
}
|
||||
|
||||
if (chan->channel <= 5350)
|
||||
ret = 1;
|
||||
else if ((chan->channel > 5350) && (chan->channel <= 5600))
|
||||
ret = 3;
|
||||
else
|
||||
ret = 5;
|
||||
|
||||
if (IS_CHAN_HT40(chan))
|
||||
ret++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -727,28 +707,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
|
||||
unsigned int regWrites = 0, i;
|
||||
u32 modesIndex;
|
||||
|
||||
switch (chan->chanmode) {
|
||||
case CHANNEL_A:
|
||||
case CHANNEL_A_HT20:
|
||||
modesIndex = 1;
|
||||
break;
|
||||
case CHANNEL_A_HT40PLUS:
|
||||
case CHANNEL_A_HT40MINUS:
|
||||
modesIndex = 2;
|
||||
break;
|
||||
case CHANNEL_G:
|
||||
case CHANNEL_G_HT20:
|
||||
case CHANNEL_B:
|
||||
modesIndex = 4;
|
||||
break;
|
||||
case CHANNEL_G_HT40PLUS:
|
||||
case CHANNEL_G_HT40MINUS:
|
||||
modesIndex = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (IS_CHAN_5GHZ(chan))
|
||||
modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
|
||||
else
|
||||
modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
|
||||
|
||||
/*
|
||||
* SOC, MAC, BB, RADIO initvals.
|
||||
@ -846,8 +808,10 @@ static void ar9003_hw_set_rfmode(struct ath_hw *ah,
|
||||
if (chan == NULL)
|
||||
return;
|
||||
|
||||
rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
|
||||
? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
|
||||
if (IS_CHAN_2GHZ(chan))
|
||||
rfMode |= AR_PHY_MODE_DYNAMIC;
|
||||
else
|
||||
rfMode |= AR_PHY_MODE_OFDM;
|
||||
|
||||
if (IS_CHAN_A_FAST_CLOCK(ah, chan))
|
||||
rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE);
|
||||
@ -1273,12 +1237,11 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
|
||||
aniState = &ah->ani;
|
||||
iniDef = &aniState->iniDef;
|
||||
|
||||
ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
|
||||
ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n",
|
||||
ah->hw_version.macVersion,
|
||||
ah->hw_version.macRev,
|
||||
ah->opmode,
|
||||
chan->channel,
|
||||
chan->channelFlags);
|
||||
chan->channel);
|
||||
|
||||
val = REG_READ(ah, AR_PHY_SFCORR);
|
||||
iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);
|
||||
@ -1536,28 +1499,10 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
|
||||
unsigned int regWrites = 0;
|
||||
u32 modesIndex;
|
||||
|
||||
switch (chan->chanmode) {
|
||||
case CHANNEL_A:
|
||||
case CHANNEL_A_HT20:
|
||||
modesIndex = 1;
|
||||
break;
|
||||
case CHANNEL_A_HT40PLUS:
|
||||
case CHANNEL_A_HT40MINUS:
|
||||
modesIndex = 2;
|
||||
break;
|
||||
case CHANNEL_G:
|
||||
case CHANNEL_G_HT20:
|
||||
case CHANNEL_B:
|
||||
modesIndex = 4;
|
||||
break;
|
||||
case CHANNEL_G_HT40PLUS:
|
||||
case CHANNEL_G_HT40MINUS:
|
||||
modesIndex = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (IS_CHAN_5GHZ(chan))
|
||||
modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
|
||||
else
|
||||
modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
|
||||
|
||||
if (modesIndex == ah->modes_index) {
|
||||
*ini_reloaded = false;
|
||||
|
@ -64,7 +64,6 @@ struct ath_node;
|
||||
|
||||
struct ath_config {
|
||||
u16 txpowlimit;
|
||||
u8 cabqReadytime;
|
||||
};
|
||||
|
||||
/*************************/
|
||||
@ -207,6 +206,14 @@ struct ath_frame_info {
|
||||
u8 baw_tracked : 1;
|
||||
};
|
||||
|
||||
struct ath_rxbuf {
|
||||
struct list_head list;
|
||||
struct sk_buff *bf_mpdu;
|
||||
void *bf_desc;
|
||||
dma_addr_t bf_daddr;
|
||||
dma_addr_t bf_buf_addr;
|
||||
};
|
||||
|
||||
struct ath_buf_state {
|
||||
u8 bf_type;
|
||||
u8 bfs_paprd;
|
||||
@ -307,7 +314,7 @@ struct ath_rx {
|
||||
struct ath_descdma rxdma;
|
||||
struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
|
||||
|
||||
struct ath_buf *buf_hold;
|
||||
struct ath_rxbuf *buf_hold;
|
||||
struct sk_buff *frag;
|
||||
|
||||
u32 ampdu_ref;
|
||||
@ -926,7 +933,6 @@ void ath9k_deinit_device(struct ath_softc *sc);
|
||||
void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
|
||||
void ath9k_reload_chainmask_settings(struct ath_softc *sc);
|
||||
|
||||
bool ath9k_uses_beacons(int type);
|
||||
void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
|
||||
int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
|
||||
enum spectral_mode spectral_mode);
|
||||
|
@ -186,7 +186,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah,
|
||||
bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ieee80211_conf *conf = &common->hw->conf;
|
||||
struct ath9k_cal_list *currCal = ah->cal_list_curr;
|
||||
|
||||
if (!ah->caldata)
|
||||
@ -208,7 +207,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
|
||||
return true;
|
||||
|
||||
ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n",
|
||||
currCal->calData->calType, conf->chandef.chan->center_freq);
|
||||
currCal->calData->calType, ah->curchan->chan->center_freq);
|
||||
|
||||
ah->caldata->CalValid &= ~currCal->calData->calType;
|
||||
currCal->calState = CAL_WAITING;
|
||||
@ -242,7 +241,6 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
|
||||
int32_t val;
|
||||
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ieee80211_conf *conf = &common->hw->conf;
|
||||
s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
|
||||
|
||||
if (ah->caldata)
|
||||
@ -252,7 +250,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
|
||||
if (chainmask & (1 << i)) {
|
||||
s16 nfval;
|
||||
|
||||
if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))
|
||||
if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
|
||||
continue;
|
||||
|
||||
if (h)
|
||||
@ -314,7 +312,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
|
||||
ENABLE_REGWRITE_BUFFER(ah);
|
||||
for (i = 0; i < NUM_NF_READINGS; i++) {
|
||||
if (chainmask & (1 << i)) {
|
||||
if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf))
|
||||
if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
|
||||
continue;
|
||||
|
||||
val = REG_READ(ah, ah->nf_regs[i]);
|
||||
@ -408,7 +406,6 @@ void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah,
|
||||
|
||||
ah->caldata->channel = chan->channel;
|
||||
ah->caldata->channelFlags = chan->channelFlags;
|
||||
ah->caldata->chanmode = chan->chanmode;
|
||||
h = ah->caldata->nfCalHist;
|
||||
default_nf = ath9k_hw_get_default_nf(ah, chan);
|
||||
for (i = 0; i < NUM_NF_READINGS; i++) {
|
||||
|
@ -49,103 +49,64 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype);
|
||||
|
||||
static u32 ath9k_get_extchanmode(struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
u32 chanmode = 0;
|
||||
|
||||
switch (chandef->chan->band) {
|
||||
case IEEE80211_BAND_2GHZ:
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
chanmode = CHANNEL_G_HT20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
if (chandef->center_freq1 > chandef->chan->center_freq)
|
||||
chanmode = CHANNEL_G_HT40PLUS;
|
||||
else
|
||||
chanmode = CHANNEL_G_HT40MINUS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IEEE80211_BAND_5GHZ:
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
chanmode = CHANNEL_A_HT20;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
if (chandef->center_freq1 > chandef->chan->center_freq)
|
||||
chanmode = CHANNEL_A_HT40PLUS;
|
||||
else
|
||||
chanmode = CHANNEL_A_HT40MINUS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return chanmode;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update internal channel flags.
|
||||
*/
|
||||
void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
ichan->channel = chandef->chan->center_freq;
|
||||
ichan->chan = chandef->chan;
|
||||
struct ieee80211_channel *chan = chandef->chan;
|
||||
u16 flags = 0;
|
||||
|
||||
if (chandef->chan->band == IEEE80211_BAND_2GHZ) {
|
||||
ichan->chanmode = CHANNEL_G;
|
||||
ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM;
|
||||
} else {
|
||||
ichan->chanmode = CHANNEL_A;
|
||||
ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
|
||||
}
|
||||
ichan->channel = chan->center_freq;
|
||||
ichan->chan = chan;
|
||||
|
||||
if (chan->band == IEEE80211_BAND_5GHZ)
|
||||
flags |= CHANNEL_5GHZ;
|
||||
|
||||
switch (chandef->width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
ichan->channelFlags |= CHANNEL_QUARTER;
|
||||
flags |= CHANNEL_QUARTER;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
ichan->channelFlags |= CHANNEL_HALF;
|
||||
flags |= CHANNEL_HALF;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
flags |= CHANNEL_HT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
ichan->chanmode = ath9k_get_extchanmode(chandef);
|
||||
if (chandef->center_freq1 > chandef->chan->center_freq)
|
||||
flags |= CHANNEL_HT40PLUS | CHANNEL_HT;
|
||||
else
|
||||
flags |= CHANNEL_HT40MINUS | CHANNEL_HT;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
ichan->channelFlags = flags;
|
||||
}
|
||||
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 ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
|
||||
struct ath_hw *ah,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct ieee80211_channel *curchan = hw->conf.chandef.chan;
|
||||
struct ieee80211_channel *curchan = chandef->chan;
|
||||
struct ath9k_channel *channel;
|
||||
u8 chan_idx;
|
||||
|
||||
chan_idx = curchan->hw_value;
|
||||
channel = &ah->channels[chan_idx];
|
||||
ath9k_cmn_update_ichannel(channel, &hw->conf.chandef);
|
||||
ath9k_cmn_update_ichannel(channel, chandef);
|
||||
|
||||
return channel;
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_cmn_get_curchannel);
|
||||
EXPORT_SYMBOL(ath9k_cmn_get_channel);
|
||||
|
||||
int ath9k_cmn_count_streams(unsigned int chainmask, int max)
|
||||
{
|
||||
|
@ -43,10 +43,9 @@
|
||||
(((x) + ((mul)/2)) / (mul))
|
||||
|
||||
int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
|
||||
void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
|
||||
struct ath_hw *ah);
|
||||
struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
|
||||
struct ath_hw *ah,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
int ath9k_cmn_count_streams(unsigned int chainmask, int max);
|
||||
void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
|
||||
enum ath_stomp_type stomp_type);
|
||||
|
@ -24,30 +24,10 @@
|
||||
static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
|
||||
struct ath9k_channel *ichan)
|
||||
{
|
||||
enum htc_phymode mode;
|
||||
if (IS_CHAN_5GHZ(ichan))
|
||||
return HTC_MODE_11NA;
|
||||
|
||||
mode = -EINVAL;
|
||||
|
||||
switch (ichan->chanmode) {
|
||||
case CHANNEL_G:
|
||||
case CHANNEL_G_HT20:
|
||||
case CHANNEL_G_HT40PLUS:
|
||||
case CHANNEL_G_HT40MINUS:
|
||||
mode = HTC_MODE_11NG;
|
||||
break;
|
||||
case CHANNEL_A:
|
||||
case CHANNEL_A_HT20:
|
||||
case CHANNEL_A_HT40PLUS:
|
||||
case CHANNEL_A_HT40MINUS:
|
||||
mode = HTC_MODE_11NA;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
WARN_ON(mode < 0);
|
||||
|
||||
return mode;
|
||||
return HTC_MODE_11NG;
|
||||
}
|
||||
|
||||
bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
|
||||
@ -926,7 +906,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
|
||||
WMI_CMD(WMI_FLUSH_RECV_CMDID);
|
||||
|
||||
/* setup initial channel */
|
||||
init_channel = ath9k_cmn_get_curchannel(hw, ah);
|
||||
init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
|
||||
|
||||
ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
|
||||
if (ret) {
|
||||
@ -1208,9 +1188,7 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
|
||||
ath_dbg(common, CONFIG, "Set channel: %d MHz\n",
|
||||
curchan->center_freq);
|
||||
|
||||
ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
|
||||
&hw->conf.chandef);
|
||||
|
||||
ath9k_cmn_get_channel(hw, priv->ah, &hw->conf.chandef);
|
||||
if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
|
||||
ath_err(common, "Unable to set channel\n");
|
||||
ret = -EINVAL;
|
||||
|
@ -130,29 +130,29 @@ void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause)
|
||||
|
||||
static void ath9k_hw_set_clockrate(struct ath_hw *ah)
|
||||
{
|
||||
struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath9k_channel *chan = ah->curchan;
|
||||
unsigned int clockrate;
|
||||
|
||||
/* AR9287 v1.3+ uses async FIFO and runs the MAC at 117 MHz */
|
||||
if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah))
|
||||
clockrate = 117;
|
||||
else if (!ah->curchan) /* should really check for CCK instead */
|
||||
else if (!chan) /* should really check for CCK instead */
|
||||
clockrate = ATH9K_CLOCK_RATE_CCK;
|
||||
else if (conf->chandef.chan->band == IEEE80211_BAND_2GHZ)
|
||||
else if (IS_CHAN_2GHZ(chan))
|
||||
clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
|
||||
else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)
|
||||
clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
|
||||
else
|
||||
clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM;
|
||||
|
||||
if (conf_is_ht40(conf))
|
||||
if (IS_CHAN_HT40(chan))
|
||||
clockrate *= 2;
|
||||
|
||||
if (ah->curchan) {
|
||||
if (IS_CHAN_HALF_RATE(ah->curchan))
|
||||
if (IS_CHAN_HALF_RATE(chan))
|
||||
clockrate /= 2;
|
||||
if (IS_CHAN_QUARTER_RATE(ah->curchan))
|
||||
if (IS_CHAN_QUARTER_RATE(chan))
|
||||
clockrate /= 4;
|
||||
}
|
||||
|
||||
@ -190,10 +190,7 @@ EXPORT_SYMBOL(ath9k_hw_wait);
|
||||
void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan,
|
||||
int hw_delay)
|
||||
{
|
||||
if (IS_CHAN_B(chan))
|
||||
hw_delay = (4 * hw_delay) / 22;
|
||||
else
|
||||
hw_delay /= 10;
|
||||
hw_delay /= 10;
|
||||
|
||||
if (IS_CHAN_HALF_RATE(chan))
|
||||
hw_delay *= 2;
|
||||
@ -294,8 +291,7 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah,
|
||||
return;
|
||||
}
|
||||
|
||||
if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
|
||||
(chan->chanmode == CHANNEL_G_HT40PLUS)) {
|
||||
if (IS_CHAN_HT40PLUS(chan)) {
|
||||
centers->synth_center =
|
||||
chan->channel + HT40_CHANNEL_CENTER_SHIFT;
|
||||
extoff = 1;
|
||||
@ -1042,7 +1038,6 @@ static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu)
|
||||
void ath9k_hw_init_global_settings(struct ath_hw *ah)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ieee80211_conf *conf = &common->hw->conf;
|
||||
const struct ath9k_channel *chan = ah->curchan;
|
||||
int acktimeout, ctstimeout, ack_offset = 0;
|
||||
int slottime;
|
||||
@ -1117,8 +1112,7 @@ void ath9k_hw_init_global_settings(struct ath_hw *ah)
|
||||
* BA frames in some implementations, but it has been found to fix ACK
|
||||
* timeout issues in other cases as well.
|
||||
*/
|
||||
if (conf->chandef.chan &&
|
||||
conf->chandef.chan->band == IEEE80211_BAND_2GHZ &&
|
||||
if (IS_CHAN_2GHZ(chan) &&
|
||||
!IS_CHAN_HALF_RATE(chan) && !IS_CHAN_QUARTER_RATE(chan)) {
|
||||
acktimeout += 64 - sifstime - ah->slottime;
|
||||
ctstimeout += 48 - sifstime - ah->slottime;
|
||||
@ -1160,9 +1154,7 @@ u32 ath9k_regd_get_ctl(struct ath_regulatory *reg, struct ath9k_channel *chan)
|
||||
{
|
||||
u32 ctl = ath_regd_get_band_ctl(reg, chan->chan->band);
|
||||
|
||||
if (IS_CHAN_B(chan))
|
||||
ctl |= CTL_11B;
|
||||
else if (IS_CHAN_G(chan))
|
||||
if (IS_CHAN_2GHZ(chan))
|
||||
ctl |= CTL_11G;
|
||||
else
|
||||
ctl |= CTL_11A;
|
||||
@ -1510,10 +1502,8 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
|
||||
int r;
|
||||
|
||||
if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) {
|
||||
u32 cur = ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
|
||||
u32 new = chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ);
|
||||
band_switch = (cur != new);
|
||||
mode_diff = (chan->chanmode != ah->curchan->chanmode);
|
||||
band_switch = IS_CHAN_5GHZ(ah->curchan) != IS_CHAN_5GHZ(chan);
|
||||
mode_diff = (chan->channelFlags != ah->curchan->channelFlags);
|
||||
}
|
||||
|
||||
for (qnum = 0; qnum < AR_NUM_QCU; qnum++) {
|
||||
@ -1552,9 +1542,7 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
|
||||
ath9k_hw_set_clockrate(ah);
|
||||
ath9k_hw_apply_txpower(ah, chan, false);
|
||||
|
||||
if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
|
||||
ath9k_hw_set_delta_slope(ah, chan);
|
||||
|
||||
ath9k_hw_set_delta_slope(ah, chan);
|
||||
ath9k_hw_spur_mitigate_freq(ah, chan);
|
||||
|
||||
if (band_switch || ini_reloaded)
|
||||
@ -1824,20 +1812,11 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* If cross-band fcc is not supoprted, bail out if
|
||||
* either channelFlags or chanmode differ.
|
||||
*
|
||||
* chanmode will be different if the HT operating mode
|
||||
* changes because of CSA.
|
||||
* If cross-band fcc is not supoprted, bail out if channelFlags differ.
|
||||
*/
|
||||
if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH)) {
|
||||
if ((chan->channelFlags & CHANNEL_ALL) !=
|
||||
(ah->curchan->channelFlags & CHANNEL_ALL))
|
||||
goto fail;
|
||||
|
||||
if (chan->chanmode != ah->curchan->chanmode)
|
||||
goto fail;
|
||||
}
|
||||
if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) &&
|
||||
chan->channelFlags != ah->curchan->channelFlags)
|
||||
goto fail;
|
||||
|
||||
if (!ath9k_hw_check_alive(ah))
|
||||
goto fail;
|
||||
@ -1899,8 +1878,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
|
||||
|
||||
ah->caldata = caldata;
|
||||
if (caldata && (chan->channel != caldata->channel ||
|
||||
chan->channelFlags != caldata->channelFlags ||
|
||||
chan->chanmode != caldata->chanmode)) {
|
||||
chan->channelFlags != caldata->channelFlags)) {
|
||||
/* Operating channel changed, reset channel calibration data */
|
||||
memset(caldata, 0, sizeof(*caldata));
|
||||
ath9k_init_nfcal_hist_buffer(ah, chan);
|
||||
@ -1989,9 +1967,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
|
||||
|
||||
ath9k_hw_init_mfp(ah);
|
||||
|
||||
if (IS_CHAN_OFDM(chan) || IS_CHAN_HT(chan))
|
||||
ath9k_hw_set_delta_slope(ah, chan);
|
||||
|
||||
ath9k_hw_set_delta_slope(ah, chan);
|
||||
ath9k_hw_spur_mitigate_freq(ah, chan);
|
||||
ah->eep_ops->set_board_values(ah, chan);
|
||||
|
||||
@ -2968,12 +2944,11 @@ void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set)
|
||||
}
|
||||
EXPORT_SYMBOL(ath9k_hw_set_tsfadjust);
|
||||
|
||||
void ath9k_hw_set11nmac2040(struct ath_hw *ah)
|
||||
void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan)
|
||||
{
|
||||
struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf;
|
||||
u32 macmode;
|
||||
|
||||
if (conf_is_ht40(conf) && !ah->config.cwm_ignore_extcca)
|
||||
if (IS_CHAN_HT40(chan) && !ah->config.cwm_ignore_extcca)
|
||||
macmode = AR_2040_JOINED_RX_CLEAR;
|
||||
else
|
||||
macmode = 0;
|
||||
|
@ -369,36 +369,6 @@ enum ath9k_int {
|
||||
ATH9K_INT_NOCARD = 0xffffffff
|
||||
};
|
||||
|
||||
#define CHANNEL_CCK 0x00020
|
||||
#define CHANNEL_OFDM 0x00040
|
||||
#define CHANNEL_2GHZ 0x00080
|
||||
#define CHANNEL_5GHZ 0x00100
|
||||
#define CHANNEL_PASSIVE 0x00200
|
||||
#define CHANNEL_DYN 0x00400
|
||||
#define CHANNEL_HALF 0x04000
|
||||
#define CHANNEL_QUARTER 0x08000
|
||||
#define CHANNEL_HT20 0x10000
|
||||
#define CHANNEL_HT40PLUS 0x20000
|
||||
#define CHANNEL_HT40MINUS 0x40000
|
||||
|
||||
#define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM)
|
||||
#define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK)
|
||||
#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM)
|
||||
#define CHANNEL_G_HT20 (CHANNEL_2GHZ|CHANNEL_HT20)
|
||||
#define CHANNEL_A_HT20 (CHANNEL_5GHZ|CHANNEL_HT20)
|
||||
#define CHANNEL_G_HT40PLUS (CHANNEL_2GHZ|CHANNEL_HT40PLUS)
|
||||
#define CHANNEL_G_HT40MINUS (CHANNEL_2GHZ|CHANNEL_HT40MINUS)
|
||||
#define CHANNEL_A_HT40PLUS (CHANNEL_5GHZ|CHANNEL_HT40PLUS)
|
||||
#define CHANNEL_A_HT40MINUS (CHANNEL_5GHZ|CHANNEL_HT40MINUS)
|
||||
#define CHANNEL_ALL \
|
||||
(CHANNEL_OFDM| \
|
||||
CHANNEL_CCK| \
|
||||
CHANNEL_2GHZ | \
|
||||
CHANNEL_5GHZ | \
|
||||
CHANNEL_HT20 | \
|
||||
CHANNEL_HT40PLUS | \
|
||||
CHANNEL_HT40MINUS)
|
||||
|
||||
#define MAX_RTT_TABLE_ENTRY 6
|
||||
#define MAX_IQCAL_MEASUREMENT 8
|
||||
#define MAX_CL_TAB_ENTRY 16
|
||||
@ -417,8 +387,7 @@ enum ath9k_cal_flags {
|
||||
|
||||
struct ath9k_hw_cal_data {
|
||||
u16 channel;
|
||||
u32 channelFlags;
|
||||
u32 chanmode;
|
||||
u16 channelFlags;
|
||||
unsigned long cal_flags;
|
||||
int32_t CalValid;
|
||||
int8_t iCoff;
|
||||
@ -436,33 +405,34 @@ struct ath9k_hw_cal_data {
|
||||
struct ath9k_channel {
|
||||
struct ieee80211_channel *chan;
|
||||
u16 channel;
|
||||
u32 channelFlags;
|
||||
u32 chanmode;
|
||||
u16 channelFlags;
|
||||
s16 noisefloor;
|
||||
};
|
||||
|
||||
#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \
|
||||
(((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \
|
||||
(((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \
|
||||
(((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS))
|
||||
#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0)
|
||||
#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0)
|
||||
#define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0)
|
||||
#define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0)
|
||||
#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0)
|
||||
#define IS_CHAN_A_FAST_CLOCK(_ah, _c) \
|
||||
((((_c)->channelFlags & CHANNEL_5GHZ) != 0) && \
|
||||
((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK))
|
||||
#define CHANNEL_5GHZ BIT(0)
|
||||
#define CHANNEL_HALF BIT(1)
|
||||
#define CHANNEL_QUARTER BIT(2)
|
||||
#define CHANNEL_HT BIT(3)
|
||||
#define CHANNEL_HT40PLUS BIT(4)
|
||||
#define CHANNEL_HT40MINUS BIT(5)
|
||||
|
||||
/* These macros check chanmode and not channelFlags */
|
||||
#define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B)
|
||||
#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) || \
|
||||
((_c)->chanmode == CHANNEL_G_HT20))
|
||||
#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) || \
|
||||
((_c)->chanmode == CHANNEL_A_HT40MINUS) || \
|
||||
((_c)->chanmode == CHANNEL_G_HT40PLUS) || \
|
||||
((_c)->chanmode == CHANNEL_G_HT40MINUS))
|
||||
#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c)))
|
||||
#define IS_CHAN_5GHZ(_c) (!!((_c)->channelFlags & CHANNEL_5GHZ))
|
||||
#define IS_CHAN_2GHZ(_c) (!IS_CHAN_5GHZ(_c))
|
||||
|
||||
#define IS_CHAN_HALF_RATE(_c) (!!((_c)->channelFlags & CHANNEL_HALF))
|
||||
#define IS_CHAN_QUARTER_RATE(_c) (!!((_c)->channelFlags & CHANNEL_QUARTER))
|
||||
#define IS_CHAN_A_FAST_CLOCK(_ah, _c) \
|
||||
(IS_CHAN_5GHZ(_c) && ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK))
|
||||
|
||||
#define IS_CHAN_HT(_c) ((_c)->channelFlags & CHANNEL_HT)
|
||||
|
||||
#define IS_CHAN_HT20(_c) (IS_CHAN_HT(_c) && !IS_CHAN_HT40(_c))
|
||||
|
||||
#define IS_CHAN_HT40(_c) \
|
||||
(!!((_c)->channelFlags & (CHANNEL_HT40PLUS | CHANNEL_HT40MINUS)))
|
||||
|
||||
#define IS_CHAN_HT40PLUS(_c) ((_c)->channelFlags & CHANNEL_HT40PLUS)
|
||||
#define IS_CHAN_HT40MINUS(_c) ((_c)->channelFlags & CHANNEL_HT40MINUS)
|
||||
|
||||
enum ath9k_power_mode {
|
||||
ATH9K_PM_AWAKE = 0,
|
||||
@ -1033,7 +1003,7 @@ void ath9k_hw_reset_tsf(struct ath_hw *ah);
|
||||
void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set);
|
||||
void ath9k_hw_init_global_settings(struct ath_hw *ah);
|
||||
u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah);
|
||||
void ath9k_hw_set11nmac2040(struct ath_hw *ah);
|
||||
void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan);
|
||||
void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period);
|
||||
void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
|
||||
const struct ath9k_beacon_state *bs);
|
||||
|
@ -347,7 +347,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
u8 *ds;
|
||||
struct ath_buf *bf;
|
||||
int i, bsize, desc_len;
|
||||
|
||||
ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n",
|
||||
@ -399,33 +398,68 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
|
||||
ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
|
||||
|
||||
/* allocate buffers */
|
||||
bsize = sizeof(struct ath_buf) * nbuf;
|
||||
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
|
||||
if (!bf)
|
||||
return -ENOMEM;
|
||||
if (is_tx) {
|
||||
struct ath_buf *bf;
|
||||
|
||||
for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
|
||||
bf->bf_desc = ds;
|
||||
bf->bf_daddr = DS2PHYS(dd, ds);
|
||||
bsize = sizeof(struct ath_buf) * nbuf;
|
||||
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
|
||||
if (!bf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!(sc->sc_ah->caps.hw_caps &
|
||||
ATH9K_HW_CAP_4KB_SPLITTRANS)) {
|
||||
/*
|
||||
* Skip descriptor addresses which can cause 4KB
|
||||
* boundary crossing (addr + length) with a 32 dword
|
||||
* descriptor fetch.
|
||||
*/
|
||||
while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
|
||||
BUG_ON((caddr_t) bf->bf_desc >=
|
||||
((caddr_t) dd->dd_desc +
|
||||
dd->dd_desc_len));
|
||||
for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
|
||||
bf->bf_desc = ds;
|
||||
bf->bf_daddr = DS2PHYS(dd, ds);
|
||||
|
||||
ds += (desc_len * ndesc);
|
||||
bf->bf_desc = ds;
|
||||
bf->bf_daddr = DS2PHYS(dd, ds);
|
||||
if (!(sc->sc_ah->caps.hw_caps &
|
||||
ATH9K_HW_CAP_4KB_SPLITTRANS)) {
|
||||
/*
|
||||
* Skip descriptor addresses which can cause 4KB
|
||||
* boundary crossing (addr + length) with a 32 dword
|
||||
* descriptor fetch.
|
||||
*/
|
||||
while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
|
||||
BUG_ON((caddr_t) bf->bf_desc >=
|
||||
((caddr_t) dd->dd_desc +
|
||||
dd->dd_desc_len));
|
||||
|
||||
ds += (desc_len * ndesc);
|
||||
bf->bf_desc = ds;
|
||||
bf->bf_daddr = DS2PHYS(dd, ds);
|
||||
}
|
||||
}
|
||||
list_add_tail(&bf->list, head);
|
||||
}
|
||||
} else {
|
||||
struct ath_rxbuf *bf;
|
||||
|
||||
bsize = sizeof(struct ath_rxbuf) * nbuf;
|
||||
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
|
||||
if (!bf)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
|
||||
bf->bf_desc = ds;
|
||||
bf->bf_daddr = DS2PHYS(dd, ds);
|
||||
|
||||
if (!(sc->sc_ah->caps.hw_caps &
|
||||
ATH9K_HW_CAP_4KB_SPLITTRANS)) {
|
||||
/*
|
||||
* Skip descriptor addresses which can cause 4KB
|
||||
* boundary crossing (addr + length) with a 32 dword
|
||||
* descriptor fetch.
|
||||
*/
|
||||
while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
|
||||
BUG_ON((caddr_t) bf->bf_desc >=
|
||||
((caddr_t) dd->dd_desc +
|
||||
dd->dd_desc_len));
|
||||
|
||||
ds += (desc_len * ndesc);
|
||||
bf->bf_desc = ds;
|
||||
bf->bf_daddr = DS2PHYS(dd, ds);
|
||||
}
|
||||
}
|
||||
list_add_tail(&bf->list, head);
|
||||
}
|
||||
list_add_tail(&bf->list, head);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -437,7 +471,6 @@ static int ath9k_init_queues(struct ath_softc *sc)
|
||||
sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah);
|
||||
sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
|
||||
|
||||
sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
|
||||
ath_cabq_update(sc);
|
||||
|
||||
sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0);
|
||||
@ -768,7 +801,7 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
|
||||
chan = &sband->channels[i];
|
||||
ah->curchan = &ah->channels[chan->hw_value];
|
||||
cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
|
||||
ath9k_cmn_update_ichannel(ah->curchan, &chandef);
|
||||
ath9k_cmn_get_channel(sc->hw, ah, &chandef);
|
||||
ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true);
|
||||
}
|
||||
}
|
||||
|
@ -374,7 +374,6 @@ EXPORT_SYMBOL(ath9k_hw_releasetxqueue);
|
||||
bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath9k_channel *chan = ah->curchan;
|
||||
struct ath9k_tx_queue_info *qi;
|
||||
u32 cwMin, chanCwMin, value;
|
||||
|
||||
@ -387,10 +386,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
|
||||
ath_dbg(common, QUEUE, "Reset TX queue: %u\n", q);
|
||||
|
||||
if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) {
|
||||
if (chan && IS_CHAN_B(chan))
|
||||
chanCwMin = INIT_CWMIN_11B;
|
||||
else
|
||||
chanCwMin = INIT_CWMIN;
|
||||
chanCwMin = INIT_CWMIN;
|
||||
|
||||
for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1);
|
||||
} else
|
||||
|
@ -603,8 +603,6 @@ enum ath9k_tx_queue_flags {
|
||||
#define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001
|
||||
|
||||
#define ATH9K_DECOMP_MASK_SIZE 128
|
||||
#define ATH9K_READY_TIME_LO_BOUND 50
|
||||
#define ATH9K_READY_TIME_HI_BOUND 96
|
||||
|
||||
enum ath9k_pkt_type {
|
||||
ATH9K_PKT_TYPE_NORMAL = 0,
|
||||
|
@ -302,17 +302,91 @@ out:
|
||||
* by reseting the chip. To accomplish this we must first cleanup any pending
|
||||
* DMA, then restart stuff.
|
||||
*/
|
||||
static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
|
||||
struct ath9k_channel *hchan)
|
||||
static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ieee80211_hw *hw = sc->hw;
|
||||
struct ath9k_channel *hchan;
|
||||
struct ieee80211_channel *chan = chandef->chan;
|
||||
unsigned long flags;
|
||||
bool offchannel;
|
||||
int pos = chan->hw_value;
|
||||
int old_pos = -1;
|
||||
int r;
|
||||
|
||||
if (test_bit(SC_OP_INVALID, &sc->sc_flags))
|
||||
return -EIO;
|
||||
|
||||
r = ath_reset_internal(sc, hchan);
|
||||
offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
|
||||
|
||||
return r;
|
||||
if (ah->curchan)
|
||||
old_pos = ah->curchan - &ah->channels[0];
|
||||
|
||||
ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
|
||||
chan->center_freq, chandef->width);
|
||||
|
||||
/* update survey stats for the old channel before switching */
|
||||
spin_lock_irqsave(&common->cc_lock, flags);
|
||||
ath_update_survey_stats(sc);
|
||||
spin_unlock_irqrestore(&common->cc_lock, flags);
|
||||
|
||||
ath9k_cmn_get_channel(hw, ah, chandef);
|
||||
|
||||
/*
|
||||
* If the operating channel changes, change the survey in-use flags
|
||||
* along with it.
|
||||
* Reset the survey data for the new channel, unless we're switching
|
||||
* back to the operating channel from an off-channel operation.
|
||||
*/
|
||||
if (!offchannel && sc->cur_survey != &sc->survey[pos]) {
|
||||
if (sc->cur_survey)
|
||||
sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
|
||||
|
||||
sc->cur_survey = &sc->survey[pos];
|
||||
|
||||
memset(sc->cur_survey, 0, sizeof(struct survey_info));
|
||||
sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
|
||||
} else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
|
||||
memset(&sc->survey[pos], 0, sizeof(struct survey_info));
|
||||
}
|
||||
|
||||
hchan = &sc->sc_ah->channels[pos];
|
||||
r = ath_reset_internal(sc, hchan);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
/*
|
||||
* The most recent snapshot of channel->noisefloor for the old
|
||||
* channel is only available after the hardware reset. Copy it to
|
||||
* the survey stats now.
|
||||
*/
|
||||
if (old_pos >= 0)
|
||||
ath_update_survey_nf(sc, old_pos);
|
||||
|
||||
/*
|
||||
* Enable radar pulse detection if on a DFS channel. Spectral
|
||||
* scanning and radar detection can not be used concurrently.
|
||||
*/
|
||||
if (hw->conf.radar_enabled) {
|
||||
u32 rxfilter;
|
||||
|
||||
/* set HW specific DFS configuration */
|
||||
ath9k_hw_set_radar_params(ah);
|
||||
rxfilter = ath9k_hw_getrxfilter(ah);
|
||||
rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
|
||||
ATH9K_RX_FILTER_PHYERR;
|
||||
ath9k_hw_setrxfilter(ah, rxfilter);
|
||||
ath_dbg(common, DFS, "DFS enabled at freq %d\n",
|
||||
chan->center_freq);
|
||||
} else {
|
||||
/* perform spectral scan if requested. */
|
||||
if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
|
||||
sc->spectral_mode == SPECTRAL_CHANSCAN)
|
||||
ath9k_spectral_scan_trigger(hw);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
|
||||
@ -601,7 +675,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
|
||||
ath9k_ps_wakeup(sc);
|
||||
mutex_lock(&sc->mutex);
|
||||
|
||||
init_channel = ath9k_cmn_get_curchannel(hw, ah);
|
||||
init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
|
||||
|
||||
/* Reset SERDES registers */
|
||||
ath9k_hw_configpcipowersave(ah, false);
|
||||
@ -804,7 +878,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
|
||||
}
|
||||
|
||||
if (!ah->curchan)
|
||||
ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
|
||||
ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
|
||||
|
||||
ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
|
||||
ath9k_hw_phy_disable(ah);
|
||||
@ -823,7 +897,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
|
||||
ath_dbg(common, CONFIG, "Driver halt\n");
|
||||
}
|
||||
|
||||
bool ath9k_uses_beacons(int type)
|
||||
static bool ath9k_uses_beacons(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
@ -1208,81 +1282,12 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
|
||||
}
|
||||
|
||||
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
|
||||
struct ieee80211_channel *curchan = hw->conf.chandef.chan;
|
||||
int pos = curchan->hw_value;
|
||||
int old_pos = -1;
|
||||
unsigned long flags;
|
||||
|
||||
if (ah->curchan)
|
||||
old_pos = ah->curchan - &ah->channels[0];
|
||||
|
||||
ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
|
||||
curchan->center_freq, hw->conf.chandef.width);
|
||||
|
||||
/* update survey stats for the old channel before switching */
|
||||
spin_lock_irqsave(&common->cc_lock, flags);
|
||||
ath_update_survey_stats(sc);
|
||||
spin_unlock_irqrestore(&common->cc_lock, flags);
|
||||
|
||||
ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
|
||||
&conf->chandef);
|
||||
|
||||
/*
|
||||
* If the operating channel changes, change the survey in-use flags
|
||||
* along with it.
|
||||
* Reset the survey data for the new channel, unless we're switching
|
||||
* back to the operating channel from an off-channel operation.
|
||||
*/
|
||||
if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) &&
|
||||
sc->cur_survey != &sc->survey[pos]) {
|
||||
|
||||
if (sc->cur_survey)
|
||||
sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
|
||||
|
||||
sc->cur_survey = &sc->survey[pos];
|
||||
|
||||
memset(sc->cur_survey, 0, sizeof(struct survey_info));
|
||||
sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
|
||||
} else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
|
||||
memset(&sc->survey[pos], 0, sizeof(struct survey_info));
|
||||
}
|
||||
|
||||
if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
|
||||
if (ath_set_channel(sc, &hw->conf.chandef) < 0) {
|
||||
ath_err(common, "Unable to set channel\n");
|
||||
mutex_unlock(&sc->mutex);
|
||||
ath9k_ps_restore(sc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The most recent snapshot of channel->noisefloor for the old
|
||||
* channel is only available after the hardware reset. Copy it to
|
||||
* the survey stats now.
|
||||
*/
|
||||
if (old_pos >= 0)
|
||||
ath_update_survey_nf(sc, old_pos);
|
||||
|
||||
/*
|
||||
* Enable radar pulse detection if on a DFS channel. Spectral
|
||||
* scanning and radar detection can not be used concurrently.
|
||||
*/
|
||||
if (hw->conf.radar_enabled) {
|
||||
u32 rxfilter;
|
||||
|
||||
/* set HW specific DFS configuration */
|
||||
ath9k_hw_set_radar_params(ah);
|
||||
rxfilter = ath9k_hw_getrxfilter(ah);
|
||||
rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
|
||||
ATH9K_RX_FILTER_PHYERR;
|
||||
ath9k_hw_setrxfilter(ah, rxfilter);
|
||||
ath_dbg(common, DFS, "DFS enabled at freq %d\n",
|
||||
curchan->center_freq);
|
||||
} else {
|
||||
/* perform spectral scan if requested. */
|
||||
if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
|
||||
sc->spectral_mode == SPECTRAL_CHANSCAN)
|
||||
ath9k_spectral_scan_trigger(hw);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed & IEEE80211_CONF_CHANGE_POWER) {
|
||||
|
@ -661,9 +661,9 @@ void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all)
|
||||
chan_start = wlan_chan - 10;
|
||||
chan_end = wlan_chan + 10;
|
||||
|
||||
if (chan->chanmode == CHANNEL_G_HT40PLUS)
|
||||
if (IS_CHAN_HT40PLUS(chan))
|
||||
chan_end += 20;
|
||||
else if (chan->chanmode == CHANNEL_G_HT40MINUS)
|
||||
else if (IS_CHAN_HT40MINUS(chan))
|
||||
chan_start -= 20;
|
||||
|
||||
/* adjust side band */
|
||||
@ -707,11 +707,11 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
|
||||
|
||||
if (setchannel) {
|
||||
struct ath9k_hw_cal_data *caldata = &sc->caldata;
|
||||
if ((caldata->chanmode == CHANNEL_G_HT40PLUS) &&
|
||||
if (IS_CHAN_HT40PLUS(ah->curchan) &&
|
||||
(ah->curchan->channel > caldata->channel) &&
|
||||
(ah->curchan->channel <= caldata->channel + 20))
|
||||
return;
|
||||
if ((caldata->chanmode == CHANNEL_G_HT40MINUS) &&
|
||||
if (IS_CHAN_HT40MINUS(ah->curchan) &&
|
||||
(ah->curchan->channel < caldata->channel) &&
|
||||
(ah->curchan->channel >= caldata->channel - 20))
|
||||
return;
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "ath9k.h"
|
||||
#include "ar9003_mac.h"
|
||||
|
||||
#define SKB_CB_ATHBUF(__skb) (*((struct ath_buf **)__skb->cb))
|
||||
#define SKB_CB_ATHBUF(__skb) (*((struct ath_rxbuf **)__skb->cb))
|
||||
|
||||
static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
|
||||
{
|
||||
@ -35,7 +35,7 @@ static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
|
||||
* buffer (or rx fifo). This can incorrectly acknowledge packets
|
||||
* to a sender if last desc is self-linked.
|
||||
*/
|
||||
static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
|
||||
static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf)
|
||||
{
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
@ -68,7 +68,7 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
|
||||
sc->rx.rxlink = &ds->ds_link;
|
||||
}
|
||||
|
||||
static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf)
|
||||
static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf)
|
||||
{
|
||||
if (sc->rx.buf_hold)
|
||||
ath_rx_buf_link(sc, sc->rx.buf_hold);
|
||||
@ -112,13 +112,13 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc,
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_rx_edma *rx_edma;
|
||||
struct sk_buff *skb;
|
||||
struct ath_buf *bf;
|
||||
struct ath_rxbuf *bf;
|
||||
|
||||
rx_edma = &sc->rx.rx_edma[qtype];
|
||||
if (skb_queue_len(&rx_edma->rx_fifo) >= rx_edma->rx_fifo_hwsize)
|
||||
return false;
|
||||
|
||||
bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
|
||||
bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
|
||||
list_del_init(&bf->list);
|
||||
|
||||
skb = bf->bf_mpdu;
|
||||
@ -138,7 +138,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc,
|
||||
enum ath9k_rx_qtype qtype)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
struct ath_buf *bf, *tbf;
|
||||
struct ath_rxbuf *bf, *tbf;
|
||||
|
||||
if (list_empty(&sc->rx.rxbuf)) {
|
||||
ath_dbg(common, QUEUE, "No free rx buf available\n");
|
||||
@ -154,7 +154,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc,
|
||||
static void ath_rx_remove_buffer(struct ath_softc *sc,
|
||||
enum ath9k_rx_qtype qtype)
|
||||
{
|
||||
struct ath_buf *bf;
|
||||
struct ath_rxbuf *bf;
|
||||
struct ath_rx_edma *rx_edma;
|
||||
struct sk_buff *skb;
|
||||
|
||||
@ -171,7 +171,7 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc)
|
||||
{
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath_buf *bf;
|
||||
struct ath_rxbuf *bf;
|
||||
|
||||
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP);
|
||||
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP);
|
||||
@ -199,7 +199,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct sk_buff *skb;
|
||||
struct ath_buf *bf;
|
||||
struct ath_rxbuf *bf;
|
||||
int error = 0, i;
|
||||
u32 size;
|
||||
|
||||
@ -211,7 +211,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
|
||||
ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP],
|
||||
ah->caps.rx_hp_qdepth);
|
||||
|
||||
size = sizeof(struct ath_buf) * nbufs;
|
||||
size = sizeof(struct ath_rxbuf) * nbufs;
|
||||
bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
|
||||
if (!bf)
|
||||
return -ENOMEM;
|
||||
@ -271,7 +271,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
|
||||
{
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
struct sk_buff *skb;
|
||||
struct ath_buf *bf;
|
||||
struct ath_rxbuf *bf;
|
||||
int error = 0;
|
||||
|
||||
spin_lock_init(&sc->sc_pcu_lock);
|
||||
@ -332,7 +332,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct sk_buff *skb;
|
||||
struct ath_buf *bf;
|
||||
struct ath_rxbuf *bf;
|
||||
|
||||
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
|
||||
ath_rx_edma_cleanup(sc);
|
||||
@ -427,7 +427,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
|
||||
int ath_startrecv(struct ath_softc *sc)
|
||||
{
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_buf *bf, *tbf;
|
||||
struct ath_rxbuf *bf, *tbf;
|
||||
|
||||
if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
|
||||
ath_edma_start_recv(sc);
|
||||
@ -447,7 +447,7 @@ int ath_startrecv(struct ath_softc *sc)
|
||||
if (list_empty(&sc->rx.rxbuf))
|
||||
goto start_recv;
|
||||
|
||||
bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
|
||||
bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
|
||||
ath9k_hw_putrxbuf(ah, bf->bf_daddr);
|
||||
ath9k_hw_rxena(ah);
|
||||
|
||||
@ -603,13 +603,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon)
|
||||
static bool ath_edma_get_buffers(struct ath_softc *sc,
|
||||
enum ath9k_rx_qtype qtype,
|
||||
struct ath_rx_status *rs,
|
||||
struct ath_buf **dest)
|
||||
struct ath_rxbuf **dest)
|
||||
{
|
||||
struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype];
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct sk_buff *skb;
|
||||
struct ath_buf *bf;
|
||||
struct ath_rxbuf *bf;
|
||||
int ret;
|
||||
|
||||
skb = skb_peek(&rx_edma->rx_fifo);
|
||||
@ -653,11 +653,11 @@ static bool ath_edma_get_buffers(struct ath_softc *sc,
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
|
||||
static struct ath_rxbuf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
|
||||
struct ath_rx_status *rs,
|
||||
enum ath9k_rx_qtype qtype)
|
||||
{
|
||||
struct ath_buf *bf = NULL;
|
||||
struct ath_rxbuf *bf = NULL;
|
||||
|
||||
while (ath_edma_get_buffers(sc, qtype, rs, &bf)) {
|
||||
if (!bf)
|
||||
@ -668,13 +668,13 @@ static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
|
||||
static struct ath_rxbuf *ath_get_next_rx_buf(struct ath_softc *sc,
|
||||
struct ath_rx_status *rs)
|
||||
{
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath_common *common = ath9k_hw_common(ah);
|
||||
struct ath_desc *ds;
|
||||
struct ath_buf *bf;
|
||||
struct ath_rxbuf *bf;
|
||||
int ret;
|
||||
|
||||
if (list_empty(&sc->rx.rxbuf)) {
|
||||
@ -682,7 +682,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
|
||||
bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
|
||||
if (bf == sc->rx.buf_hold)
|
||||
return NULL;
|
||||
|
||||
@ -702,7 +702,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
|
||||
ret = ath9k_hw_rxprocdesc(ah, ds, rs);
|
||||
if (ret == -EINPROGRESS) {
|
||||
struct ath_rx_status trs;
|
||||
struct ath_buf *tbf;
|
||||
struct ath_rxbuf *tbf;
|
||||
struct ath_desc *tds;
|
||||
|
||||
memset(&trs, 0, sizeof(trs));
|
||||
@ -711,7 +711,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tbf = list_entry(bf->list.next, struct ath_buf, list);
|
||||
tbf = list_entry(bf->list.next, struct ath_rxbuf, list);
|
||||
|
||||
/*
|
||||
* On some hardware the descriptor status words could
|
||||
@ -1308,7 +1308,7 @@ static void ath9k_apply_ampdu_details(struct ath_softc *sc,
|
||||
|
||||
int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
|
||||
{
|
||||
struct ath_buf *bf;
|
||||
struct ath_rxbuf *bf;
|
||||
struct sk_buff *skb = NULL, *requeue_skb, *hdr_skb;
|
||||
struct ieee80211_rx_status *rxs;
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
|
@ -1704,16 +1704,9 @@ int ath_cabq_update(struct ath_softc *sc)
|
||||
int qnum = sc->beacon.cabq->axq_qnum;
|
||||
|
||||
ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
|
||||
/*
|
||||
* Ensure the readytime % is within the bounds.
|
||||
*/
|
||||
if (sc->config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND)
|
||||
sc->config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND;
|
||||
else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND)
|
||||
sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND;
|
||||
|
||||
qi.tqi_readyTime = (cur_conf->beacon_interval *
|
||||
sc->config.cabqReadytime) / 100;
|
||||
ATH_CABQ_READY_TIME) / 100;
|
||||
ath_txq_update(sc, qnum, &qi);
|
||||
|
||||
return 0;
|
||||
@ -2037,8 +2030,7 @@ u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate)
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
struct ath9k_channel *curchan = ah->curchan;
|
||||
|
||||
if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) &&
|
||||
(curchan->channelFlags & CHANNEL_5GHZ) &&
|
||||
if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && IS_CHAN_5GHZ(curchan) &&
|
||||
(chainmask == 0x7) && (rate < 0x90))
|
||||
return 0x3;
|
||||
else if (AR_SREV_9462(ah) && ath9k_hw_btcoex_is_enabled(ah) &&
|
||||
|
16
drivers/net/wireless/ath/wcn36xx/Kconfig
Normal file
16
drivers/net/wireless/ath/wcn36xx/Kconfig
Normal file
@ -0,0 +1,16 @@
|
||||
config WCN36XX
|
||||
tristate "Qualcomm Atheros WCN3660/3680 support"
|
||||
depends on MAC80211 && HAS_DMA
|
||||
---help---
|
||||
This module adds support for wireless adapters based on
|
||||
Qualcomm Atheros WCN3660 and WCN3680 mobile chipsets.
|
||||
|
||||
If you choose to build a module, it'll be called wcn36xx.
|
||||
|
||||
config WCN36XX_DEBUGFS
|
||||
bool "WCN36XX debugfs support"
|
||||
depends on WCN36XX
|
||||
---help---
|
||||
Enabled debugfs support
|
||||
|
||||
If unsure, say Y to make it easier to debug problems.
|
7
drivers/net/wireless/ath/wcn36xx/Makefile
Normal file
7
drivers/net/wireless/ath/wcn36xx/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
obj-$(CONFIG_WCN36XX) := wcn36xx.o
|
||||
wcn36xx-y += main.o \
|
||||
dxe.o \
|
||||
txrx.o \
|
||||
smd.o \
|
||||
pmc.o \
|
||||
debug.o
|
181
drivers/net/wireless/ath/wcn36xx/debug.c
Normal file
181
drivers/net/wireless/ath/wcn36xx/debug.c
Normal file
@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "wcn36xx.h"
|
||||
#include "debug.h"
|
||||
#include "pmc.h"
|
||||
|
||||
#ifdef CONFIG_WCN36XX_DEBUGFS
|
||||
|
||||
static ssize_t read_file_bool_bmps(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct wcn36xx *wcn = file->private_data;
|
||||
struct wcn36xx_vif *vif_priv = NULL;
|
||||
struct ieee80211_vif *vif = NULL;
|
||||
char buf[3];
|
||||
|
||||
list_for_each_entry(vif_priv, &wcn->vif_list, list) {
|
||||
vif = container_of((void *)vif_priv,
|
||||
struct ieee80211_vif,
|
||||
drv_priv);
|
||||
if (NL80211_IFTYPE_STATION == vif->type) {
|
||||
if (vif_priv->pw_state == WCN36XX_BMPS)
|
||||
buf[0] = '1';
|
||||
else
|
||||
buf[0] = '0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
buf[1] = '\n';
|
||||
buf[2] = 0x00;
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
|
||||
}
|
||||
|
||||
static ssize_t write_file_bool_bmps(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct wcn36xx *wcn = file->private_data;
|
||||
struct wcn36xx_vif *vif_priv = NULL;
|
||||
struct ieee80211_vif *vif = NULL;
|
||||
|
||||
char buf[32];
|
||||
int buf_size;
|
||||
|
||||
buf_size = min(count, (sizeof(buf)-1));
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
switch (buf[0]) {
|
||||
case 'y':
|
||||
case 'Y':
|
||||
case '1':
|
||||
list_for_each_entry(vif_priv, &wcn->vif_list, list) {
|
||||
vif = container_of((void *)vif_priv,
|
||||
struct ieee80211_vif,
|
||||
drv_priv);
|
||||
if (NL80211_IFTYPE_STATION == vif->type) {
|
||||
wcn36xx_enable_keep_alive_null_packet(wcn, vif);
|
||||
wcn36xx_pmc_enter_bmps_state(wcn, vif);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
case 'N':
|
||||
case '0':
|
||||
list_for_each_entry(vif_priv, &wcn->vif_list, list) {
|
||||
vif = container_of((void *)vif_priv,
|
||||
struct ieee80211_vif,
|
||||
drv_priv);
|
||||
if (NL80211_IFTYPE_STATION == vif->type)
|
||||
wcn36xx_pmc_exit_bmps_state(wcn, vif);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_wcn36xx_bmps = {
|
||||
.open = simple_open,
|
||||
.read = read_file_bool_bmps,
|
||||
.write = write_file_bool_bmps,
|
||||
};
|
||||
|
||||
static ssize_t write_file_dump(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct wcn36xx *wcn = file->private_data;
|
||||
char buf[255], *tmp;
|
||||
int buf_size;
|
||||
u32 arg[WCN36xx_MAX_DUMP_ARGS];
|
||||
int i;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
memset(arg, 0, sizeof(arg));
|
||||
|
||||
buf_size = min(count, (sizeof(buf) - 1));
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
tmp = buf;
|
||||
|
||||
for (i = 0; i < WCN36xx_MAX_DUMP_ARGS; i++) {
|
||||
char *begin;
|
||||
begin = strsep(&tmp, " ");
|
||||
if (begin == NULL)
|
||||
break;
|
||||
|
||||
if (kstrtoul(begin, 0, (unsigned long *)(arg + i)) != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
wcn36xx_info("DUMP args is %d %d %d %d %d\n", arg[0], arg[1], arg[2],
|
||||
arg[3], arg[4]);
|
||||
wcn36xx_smd_dump_cmd_req(wcn, arg[0], arg[1], arg[2], arg[3], arg[4]);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_wcn36xx_dump = {
|
||||
.open = simple_open,
|
||||
.write = write_file_dump,
|
||||
};
|
||||
|
||||
#define ADD_FILE(name, mode, fop, priv_data) \
|
||||
do { \
|
||||
struct dentry *d; \
|
||||
d = debugfs_create_file(__stringify(name), \
|
||||
mode, dfs->rootdir, \
|
||||
priv_data, fop); \
|
||||
dfs->file_##name.dentry = d; \
|
||||
if (IS_ERR(d)) { \
|
||||
wcn36xx_warn("Create the debugfs entry failed");\
|
||||
dfs->file_##name.dentry = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
void wcn36xx_debugfs_init(struct wcn36xx *wcn)
|
||||
{
|
||||
struct wcn36xx_dfs_entry *dfs = &wcn->dfs;
|
||||
|
||||
dfs->rootdir = debugfs_create_dir(KBUILD_MODNAME,
|
||||
wcn->hw->wiphy->debugfsdir);
|
||||
if (IS_ERR(dfs->rootdir)) {
|
||||
wcn36xx_warn("Create the debugfs failed\n");
|
||||
dfs->rootdir = NULL;
|
||||
}
|
||||
|
||||
ADD_FILE(bmps_switcher, S_IRUSR | S_IWUSR,
|
||||
&fops_wcn36xx_bmps, wcn);
|
||||
ADD_FILE(dump, S_IWUSR, &fops_wcn36xx_dump, wcn);
|
||||
}
|
||||
|
||||
void wcn36xx_debugfs_exit(struct wcn36xx *wcn)
|
||||
{
|
||||
struct wcn36xx_dfs_entry *dfs = &wcn->dfs;
|
||||
debugfs_remove_recursive(dfs->rootdir);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_WCN36XX_DEBUGFS */
|
49
drivers/net/wireless/ath/wcn36xx/debug.h
Normal file
49
drivers/net/wireless/ath/wcn36xx/debug.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
*
|
||||
* 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 _WCN36XX_DEBUG_H_
|
||||
#define _WCN36XX_DEBUG_H_
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define WCN36xx_MAX_DUMP_ARGS 5
|
||||
|
||||
#ifdef CONFIG_WCN36XX_DEBUGFS
|
||||
struct wcn36xx_dfs_file {
|
||||
struct dentry *dentry;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
struct wcn36xx_dfs_entry {
|
||||
struct dentry *rootdir;
|
||||
struct wcn36xx_dfs_file file_bmps_switcher;
|
||||
struct wcn36xx_dfs_file file_dump;
|
||||
};
|
||||
|
||||
void wcn36xx_debugfs_init(struct wcn36xx *wcn);
|
||||
void wcn36xx_debugfs_exit(struct wcn36xx *wcn);
|
||||
|
||||
#else
|
||||
static inline void wcn36xx_debugfs_init(struct wcn36xx *wcn)
|
||||
{
|
||||
}
|
||||
static inline void wcn36xx_debugfs_exit(struct wcn36xx *wcn)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_WCN36XX_DEBUGFS */
|
||||
|
||||
#endif /* _WCN36XX_DEBUG_H_ */
|
805
drivers/net/wireless/ath/wcn36xx/dxe.c
Normal file
805
drivers/net/wireless/ath/wcn36xx/dxe.c
Normal file
@ -0,0 +1,805 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* DXE - DMA transfer engine
|
||||
* we have 2 channels(High prio and Low prio) for TX and 2 channels for RX.
|
||||
* through low channels data packets are transfered
|
||||
* through high channels managment packets are transfered
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include "wcn36xx.h"
|
||||
#include "txrx.h"
|
||||
|
||||
void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low)
|
||||
{
|
||||
struct wcn36xx_dxe_ch *ch = is_low ?
|
||||
&wcn->dxe_tx_l_ch :
|
||||
&wcn->dxe_tx_h_ch;
|
||||
|
||||
return ch->head_blk_ctl->bd_cpu_addr;
|
||||
}
|
||||
|
||||
static void wcn36xx_dxe_write_register(struct wcn36xx *wcn, int addr, int data)
|
||||
{
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE,
|
||||
"wcn36xx_dxe_write_register: addr=%x, data=%x\n",
|
||||
addr, data);
|
||||
|
||||
writel(data, wcn->mmio + addr);
|
||||
}
|
||||
|
||||
static void wcn36xx_dxe_read_register(struct wcn36xx *wcn, int addr, int *data)
|
||||
{
|
||||
*data = readl(wcn->mmio + addr);
|
||||
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE,
|
||||
"wcn36xx_dxe_read_register: addr=%x, data=%x\n",
|
||||
addr, *data);
|
||||
}
|
||||
|
||||
static void wcn36xx_dxe_free_ctl_block(struct wcn36xx_dxe_ch *ch)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl, *next;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ch->desc_num && ctl; i++) {
|
||||
next = ctl->next;
|
||||
kfree(ctl);
|
||||
ctl = next;
|
||||
}
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *prev_ctl = NULL;
|
||||
struct wcn36xx_dxe_ctl *cur_ctl = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ch->desc_num; i++) {
|
||||
cur_ctl = kzalloc(sizeof(*cur_ctl), GFP_KERNEL);
|
||||
if (!cur_ctl)
|
||||
goto out_fail;
|
||||
|
||||
cur_ctl->ctl_blk_order = i;
|
||||
if (i == 0) {
|
||||
ch->head_blk_ctl = cur_ctl;
|
||||
ch->tail_blk_ctl = cur_ctl;
|
||||
} else if (ch->desc_num - 1 == i) {
|
||||
prev_ctl->next = cur_ctl;
|
||||
cur_ctl->next = ch->head_blk_ctl;
|
||||
} else {
|
||||
prev_ctl->next = cur_ctl;
|
||||
}
|
||||
prev_ctl = cur_ctl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_fail:
|
||||
wcn36xx_dxe_free_ctl_block(ch);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
wcn->dxe_tx_l_ch.ch_type = WCN36XX_DXE_CH_TX_L;
|
||||
wcn->dxe_tx_h_ch.ch_type = WCN36XX_DXE_CH_TX_H;
|
||||
wcn->dxe_rx_l_ch.ch_type = WCN36XX_DXE_CH_RX_L;
|
||||
wcn->dxe_rx_h_ch.ch_type = WCN36XX_DXE_CH_RX_H;
|
||||
|
||||
wcn->dxe_tx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_L;
|
||||
wcn->dxe_tx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_TX_H;
|
||||
wcn->dxe_rx_l_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_L;
|
||||
wcn->dxe_rx_h_ch.desc_num = WCN36XX_DXE_CH_DESC_NUMB_RX_H;
|
||||
|
||||
wcn->dxe_tx_l_ch.dxe_wq = WCN36XX_DXE_WQ_TX_L;
|
||||
wcn->dxe_tx_h_ch.dxe_wq = WCN36XX_DXE_WQ_TX_H;
|
||||
|
||||
wcn->dxe_tx_l_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_L_BD;
|
||||
wcn->dxe_tx_h_ch.ctrl_bd = WCN36XX_DXE_CTRL_TX_H_BD;
|
||||
|
||||
wcn->dxe_tx_l_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_L_SKB;
|
||||
wcn->dxe_tx_h_ch.ctrl_skb = WCN36XX_DXE_CTRL_TX_H_SKB;
|
||||
|
||||
wcn->dxe_tx_l_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_L;
|
||||
wcn->dxe_tx_h_ch.reg_ctrl = WCN36XX_DXE_REG_CTL_TX_H;
|
||||
|
||||
wcn->dxe_tx_l_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_L;
|
||||
wcn->dxe_tx_h_ch.def_ctrl = WCN36XX_DXE_CH_DEFAULT_CTL_TX_H;
|
||||
|
||||
/* DXE control block allocation */
|
||||
ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_l_ch);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_tx_h_ch);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_l_ch);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
ret = wcn36xx_dxe_allocate_ctl_block(&wcn->dxe_rx_h_ch);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
/* Initialize SMSM state Clear TX Enable RING EMPTY STATE */
|
||||
ret = wcn->ctrl_ops->smsm_change_state(
|
||||
WCN36XX_SMSM_WLAN_TX_ENABLE,
|
||||
WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
wcn36xx_err("Failed to allocate DXE control blocks\n");
|
||||
wcn36xx_dxe_free_ctl_blks(wcn);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn)
|
||||
{
|
||||
wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_l_ch);
|
||||
wcn36xx_dxe_free_ctl_block(&wcn->dxe_tx_h_ch);
|
||||
wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_l_ch);
|
||||
wcn36xx_dxe_free_ctl_block(&wcn->dxe_rx_h_ch);
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_init_descs(struct wcn36xx_dxe_ch *wcn_ch)
|
||||
{
|
||||
struct wcn36xx_dxe_desc *cur_dxe = NULL;
|
||||
struct wcn36xx_dxe_desc *prev_dxe = NULL;
|
||||
struct wcn36xx_dxe_ctl *cur_ctl = NULL;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
size = wcn_ch->desc_num * sizeof(struct wcn36xx_dxe_desc);
|
||||
wcn_ch->cpu_addr = dma_alloc_coherent(NULL, size, &wcn_ch->dma_addr,
|
||||
GFP_KERNEL);
|
||||
if (!wcn_ch->cpu_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(wcn_ch->cpu_addr, 0, size);
|
||||
|
||||
cur_dxe = (struct wcn36xx_dxe_desc *)wcn_ch->cpu_addr;
|
||||
cur_ctl = wcn_ch->head_blk_ctl;
|
||||
|
||||
for (i = 0; i < wcn_ch->desc_num; i++) {
|
||||
cur_ctl->desc = cur_dxe;
|
||||
cur_ctl->desc_phy_addr = wcn_ch->dma_addr +
|
||||
i * sizeof(struct wcn36xx_dxe_desc);
|
||||
|
||||
switch (wcn_ch->ch_type) {
|
||||
case WCN36XX_DXE_CH_TX_L:
|
||||
cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_L;
|
||||
cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_L;
|
||||
break;
|
||||
case WCN36XX_DXE_CH_TX_H:
|
||||
cur_dxe->ctrl = WCN36XX_DXE_CTRL_TX_H;
|
||||
cur_dxe->dst_addr_l = WCN36XX_DXE_WQ_TX_H;
|
||||
break;
|
||||
case WCN36XX_DXE_CH_RX_L:
|
||||
cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_L;
|
||||
cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_L;
|
||||
break;
|
||||
case WCN36XX_DXE_CH_RX_H:
|
||||
cur_dxe->ctrl = WCN36XX_DXE_CTRL_RX_H;
|
||||
cur_dxe->src_addr_l = WCN36XX_DXE_WQ_RX_H;
|
||||
break;
|
||||
}
|
||||
if (0 == i) {
|
||||
cur_dxe->phy_next_l = 0;
|
||||
} else if ((0 < i) && (i < wcn_ch->desc_num - 1)) {
|
||||
prev_dxe->phy_next_l =
|
||||
cur_ctl->desc_phy_addr;
|
||||
} else if (i == (wcn_ch->desc_num - 1)) {
|
||||
prev_dxe->phy_next_l =
|
||||
cur_ctl->desc_phy_addr;
|
||||
cur_dxe->phy_next_l =
|
||||
wcn_ch->head_blk_ctl->desc_phy_addr;
|
||||
}
|
||||
cur_ctl = cur_ctl->next;
|
||||
prev_dxe = cur_dxe;
|
||||
cur_dxe++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wcn36xx_dxe_init_tx_bd(struct wcn36xx_dxe_ch *ch,
|
||||
struct wcn36xx_dxe_mem_pool *pool)
|
||||
{
|
||||
int i, chunk_size = pool->chunk_size;
|
||||
dma_addr_t bd_phy_addr = pool->phy_addr;
|
||||
void *bd_cpu_addr = pool->virt_addr;
|
||||
struct wcn36xx_dxe_ctl *cur = ch->head_blk_ctl;
|
||||
|
||||
for (i = 0; i < ch->desc_num; i++) {
|
||||
/* Only every second dxe needs a bd pointer,
|
||||
the other will point to the skb data */
|
||||
if (!(i & 1)) {
|
||||
cur->bd_phy_addr = bd_phy_addr;
|
||||
cur->bd_cpu_addr = bd_cpu_addr;
|
||||
bd_phy_addr += chunk_size;
|
||||
bd_cpu_addr += chunk_size;
|
||||
} else {
|
||||
cur->bd_phy_addr = 0;
|
||||
cur->bd_cpu_addr = NULL;
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_enable_ch_int(struct wcn36xx *wcn, u16 wcn_ch)
|
||||
{
|
||||
int reg_data = 0;
|
||||
|
||||
wcn36xx_dxe_read_register(wcn,
|
||||
WCN36XX_DXE_INT_MASK_REG,
|
||||
®_data);
|
||||
|
||||
reg_data |= wcn_ch;
|
||||
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_INT_MASK_REG,
|
||||
(int)reg_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_fill_skb(struct wcn36xx_dxe_ctl *ctl)
|
||||
{
|
||||
struct wcn36xx_dxe_desc *dxe = ctl->desc;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(WCN36XX_PKT_SIZE, GFP_ATOMIC);
|
||||
if (skb == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dxe->dst_addr_l = dma_map_single(NULL,
|
||||
skb_tail_pointer(skb),
|
||||
WCN36XX_PKT_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
ctl->skb = skb;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_ch_alloc_skb(struct wcn36xx *wcn,
|
||||
struct wcn36xx_dxe_ch *wcn_ch)
|
||||
{
|
||||
int i;
|
||||
struct wcn36xx_dxe_ctl *cur_ctl = NULL;
|
||||
|
||||
cur_ctl = wcn_ch->head_blk_ctl;
|
||||
|
||||
for (i = 0; i < wcn_ch->desc_num; i++) {
|
||||
wcn36xx_dxe_fill_skb(cur_ctl);
|
||||
cur_ctl = cur_ctl->next;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wcn36xx_dxe_ch_free_skbs(struct wcn36xx *wcn,
|
||||
struct wcn36xx_dxe_ch *wcn_ch)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *cur = wcn_ch->head_blk_ctl;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < wcn_ch->desc_num; i++) {
|
||||
kfree_skb(cur->skb);
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status)
|
||||
{
|
||||
struct ieee80211_tx_info *info;
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wcn->dxe_lock, flags);
|
||||
skb = wcn->tx_ack_skb;
|
||||
wcn->tx_ack_skb = NULL;
|
||||
spin_unlock_irqrestore(&wcn->dxe_lock, flags);
|
||||
|
||||
if (!skb) {
|
||||
wcn36xx_warn("Spurious TX complete indication\n");
|
||||
return;
|
||||
}
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
if (status == 1)
|
||||
info->flags |= IEEE80211_TX_STAT_ACK;
|
||||
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ack status: %d\n", status);
|
||||
|
||||
ieee80211_tx_status_irqsafe(wcn->hw, skb);
|
||||
ieee80211_wake_queues(wcn->hw);
|
||||
}
|
||||
|
||||
static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *ctl = ch->tail_blk_ctl;
|
||||
struct ieee80211_tx_info *info;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Make at least one loop of do-while because in case ring is
|
||||
* completely full head and tail are pointing to the same element
|
||||
* and while-do will not make any cycles.
|
||||
*/
|
||||
do {
|
||||
if (ctl->skb) {
|
||||
dma_unmap_single(NULL, ctl->desc->src_addr_l,
|
||||
ctl->skb->len, DMA_TO_DEVICE);
|
||||
info = IEEE80211_SKB_CB(ctl->skb);
|
||||
if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) {
|
||||
/* Keep frame until TX status comes */
|
||||
ieee80211_free_txskb(wcn->hw, ctl->skb);
|
||||
}
|
||||
spin_lock_irqsave(&ctl->skb_lock, flags);
|
||||
if (wcn->queues_stopped) {
|
||||
wcn->queues_stopped = false;
|
||||
ieee80211_wake_queues(wcn->hw);
|
||||
}
|
||||
spin_unlock_irqrestore(&ctl->skb_lock, flags);
|
||||
|
||||
ctl->skb = NULL;
|
||||
}
|
||||
ctl = ctl->next;
|
||||
} while (ctl != ch->head_blk_ctl &&
|
||||
!(ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK));
|
||||
|
||||
ch->tail_blk_ctl = ctl;
|
||||
}
|
||||
|
||||
static irqreturn_t wcn36xx_irq_tx_complete(int irq, void *dev)
|
||||
{
|
||||
struct wcn36xx *wcn = (struct wcn36xx *)dev;
|
||||
int int_src, int_reason;
|
||||
|
||||
wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);
|
||||
|
||||
if (int_src & WCN36XX_INT_MASK_CHAN_TX_H) {
|
||||
wcn36xx_dxe_read_register(wcn,
|
||||
WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H,
|
||||
&int_reason);
|
||||
|
||||
/* TODO: Check int_reason */
|
||||
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_0_INT_CLR,
|
||||
WCN36XX_INT_MASK_CHAN_TX_H);
|
||||
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR,
|
||||
WCN36XX_INT_MASK_CHAN_TX_H);
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready high\n");
|
||||
reap_tx_dxes(wcn, &wcn->dxe_tx_h_ch);
|
||||
}
|
||||
|
||||
if (int_src & WCN36XX_INT_MASK_CHAN_TX_L) {
|
||||
wcn36xx_dxe_read_register(wcn,
|
||||
WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L,
|
||||
&int_reason);
|
||||
/* TODO: Check int_reason */
|
||||
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_0_INT_CLR,
|
||||
WCN36XX_INT_MASK_CHAN_TX_L);
|
||||
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_ED_CLR,
|
||||
WCN36XX_INT_MASK_CHAN_TX_L);
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE, "dxe tx ready low\n");
|
||||
reap_tx_dxes(wcn, &wcn->dxe_tx_l_ch);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t wcn36xx_irq_rx_ready(int irq, void *dev)
|
||||
{
|
||||
struct wcn36xx *wcn = (struct wcn36xx *)dev;
|
||||
|
||||
disable_irq_nosync(wcn->rx_irq);
|
||||
wcn36xx_dxe_rx_frame(wcn);
|
||||
enable_irq(wcn->rx_irq);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int wcn36xx_dxe_request_irqs(struct wcn36xx *wcn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = request_irq(wcn->tx_irq, wcn36xx_irq_tx_complete,
|
||||
IRQF_TRIGGER_HIGH, "wcn36xx_tx", wcn);
|
||||
if (ret) {
|
||||
wcn36xx_err("failed to alloc tx irq\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ret = request_irq(wcn->rx_irq, wcn36xx_irq_rx_ready, IRQF_TRIGGER_HIGH,
|
||||
"wcn36xx_rx", wcn);
|
||||
if (ret) {
|
||||
wcn36xx_err("failed to alloc rx irq\n");
|
||||
goto out_txirq;
|
||||
}
|
||||
|
||||
enable_irq_wake(wcn->rx_irq);
|
||||
|
||||
return 0;
|
||||
|
||||
out_txirq:
|
||||
free_irq(wcn->tx_irq, wcn);
|
||||
out_err:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int wcn36xx_rx_handle_packets(struct wcn36xx *wcn,
|
||||
struct wcn36xx_dxe_ch *ch)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *ctl = ch->head_blk_ctl;
|
||||
struct wcn36xx_dxe_desc *dxe = ctl->desc;
|
||||
dma_addr_t dma_addr;
|
||||
struct sk_buff *skb;
|
||||
|
||||
while (!(dxe->ctrl & WCN36XX_DXE_CTRL_VALID_MASK)) {
|
||||
skb = ctl->skb;
|
||||
dma_addr = dxe->dst_addr_l;
|
||||
wcn36xx_dxe_fill_skb(ctl);
|
||||
|
||||
switch (ch->ch_type) {
|
||||
case WCN36XX_DXE_CH_RX_L:
|
||||
dxe->ctrl = WCN36XX_DXE_CTRL_RX_L;
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
|
||||
WCN36XX_DXE_INT_CH1_MASK);
|
||||
break;
|
||||
case WCN36XX_DXE_CH_RX_H:
|
||||
dxe->ctrl = WCN36XX_DXE_CTRL_RX_H;
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_ENCH_ADDR,
|
||||
WCN36XX_DXE_INT_CH3_MASK);
|
||||
break;
|
||||
default:
|
||||
wcn36xx_warn("Unknown channel\n");
|
||||
}
|
||||
|
||||
dma_unmap_single(NULL, dma_addr, WCN36XX_PKT_SIZE,
|
||||
DMA_FROM_DEVICE);
|
||||
wcn36xx_rx_skb(wcn, skb);
|
||||
ctl = ctl->next;
|
||||
dxe = ctl->desc;
|
||||
}
|
||||
|
||||
ch->head_blk_ctl = ctl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn)
|
||||
{
|
||||
int int_src;
|
||||
|
||||
wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_INT_SRC_RAW_REG, &int_src);
|
||||
|
||||
/* RX_LOW_PRI */
|
||||
if (int_src & WCN36XX_DXE_INT_CH1_MASK) {
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
|
||||
WCN36XX_DXE_INT_CH1_MASK);
|
||||
wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_l_ch));
|
||||
}
|
||||
|
||||
/* RX_HIGH_PRI */
|
||||
if (int_src & WCN36XX_DXE_INT_CH3_MASK) {
|
||||
/* Clean up all the INT within this channel */
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_0_INT_CLR,
|
||||
WCN36XX_DXE_INT_CH3_MASK);
|
||||
wcn36xx_rx_handle_packets(wcn, &(wcn->dxe_rx_h_ch));
|
||||
}
|
||||
|
||||
if (!int_src)
|
||||
wcn36xx_warn("No DXE interrupt pending\n");
|
||||
}
|
||||
|
||||
int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn)
|
||||
{
|
||||
size_t s;
|
||||
void *cpu_addr;
|
||||
|
||||
/* Allocate BD headers for MGMT frames */
|
||||
|
||||
/* Where this come from ask QC */
|
||||
wcn->mgmt_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE +
|
||||
16 - (WCN36XX_BD_CHUNK_SIZE % 8);
|
||||
|
||||
s = wcn->mgmt_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_H;
|
||||
cpu_addr = dma_alloc_coherent(NULL, s, &wcn->mgmt_mem_pool.phy_addr,
|
||||
GFP_KERNEL);
|
||||
if (!cpu_addr)
|
||||
goto out_err;
|
||||
|
||||
wcn->mgmt_mem_pool.virt_addr = cpu_addr;
|
||||
memset(cpu_addr, 0, s);
|
||||
|
||||
/* Allocate BD headers for DATA frames */
|
||||
|
||||
/* Where this come from ask QC */
|
||||
wcn->data_mem_pool.chunk_size = WCN36XX_BD_CHUNK_SIZE +
|
||||
16 - (WCN36XX_BD_CHUNK_SIZE % 8);
|
||||
|
||||
s = wcn->data_mem_pool.chunk_size * WCN36XX_DXE_CH_DESC_NUMB_TX_L;
|
||||
cpu_addr = dma_alloc_coherent(NULL, s, &wcn->data_mem_pool.phy_addr,
|
||||
GFP_KERNEL);
|
||||
if (!cpu_addr)
|
||||
goto out_err;
|
||||
|
||||
wcn->data_mem_pool.virt_addr = cpu_addr;
|
||||
memset(cpu_addr, 0, s);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
wcn36xx_dxe_free_mem_pools(wcn);
|
||||
wcn36xx_err("Failed to allocate BD mempool\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn)
|
||||
{
|
||||
if (wcn->mgmt_mem_pool.virt_addr)
|
||||
dma_free_coherent(NULL, wcn->mgmt_mem_pool.chunk_size *
|
||||
WCN36XX_DXE_CH_DESC_NUMB_TX_H,
|
||||
wcn->mgmt_mem_pool.virt_addr,
|
||||
wcn->mgmt_mem_pool.phy_addr);
|
||||
|
||||
if (wcn->data_mem_pool.virt_addr) {
|
||||
dma_free_coherent(NULL, wcn->data_mem_pool.chunk_size *
|
||||
WCN36XX_DXE_CH_DESC_NUMB_TX_L,
|
||||
wcn->data_mem_pool.virt_addr,
|
||||
wcn->data_mem_pool.phy_addr);
|
||||
}
|
||||
}
|
||||
|
||||
int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
|
||||
struct wcn36xx_vif *vif_priv,
|
||||
struct sk_buff *skb,
|
||||
bool is_low)
|
||||
{
|
||||
struct wcn36xx_dxe_ctl *ctl = NULL;
|
||||
struct wcn36xx_dxe_desc *desc = NULL;
|
||||
struct wcn36xx_dxe_ch *ch = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
ch = is_low ? &wcn->dxe_tx_l_ch : &wcn->dxe_tx_h_ch;
|
||||
|
||||
ctl = ch->head_blk_ctl;
|
||||
|
||||
spin_lock_irqsave(&ctl->next->skb_lock, flags);
|
||||
|
||||
/*
|
||||
* If skb is not null that means that we reached the tail of the ring
|
||||
* hence ring is full. Stop queues to let mac80211 back off until ring
|
||||
* has an empty slot again.
|
||||
*/
|
||||
if (NULL != ctl->next->skb) {
|
||||
ieee80211_stop_queues(wcn->hw);
|
||||
wcn->queues_stopped = true;
|
||||
spin_unlock_irqrestore(&ctl->next->skb_lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
spin_unlock_irqrestore(&ctl->next->skb_lock, flags);
|
||||
|
||||
ctl->skb = NULL;
|
||||
desc = ctl->desc;
|
||||
|
||||
/* Set source address of the BD we send */
|
||||
desc->src_addr_l = ctl->bd_phy_addr;
|
||||
|
||||
desc->dst_addr_l = ch->dxe_wq;
|
||||
desc->fr_len = sizeof(struct wcn36xx_tx_bd);
|
||||
desc->ctrl = ch->ctrl_bd;
|
||||
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE, "DXE TX\n");
|
||||
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC1 >>> ",
|
||||
(char *)desc, sizeof(*desc));
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP,
|
||||
"BD >>> ", (char *)ctl->bd_cpu_addr,
|
||||
sizeof(struct wcn36xx_tx_bd));
|
||||
|
||||
/* Set source address of the SKB we send */
|
||||
ctl = ctl->next;
|
||||
ctl->skb = skb;
|
||||
desc = ctl->desc;
|
||||
if (ctl->bd_cpu_addr) {
|
||||
wcn36xx_err("bd_cpu_addr cannot be NULL for skb DXE\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
desc->src_addr_l = dma_map_single(NULL,
|
||||
ctl->skb->data,
|
||||
ctl->skb->len,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
desc->dst_addr_l = ch->dxe_wq;
|
||||
desc->fr_len = ctl->skb->len;
|
||||
|
||||
/* set dxe descriptor to VALID */
|
||||
desc->ctrl = ch->ctrl_skb;
|
||||
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "DESC2 >>> ",
|
||||
(char *)desc, sizeof(*desc));
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_DXE_DUMP, "SKB >>> ",
|
||||
(char *)ctl->skb->data, ctl->skb->len);
|
||||
|
||||
/* Move the head of the ring to the next empty descriptor */
|
||||
ch->head_blk_ctl = ctl->next;
|
||||
|
||||
/*
|
||||
* When connected and trying to send data frame chip can be in sleep
|
||||
* mode and writing to the register will not wake up the chip. Instead
|
||||
* notify chip about new frame through SMSM bus.
|
||||
*/
|
||||
if (is_low && vif_priv->pw_state == WCN36XX_BMPS) {
|
||||
wcn->ctrl_ops->smsm_change_state(
|
||||
0,
|
||||
WCN36XX_SMSM_WLAN_TX_ENABLE);
|
||||
} else {
|
||||
/* indicate End Of Packet and generate interrupt on descriptor
|
||||
* done.
|
||||
*/
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
ch->reg_ctrl, ch->def_ctrl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wcn36xx_dxe_init(struct wcn36xx *wcn)
|
||||
{
|
||||
int reg_data = 0, ret;
|
||||
|
||||
reg_data = WCN36XX_DXE_REG_RESET;
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CSR_RESET, reg_data);
|
||||
|
||||
/* Setting interrupt path */
|
||||
reg_data = WCN36XX_DXE_CCU_INT;
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_REG_CCU_INT, reg_data);
|
||||
|
||||
/***************************************/
|
||||
/* Init descriptors for TX LOW channel */
|
||||
/***************************************/
|
||||
wcn36xx_dxe_init_descs(&wcn->dxe_tx_l_ch);
|
||||
wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_l_ch, &wcn->data_mem_pool);
|
||||
|
||||
/* Write channel head to a NEXT register */
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L,
|
||||
wcn->dxe_tx_l_ch.head_blk_ctl->desc_phy_addr);
|
||||
|
||||
/* Program DMA destination addr for TX LOW */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_DEST_ADDR_TX_L,
|
||||
WCN36XX_DXE_WQ_TX_L);
|
||||
|
||||
wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data);
|
||||
wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_L);
|
||||
|
||||
/***************************************/
|
||||
/* Init descriptors for TX HIGH channel */
|
||||
/***************************************/
|
||||
wcn36xx_dxe_init_descs(&wcn->dxe_tx_h_ch);
|
||||
wcn36xx_dxe_init_tx_bd(&wcn->dxe_tx_h_ch, &wcn->mgmt_mem_pool);
|
||||
|
||||
/* Write channel head to a NEXT register */
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H,
|
||||
wcn->dxe_tx_h_ch.head_blk_ctl->desc_phy_addr);
|
||||
|
||||
/* Program DMA destination addr for TX HIGH */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_DEST_ADDR_TX_H,
|
||||
WCN36XX_DXE_WQ_TX_H);
|
||||
|
||||
wcn36xx_dxe_read_register(wcn, WCN36XX_DXE_REG_CH_EN, ®_data);
|
||||
|
||||
/* Enable channel interrupts */
|
||||
wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_TX_H);
|
||||
|
||||
/***************************************/
|
||||
/* Init descriptors for RX LOW channel */
|
||||
/***************************************/
|
||||
wcn36xx_dxe_init_descs(&wcn->dxe_rx_l_ch);
|
||||
|
||||
/* For RX we need to preallocated buffers */
|
||||
wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_l_ch);
|
||||
|
||||
/* Write channel head to a NEXT register */
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L,
|
||||
wcn->dxe_rx_l_ch.head_blk_ctl->desc_phy_addr);
|
||||
|
||||
/* Write DMA source address */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_SRC_ADDR_RX_L,
|
||||
WCN36XX_DXE_WQ_RX_L);
|
||||
|
||||
/* Program preallocated destination address */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_DEST_ADDR_RX_L,
|
||||
wcn->dxe_rx_l_ch.head_blk_ctl->desc->phy_next_l);
|
||||
|
||||
/* Enable default control registers */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_REG_CTL_RX_L,
|
||||
WCN36XX_DXE_CH_DEFAULT_CTL_RX_L);
|
||||
|
||||
/* Enable channel interrupts */
|
||||
wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_L);
|
||||
|
||||
/***************************************/
|
||||
/* Init descriptors for RX HIGH channel */
|
||||
/***************************************/
|
||||
wcn36xx_dxe_init_descs(&wcn->dxe_rx_h_ch);
|
||||
|
||||
/* For RX we need to prealocat buffers */
|
||||
wcn36xx_dxe_ch_alloc_skb(wcn, &wcn->dxe_rx_h_ch);
|
||||
|
||||
/* Write chanel head to a NEXT register */
|
||||
wcn36xx_dxe_write_register(wcn, WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H,
|
||||
wcn->dxe_rx_h_ch.head_blk_ctl->desc_phy_addr);
|
||||
|
||||
/* Write DMA source address */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_SRC_ADDR_RX_H,
|
||||
WCN36XX_DXE_WQ_RX_H);
|
||||
|
||||
/* Program preallocated destination address */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_CH_DEST_ADDR_RX_H,
|
||||
wcn->dxe_rx_h_ch.head_blk_ctl->desc->phy_next_l);
|
||||
|
||||
/* Enable default control registers */
|
||||
wcn36xx_dxe_write_register(wcn,
|
||||
WCN36XX_DXE_REG_CTL_RX_H,
|
||||
WCN36XX_DXE_CH_DEFAULT_CTL_RX_H);
|
||||
|
||||
/* Enable channel interrupts */
|
||||
wcn36xx_dxe_enable_ch_int(wcn, WCN36XX_INT_MASK_CHAN_RX_H);
|
||||
|
||||
ret = wcn36xx_dxe_request_irqs(wcn);
|
||||
if (ret < 0)
|
||||
goto out_err;
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void wcn36xx_dxe_deinit(struct wcn36xx *wcn)
|
||||
{
|
||||
free_irq(wcn->tx_irq, wcn);
|
||||
free_irq(wcn->rx_irq, wcn);
|
||||
|
||||
if (wcn->tx_ack_skb) {
|
||||
ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb);
|
||||
wcn->tx_ack_skb = NULL;
|
||||
}
|
||||
|
||||
wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_l_ch);
|
||||
wcn36xx_dxe_ch_free_skbs(wcn, &wcn->dxe_rx_h_ch);
|
||||
}
|
284
drivers/net/wireless/ath/wcn36xx/dxe.h
Normal file
284
drivers/net/wireless/ath/wcn36xx/dxe.h
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
*
|
||||
* 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 _DXE_H_
|
||||
#define _DXE_H_
|
||||
|
||||
#include "wcn36xx.h"
|
||||
|
||||
/*
|
||||
TX_LOW = DMA0
|
||||
TX_HIGH = DMA4
|
||||
RX_LOW = DMA1
|
||||
RX_HIGH = DMA3
|
||||
H2H_TEST_RX_TX = DMA2
|
||||
*/
|
||||
|
||||
/* DXE registers */
|
||||
#define WCN36XX_DXE_MEM_BASE 0x03000000
|
||||
#define WCN36XX_DXE_MEM_REG 0x202000
|
||||
|
||||
#define WCN36XX_DXE_CCU_INT 0xA0011
|
||||
#define WCN36XX_DXE_REG_CCU_INT 0x200b10
|
||||
|
||||
/* TODO This must calculated properly but not hardcoded */
|
||||
#define WCN36XX_DXE_CTRL_TX_L 0x328a44
|
||||
#define WCN36XX_DXE_CTRL_TX_H 0x32ce44
|
||||
#define WCN36XX_DXE_CTRL_RX_L 0x12ad2f
|
||||
#define WCN36XX_DXE_CTRL_RX_H 0x12d12f
|
||||
#define WCN36XX_DXE_CTRL_TX_H_BD 0x30ce45
|
||||
#define WCN36XX_DXE_CTRL_TX_H_SKB 0x32ce4d
|
||||
#define WCN36XX_DXE_CTRL_TX_L_BD 0x308a45
|
||||
#define WCN36XX_DXE_CTRL_TX_L_SKB 0x328a4d
|
||||
|
||||
/* TODO This must calculated properly but not hardcoded */
|
||||
#define WCN36XX_DXE_WQ_TX_L 0x17
|
||||
#define WCN36XX_DXE_WQ_TX_H 0x17
|
||||
#define WCN36XX_DXE_WQ_RX_L 0xB
|
||||
#define WCN36XX_DXE_WQ_RX_H 0x4
|
||||
|
||||
/* DXE descriptor control filed */
|
||||
#define WCN36XX_DXE_CTRL_VALID_MASK (0x00000001)
|
||||
|
||||
/* TODO This must calculated properly but not hardcoded */
|
||||
/* DXE default control register values */
|
||||
#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_L 0x847EAD2F
|
||||
#define WCN36XX_DXE_CH_DEFAULT_CTL_RX_H 0x84FED12F
|
||||
#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_H 0x853ECF4D
|
||||
#define WCN36XX_DXE_CH_DEFAULT_CTL_TX_L 0x843e8b4d
|
||||
|
||||
/* Common DXE registers */
|
||||
#define WCN36XX_DXE_MEM_CSR (WCN36XX_DXE_MEM_REG + 0x00)
|
||||
#define WCN36XX_DXE_REG_CSR_RESET (WCN36XX_DXE_MEM_REG + 0x00)
|
||||
#define WCN36XX_DXE_ENCH_ADDR (WCN36XX_DXE_MEM_REG + 0x04)
|
||||
#define WCN36XX_DXE_REG_CH_EN (WCN36XX_DXE_MEM_REG + 0x08)
|
||||
#define WCN36XX_DXE_REG_CH_DONE (WCN36XX_DXE_MEM_REG + 0x0C)
|
||||
#define WCN36XX_DXE_REG_CH_ERR (WCN36XX_DXE_MEM_REG + 0x10)
|
||||
#define WCN36XX_DXE_INT_MASK_REG (WCN36XX_DXE_MEM_REG + 0x18)
|
||||
#define WCN36XX_DXE_INT_SRC_RAW_REG (WCN36XX_DXE_MEM_REG + 0x20)
|
||||
/* #define WCN36XX_DXE_INT_CH6_MASK 0x00000040 */
|
||||
/* #define WCN36XX_DXE_INT_CH5_MASK 0x00000020 */
|
||||
#define WCN36XX_DXE_INT_CH4_MASK 0x00000010
|
||||
#define WCN36XX_DXE_INT_CH3_MASK 0x00000008
|
||||
/* #define WCN36XX_DXE_INT_CH2_MASK 0x00000004 */
|
||||
#define WCN36XX_DXE_INT_CH1_MASK 0x00000002
|
||||
#define WCN36XX_DXE_INT_CH0_MASK 0x00000001
|
||||
#define WCN36XX_DXE_0_INT_CLR (WCN36XX_DXE_MEM_REG + 0x30)
|
||||
#define WCN36XX_DXE_0_INT_ED_CLR (WCN36XX_DXE_MEM_REG + 0x34)
|
||||
#define WCN36XX_DXE_0_INT_DONE_CLR (WCN36XX_DXE_MEM_REG + 0x38)
|
||||
#define WCN36XX_DXE_0_INT_ERR_CLR (WCN36XX_DXE_MEM_REG + 0x3C)
|
||||
|
||||
#define WCN36XX_DXE_0_CH0_STATUS (WCN36XX_DXE_MEM_REG + 0x404)
|
||||
#define WCN36XX_DXE_0_CH1_STATUS (WCN36XX_DXE_MEM_REG + 0x444)
|
||||
#define WCN36XX_DXE_0_CH2_STATUS (WCN36XX_DXE_MEM_REG + 0x484)
|
||||
#define WCN36XX_DXE_0_CH3_STATUS (WCN36XX_DXE_MEM_REG + 0x4C4)
|
||||
#define WCN36XX_DXE_0_CH4_STATUS (WCN36XX_DXE_MEM_REG + 0x504)
|
||||
|
||||
#define WCN36XX_DXE_REG_RESET 0x5c89
|
||||
|
||||
/* Temporary BMU Workqueue 4 */
|
||||
#define WCN36XX_DXE_BMU_WQ_RX_LOW 0xB
|
||||
#define WCN36XX_DXE_BMU_WQ_RX_HIGH 0x4
|
||||
/* DMA channel offset */
|
||||
#define WCN36XX_DXE_TX_LOW_OFFSET 0x400
|
||||
#define WCN36XX_DXE_TX_HIGH_OFFSET 0x500
|
||||
#define WCN36XX_DXE_RX_LOW_OFFSET 0x440
|
||||
#define WCN36XX_DXE_RX_HIGH_OFFSET 0x4C0
|
||||
|
||||
/* Address of the next DXE descriptor */
|
||||
#define WCN36XX_DXE_CH_NEXT_DESC_ADDR 0x001C
|
||||
#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_TX_LOW_OFFSET + \
|
||||
WCN36XX_DXE_CH_NEXT_DESC_ADDR)
|
||||
#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_TX_HIGH_OFFSET + \
|
||||
WCN36XX_DXE_CH_NEXT_DESC_ADDR)
|
||||
#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_RX_LOW_OFFSET + \
|
||||
WCN36XX_DXE_CH_NEXT_DESC_ADDR)
|
||||
#define WCN36XX_DXE_CH_NEXT_DESC_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_RX_HIGH_OFFSET + \
|
||||
WCN36XX_DXE_CH_NEXT_DESC_ADDR)
|
||||
|
||||
/* DXE Descriptor source address */
|
||||
#define WCN36XX_DXE_CH_SRC_ADDR 0x000C
|
||||
#define WCN36XX_DXE_CH_SRC_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_RX_LOW_OFFSET + \
|
||||
WCN36XX_DXE_CH_SRC_ADDR)
|
||||
#define WCN36XX_DXE_CH_SRC_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_RX_HIGH_OFFSET + \
|
||||
WCN36XX_DXE_CH_SRC_ADDR)
|
||||
|
||||
/* DXE Descriptor address destination address */
|
||||
#define WCN36XX_DXE_CH_DEST_ADDR 0x0014
|
||||
#define WCN36XX_DXE_CH_DEST_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_TX_LOW_OFFSET + \
|
||||
WCN36XX_DXE_CH_DEST_ADDR)
|
||||
#define WCN36XX_DXE_CH_DEST_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_TX_HIGH_OFFSET + \
|
||||
WCN36XX_DXE_CH_DEST_ADDR)
|
||||
#define WCN36XX_DXE_CH_DEST_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_RX_LOW_OFFSET + \
|
||||
WCN36XX_DXE_CH_DEST_ADDR)
|
||||
#define WCN36XX_DXE_CH_DEST_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_RX_HIGH_OFFSET + \
|
||||
WCN36XX_DXE_CH_DEST_ADDR)
|
||||
|
||||
/* Interrupt status */
|
||||
#define WCN36XX_DXE_CH_STATUS_REG_ADDR 0x0004
|
||||
#define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_L (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_TX_LOW_OFFSET + \
|
||||
WCN36XX_DXE_CH_STATUS_REG_ADDR)
|
||||
#define WCN36XX_DXE_CH_STATUS_REG_ADDR_TX_H (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_TX_HIGH_OFFSET + \
|
||||
WCN36XX_DXE_CH_STATUS_REG_ADDR)
|
||||
#define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_L (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_RX_LOW_OFFSET + \
|
||||
WCN36XX_DXE_CH_STATUS_REG_ADDR)
|
||||
#define WCN36XX_DXE_CH_STATUS_REG_ADDR_RX_H (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_RX_HIGH_OFFSET + \
|
||||
WCN36XX_DXE_CH_STATUS_REG_ADDR)
|
||||
|
||||
|
||||
/* DXE default control register */
|
||||
#define WCN36XX_DXE_REG_CTL_RX_L (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_RX_LOW_OFFSET)
|
||||
#define WCN36XX_DXE_REG_CTL_RX_H (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_RX_HIGH_OFFSET)
|
||||
#define WCN36XX_DXE_REG_CTL_TX_H (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_TX_HIGH_OFFSET)
|
||||
#define WCN36XX_DXE_REG_CTL_TX_L (WCN36XX_DXE_MEM_REG + \
|
||||
WCN36XX_DXE_TX_LOW_OFFSET)
|
||||
|
||||
#define WCN36XX_SMSM_WLAN_TX_ENABLE 0x00000400
|
||||
#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY 0x00000200
|
||||
|
||||
|
||||
/* Interrupt control channel mask */
|
||||
#define WCN36XX_INT_MASK_CHAN_TX_L 0x00000001
|
||||
#define WCN36XX_INT_MASK_CHAN_RX_L 0x00000002
|
||||
#define WCN36XX_INT_MASK_CHAN_RX_H 0x00000008
|
||||
#define WCN36XX_INT_MASK_CHAN_TX_H 0x00000010
|
||||
|
||||
#define WCN36XX_BD_CHUNK_SIZE 128
|
||||
|
||||
#define WCN36XX_PKT_SIZE 0xF20
|
||||
enum wcn36xx_dxe_ch_type {
|
||||
WCN36XX_DXE_CH_TX_L,
|
||||
WCN36XX_DXE_CH_TX_H,
|
||||
WCN36XX_DXE_CH_RX_L,
|
||||
WCN36XX_DXE_CH_RX_H
|
||||
};
|
||||
|
||||
/* amount of descriptors per channel */
|
||||
enum wcn36xx_dxe_ch_desc_num {
|
||||
WCN36XX_DXE_CH_DESC_NUMB_TX_L = 128,
|
||||
WCN36XX_DXE_CH_DESC_NUMB_TX_H = 10,
|
||||
WCN36XX_DXE_CH_DESC_NUMB_RX_L = 512,
|
||||
WCN36XX_DXE_CH_DESC_NUMB_RX_H = 40
|
||||
};
|
||||
|
||||
/**
|
||||
* struct wcn36xx_dxe_desc - describes descriptor of one DXE buffer
|
||||
*
|
||||
* @ctrl: is a union that consists of following bits:
|
||||
* union {
|
||||
* u32 valid :1; //0 = DMA stop, 1 = DMA continue with this
|
||||
* //descriptor
|
||||
* u32 transfer_type :2; //0 = Host to Host space
|
||||
* u32 eop :1; //End of Packet
|
||||
* u32 bd_handling :1; //if transferType = Host to BMU, then 0
|
||||
* // means first 128 bytes contain BD, and 1
|
||||
* // means create new empty BD
|
||||
* u32 siq :1; // SIQ
|
||||
* u32 diq :1; // DIQ
|
||||
* u32 pdu_rel :1; //0 = don't release BD and PDUs when done,
|
||||
* // 1 = release them
|
||||
* u32 bthld_sel :4; //BMU Threshold Select
|
||||
* u32 prio :3; //Specifies the priority level to use for
|
||||
* // the transfer
|
||||
* u32 stop_channel :1; //1 = DMA stops processing further, channel
|
||||
* //requires re-enabling after this
|
||||
* u32 intr :1; //Interrupt on Descriptor Done
|
||||
* u32 rsvd :1; //reserved
|
||||
* u32 size :14;//14 bits used - ignored for BMU transfers,
|
||||
* //only used for host to host transfers?
|
||||
* } ctrl;
|
||||
*/
|
||||
struct wcn36xx_dxe_desc {
|
||||
u32 ctrl;
|
||||
u32 fr_len;
|
||||
|
||||
u32 src_addr_l;
|
||||
u32 dst_addr_l;
|
||||
u32 phy_next_l;
|
||||
u32 src_addr_h;
|
||||
u32 dst_addr_h;
|
||||
u32 phy_next_h;
|
||||
} __packed;
|
||||
|
||||
/* DXE Control block */
|
||||
struct wcn36xx_dxe_ctl {
|
||||
struct wcn36xx_dxe_ctl *next;
|
||||
struct wcn36xx_dxe_desc *desc;
|
||||
unsigned int desc_phy_addr;
|
||||
int ctl_blk_order;
|
||||
struct sk_buff *skb;
|
||||
spinlock_t skb_lock;
|
||||
void *bd_cpu_addr;
|
||||
dma_addr_t bd_phy_addr;
|
||||
};
|
||||
|
||||
struct wcn36xx_dxe_ch {
|
||||
enum wcn36xx_dxe_ch_type ch_type;
|
||||
void *cpu_addr;
|
||||
dma_addr_t dma_addr;
|
||||
enum wcn36xx_dxe_ch_desc_num desc_num;
|
||||
/* DXE control block ring */
|
||||
struct wcn36xx_dxe_ctl *head_blk_ctl;
|
||||
struct wcn36xx_dxe_ctl *tail_blk_ctl;
|
||||
|
||||
/* DXE channel specific configs */
|
||||
u32 dxe_wq;
|
||||
u32 ctrl_bd;
|
||||
u32 ctrl_skb;
|
||||
u32 reg_ctrl;
|
||||
u32 def_ctrl;
|
||||
};
|
||||
|
||||
/* Memory Pool for BD headers */
|
||||
struct wcn36xx_dxe_mem_pool {
|
||||
int chunk_size;
|
||||
void *virt_addr;
|
||||
dma_addr_t phy_addr;
|
||||
};
|
||||
|
||||
struct wcn36xx_vif;
|
||||
int wcn36xx_dxe_allocate_mem_pools(struct wcn36xx *wcn);
|
||||
void wcn36xx_dxe_free_mem_pools(struct wcn36xx *wcn);
|
||||
void wcn36xx_dxe_rx_frame(struct wcn36xx *wcn);
|
||||
int wcn36xx_dxe_alloc_ctl_blks(struct wcn36xx *wcn);
|
||||
void wcn36xx_dxe_free_ctl_blks(struct wcn36xx *wcn);
|
||||
int wcn36xx_dxe_init(struct wcn36xx *wcn);
|
||||
void wcn36xx_dxe_deinit(struct wcn36xx *wcn);
|
||||
int wcn36xx_dxe_init_channels(struct wcn36xx *wcn);
|
||||
int wcn36xx_dxe_tx_frame(struct wcn36xx *wcn,
|
||||
struct wcn36xx_vif *vif_priv,
|
||||
struct sk_buff *skb,
|
||||
bool is_low);
|
||||
void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status);
|
||||
void *wcn36xx_dxe_get_next_bd(struct wcn36xx *wcn, bool is_low);
|
||||
#endif /* _DXE_H_ */
|
4657
drivers/net/wireless/ath/wcn36xx/hal.h
Normal file
4657
drivers/net/wireless/ath/wcn36xx/hal.h
Normal file
File diff suppressed because it is too large
Load Diff
1036
drivers/net/wireless/ath/wcn36xx/main.c
Normal file
1036
drivers/net/wireless/ath/wcn36xx/main.c
Normal file
File diff suppressed because it is too large
Load Diff
62
drivers/net/wireless/ath/wcn36xx/pmc.c
Normal file
62
drivers/net/wireless/ath/wcn36xx/pmc.c
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include "wcn36xx.h"
|
||||
|
||||
int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
int ret = 0;
|
||||
struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
|
||||
/* TODO: Make sure the TX chain clean */
|
||||
ret = wcn36xx_smd_enter_bmps(wcn, vif);
|
||||
if (!ret) {
|
||||
wcn36xx_dbg(WCN36XX_DBG_PMC, "Entered BMPS\n");
|
||||
vif_priv->pw_state = WCN36XX_BMPS;
|
||||
} else {
|
||||
/*
|
||||
* One of the reasons why HW will not enter BMPS is because
|
||||
* driver is trying to enter bmps before first beacon was
|
||||
* received just after auth complete
|
||||
*/
|
||||
wcn36xx_err("Can not enter BMPS!\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct wcn36xx_vif *vif_priv = (struct wcn36xx_vif *)vif->drv_priv;
|
||||
|
||||
if (WCN36XX_BMPS != vif_priv->pw_state) {
|
||||
wcn36xx_err("Not in BMPS mode, no need to exit from BMPS mode!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
wcn36xx_smd_exit_bmps(wcn, vif);
|
||||
vif_priv->pw_state = WCN36XX_FULL_POWER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
wcn36xx_dbg(WCN36XX_DBG_PMC, "%s\n", __func__);
|
||||
return wcn36xx_smd_keep_alive_req(wcn, vif,
|
||||
WCN36XX_HAL_KEEP_ALIVE_NULL_PKT);
|
||||
}
|
33
drivers/net/wireless/ath/wcn36xx/pmc.h
Normal file
33
drivers/net/wireless/ath/wcn36xx/pmc.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
*
|
||||
* 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 _WCN36XX_PMC_H_
|
||||
#define _WCN36XX_PMC_H_
|
||||
|
||||
struct wcn36xx;
|
||||
|
||||
enum wcn36xx_power_state {
|
||||
WCN36XX_FULL_POWER,
|
||||
WCN36XX_BMPS
|
||||
};
|
||||
|
||||
int wcn36xx_pmc_enter_bmps_state(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif);
|
||||
int wcn36xx_pmc_exit_bmps_state(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif);
|
||||
int wcn36xx_enable_keep_alive_null_packet(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif);
|
||||
#endif /* _WCN36XX_PMC_H_ */
|
2126
drivers/net/wireless/ath/wcn36xx/smd.c
Normal file
2126
drivers/net/wireless/ath/wcn36xx/smd.c
Normal file
File diff suppressed because it is too large
Load Diff
127
drivers/net/wireless/ath/wcn36xx/smd.h
Normal file
127
drivers/net/wireless/ath/wcn36xx/smd.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
*
|
||||
* 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 _SMD_H_
|
||||
#define _SMD_H_
|
||||
|
||||
#include "wcn36xx.h"
|
||||
|
||||
/* Max shared size is 4k but we take less.*/
|
||||
#define WCN36XX_NV_FRAGMENT_SIZE 3072
|
||||
|
||||
#define WCN36XX_HAL_BUF_SIZE 4096
|
||||
|
||||
#define HAL_MSG_TIMEOUT 200
|
||||
#define WCN36XX_SMSM_WLAN_TX_ENABLE 0x00000400
|
||||
#define WCN36XX_SMSM_WLAN_TX_RINGS_EMPTY 0x00000200
|
||||
/* The PNO version info be contained in the rsp msg */
|
||||
#define WCN36XX_FW_MSG_PNO_VERSION_MASK 0x8000
|
||||
|
||||
enum wcn36xx_fw_msg_result {
|
||||
WCN36XX_FW_MSG_RESULT_SUCCESS = 0,
|
||||
WCN36XX_FW_MSG_RESULT_SUCCESS_SYNC = 1,
|
||||
|
||||
WCN36XX_FW_MSG_RESULT_MEM_FAIL = 5,
|
||||
};
|
||||
|
||||
/******************************/
|
||||
/* SMD requests and responses */
|
||||
/******************************/
|
||||
struct wcn36xx_fw_msg_status_rsp {
|
||||
u32 status;
|
||||
} __packed;
|
||||
|
||||
struct wcn36xx_hal_ind_msg {
|
||||
struct list_head list;
|
||||
u8 *msg;
|
||||
size_t msg_len;
|
||||
};
|
||||
|
||||
struct wcn36xx;
|
||||
|
||||
int wcn36xx_smd_open(struct wcn36xx *wcn);
|
||||
void wcn36xx_smd_close(struct wcn36xx *wcn);
|
||||
|
||||
int wcn36xx_smd_load_nv(struct wcn36xx *wcn);
|
||||
int wcn36xx_smd_start(struct wcn36xx *wcn);
|
||||
int wcn36xx_smd_stop(struct wcn36xx *wcn);
|
||||
int wcn36xx_smd_init_scan(struct wcn36xx *wcn, enum wcn36xx_hal_sys_mode mode);
|
||||
int wcn36xx_smd_start_scan(struct wcn36xx *wcn);
|
||||
int wcn36xx_smd_end_scan(struct wcn36xx *wcn);
|
||||
int wcn36xx_smd_finish_scan(struct wcn36xx *wcn,
|
||||
enum wcn36xx_hal_sys_mode mode);
|
||||
int wcn36xx_smd_update_scan_params(struct wcn36xx *wcn);
|
||||
int wcn36xx_smd_add_sta_self(struct wcn36xx *wcn, struct ieee80211_vif *vif);
|
||||
int wcn36xx_smd_delete_sta_self(struct wcn36xx *wcn, u8 *addr);
|
||||
int wcn36xx_smd_delete_sta(struct wcn36xx *wcn, u8 sta_index);
|
||||
int wcn36xx_smd_join(struct wcn36xx *wcn, const u8 *bssid, u8 *vif, u8 ch);
|
||||
int wcn36xx_smd_set_link_st(struct wcn36xx *wcn, const u8 *bssid,
|
||||
const u8 *sta_mac,
|
||||
enum wcn36xx_hal_link_state state);
|
||||
int wcn36xx_smd_config_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta, const u8 *bssid,
|
||||
bool update);
|
||||
int wcn36xx_smd_delete_bss(struct wcn36xx *wcn, struct ieee80211_vif *vif);
|
||||
int wcn36xx_smd_config_sta(struct wcn36xx *wcn, struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta);
|
||||
int wcn36xx_smd_send_beacon(struct wcn36xx *wcn, struct ieee80211_vif *vif,
|
||||
struct sk_buff *skb_beacon, u16 tim_off,
|
||||
u16 p2p_off);
|
||||
int wcn36xx_smd_switch_channel(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif, int ch);
|
||||
int wcn36xx_smd_update_proberesp_tmpl(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif,
|
||||
struct sk_buff *skb);
|
||||
int wcn36xx_smd_set_stakey(struct wcn36xx *wcn,
|
||||
enum ani_ed_type enc_type,
|
||||
u8 keyidx,
|
||||
u8 keylen,
|
||||
u8 *key,
|
||||
u8 sta_index);
|
||||
int wcn36xx_smd_set_bsskey(struct wcn36xx *wcn,
|
||||
enum ani_ed_type enc_type,
|
||||
u8 keyidx,
|
||||
u8 keylen,
|
||||
u8 *key);
|
||||
int wcn36xx_smd_remove_stakey(struct wcn36xx *wcn,
|
||||
enum ani_ed_type enc_type,
|
||||
u8 keyidx,
|
||||
u8 sta_index);
|
||||
int wcn36xx_smd_remove_bsskey(struct wcn36xx *wcn,
|
||||
enum ani_ed_type enc_type,
|
||||
u8 keyidx);
|
||||
int wcn36xx_smd_enter_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif);
|
||||
int wcn36xx_smd_exit_bmps(struct wcn36xx *wcn, struct ieee80211_vif *vif);
|
||||
int wcn36xx_smd_set_power_params(struct wcn36xx *wcn, bool ignore_dtim);
|
||||
int wcn36xx_smd_keep_alive_req(struct wcn36xx *wcn,
|
||||
struct ieee80211_vif *vif,
|
||||
int packet_type);
|
||||
int wcn36xx_smd_dump_cmd_req(struct wcn36xx *wcn, u32 arg1, u32 arg2,
|
||||
u32 arg3, u32 arg4, u32 arg5);
|
||||
int wcn36xx_smd_feature_caps_exchange(struct wcn36xx *wcn);
|
||||
|
||||
int wcn36xx_smd_add_ba_session(struct wcn36xx *wcn,
|
||||
struct ieee80211_sta *sta,
|
||||
u16 tid,
|
||||
u16 *ssn,
|
||||
u8 direction,
|
||||
u8 sta_index);
|
||||
int wcn36xx_smd_add_ba(struct wcn36xx *wcn);
|
||||
int wcn36xx_smd_del_ba(struct wcn36xx *wcn, u16 tid, u8 sta_index);
|
||||
int wcn36xx_smd_trigger_ba(struct wcn36xx *wcn, u8 sta_index);
|
||||
|
||||
int wcn36xx_smd_update_cfg(struct wcn36xx *wcn, u32 cfg_id, u32 value);
|
||||
#endif /* _SMD_H_ */
|
284
drivers/net/wireless/ath/wcn36xx/txrx.c
Normal file
284
drivers/net/wireless/ath/wcn36xx/txrx.c
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include "txrx.h"
|
||||
|
||||
static inline int get_rssi0(struct wcn36xx_rx_bd *bd)
|
||||
{
|
||||
return 100 - ((bd->phy_stat0 >> 24) & 0xff);
|
||||
}
|
||||
|
||||
int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_rx_status status;
|
||||
struct ieee80211_hdr *hdr;
|
||||
struct wcn36xx_rx_bd *bd;
|
||||
u16 fc, sn;
|
||||
|
||||
/*
|
||||
* All fields must be 0, otherwise it can lead to
|
||||
* unexpected consequences.
|
||||
*/
|
||||
memset(&status, 0, sizeof(status));
|
||||
|
||||
bd = (struct wcn36xx_rx_bd *)skb->data;
|
||||
buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32));
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP,
|
||||
"BD <<< ", (char *)bd,
|
||||
sizeof(struct wcn36xx_rx_bd));
|
||||
|
||||
skb_put(skb, bd->pdu.mpdu_header_off + bd->pdu.mpdu_len);
|
||||
skb_pull(skb, bd->pdu.mpdu_header_off);
|
||||
|
||||
status.mactime = 10;
|
||||
status.freq = WCN36XX_CENTER_FREQ(wcn);
|
||||
status.band = WCN36XX_BAND(wcn);
|
||||
status.signal = -get_rssi0(bd);
|
||||
status.antenna = 1;
|
||||
status.rate_idx = 1;
|
||||
status.flag = 0;
|
||||
status.rx_flags = 0;
|
||||
status.flag |= RX_FLAG_IV_STRIPPED |
|
||||
RX_FLAG_MMIC_STRIPPED |
|
||||
RX_FLAG_DECRYPTED;
|
||||
|
||||
wcn36xx_dbg(WCN36XX_DBG_RX, "status.flags=%x status->vendor_radiotap_len=%x\n",
|
||||
status.flag, status.vendor_radiotap_len);
|
||||
|
||||
memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
fc = __le16_to_cpu(hdr->frame_control);
|
||||
sn = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl));
|
||||
|
||||
if (ieee80211_is_beacon(hdr->frame_control)) {
|
||||
wcn36xx_dbg(WCN36XX_DBG_BEACON, "beacon skb %p len %d fc %04x sn %d\n",
|
||||
skb, skb->len, fc, sn);
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_BEACON_DUMP, "SKB <<< ",
|
||||
(char *)skb->data, skb->len);
|
||||
} else {
|
||||
wcn36xx_dbg(WCN36XX_DBG_RX, "rx skb %p len %d fc %04x sn %d\n",
|
||||
skb, skb->len, fc, sn);
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_RX_DUMP, "SKB <<< ",
|
||||
(char *)skb->data, skb->len);
|
||||
}
|
||||
|
||||
ieee80211_rx_irqsafe(wcn->hw, skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wcn36xx_set_tx_pdu(struct wcn36xx_tx_bd *bd,
|
||||
u32 mpdu_header_len,
|
||||
u32 len,
|
||||
u16 tid)
|
||||
{
|
||||
bd->pdu.mpdu_header_len = mpdu_header_len;
|
||||
bd->pdu.mpdu_header_off = sizeof(*bd);
|
||||
bd->pdu.mpdu_data_off = bd->pdu.mpdu_header_len +
|
||||
bd->pdu.mpdu_header_off;
|
||||
bd->pdu.mpdu_len = len;
|
||||
bd->pdu.tid = tid;
|
||||
}
|
||||
|
||||
static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn,
|
||||
u8 *addr)
|
||||
{
|
||||
struct wcn36xx_vif *vif_priv = NULL;
|
||||
struct ieee80211_vif *vif = NULL;
|
||||
list_for_each_entry(vif_priv, &wcn->vif_list, list) {
|
||||
vif = container_of((void *)vif_priv,
|
||||
struct ieee80211_vif,
|
||||
drv_priv);
|
||||
if (memcmp(vif->addr, addr, ETH_ALEN) == 0)
|
||||
return vif_priv;
|
||||
}
|
||||
wcn36xx_warn("vif %pM not found\n", addr);
|
||||
return NULL;
|
||||
}
|
||||
static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd,
|
||||
struct wcn36xx *wcn,
|
||||
struct wcn36xx_vif **vif_priv,
|
||||
struct wcn36xx_sta *sta_priv,
|
||||
struct ieee80211_hdr *hdr,
|
||||
bool bcast)
|
||||
{
|
||||
struct ieee80211_vif *vif = NULL;
|
||||
struct wcn36xx_vif *__vif_priv = NULL;
|
||||
bd->bd_rate = WCN36XX_BD_RATE_DATA;
|
||||
|
||||
/*
|
||||
* For not unicast frames mac80211 will not set sta pointer so use
|
||||
* self_sta_index instead.
|
||||
*/
|
||||
if (sta_priv) {
|
||||
__vif_priv = sta_priv->vif;
|
||||
vif = container_of((void *)__vif_priv,
|
||||
struct ieee80211_vif,
|
||||
drv_priv);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_STATION) {
|
||||
bd->sta_index = sta_priv->bss_sta_index;
|
||||
bd->dpu_desc_idx = sta_priv->bss_dpu_desc_index;
|
||||
} else if (vif->type == NL80211_IFTYPE_AP ||
|
||||
vif->type == NL80211_IFTYPE_ADHOC ||
|
||||
vif->type == NL80211_IFTYPE_MESH_POINT) {
|
||||
bd->sta_index = sta_priv->sta_index;
|
||||
bd->dpu_desc_idx = sta_priv->dpu_desc_index;
|
||||
}
|
||||
} else {
|
||||
__vif_priv = get_vif_by_addr(wcn, hdr->addr2);
|
||||
bd->sta_index = __vif_priv->self_sta_index;
|
||||
bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index;
|
||||
}
|
||||
|
||||
bd->dpu_sign = __vif_priv->ucast_dpu_signature;
|
||||
|
||||
if (ieee80211_is_nullfunc(hdr->frame_control) ||
|
||||
(sta_priv && !sta_priv->is_data_encrypted))
|
||||
bd->dpu_ne = 1;
|
||||
|
||||
if (bcast) {
|
||||
bd->ub = 1;
|
||||
bd->ack_policy = 1;
|
||||
}
|
||||
*vif_priv = __vif_priv;
|
||||
}
|
||||
|
||||
static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd,
|
||||
struct wcn36xx *wcn,
|
||||
struct wcn36xx_vif **vif_priv,
|
||||
struct ieee80211_hdr *hdr,
|
||||
bool bcast)
|
||||
{
|
||||
struct wcn36xx_vif *__vif_priv =
|
||||
get_vif_by_addr(wcn, hdr->addr2);
|
||||
bd->sta_index = __vif_priv->self_sta_index;
|
||||
bd->dpu_desc_idx = __vif_priv->self_dpu_desc_index;
|
||||
bd->dpu_ne = 1;
|
||||
|
||||
/* default rate for unicast */
|
||||
if (ieee80211_is_mgmt(hdr->frame_control))
|
||||
bd->bd_rate = (WCN36XX_BAND(wcn) == IEEE80211_BAND_5GHZ) ?
|
||||
WCN36XX_BD_RATE_CTRL :
|
||||
WCN36XX_BD_RATE_MGMT;
|
||||
else if (ieee80211_is_ctl(hdr->frame_control))
|
||||
bd->bd_rate = WCN36XX_BD_RATE_CTRL;
|
||||
else
|
||||
wcn36xx_warn("frame control type unknown\n");
|
||||
|
||||
/*
|
||||
* In joining state trick hardware that probe is sent as
|
||||
* unicast even if address is broadcast.
|
||||
*/
|
||||
if (__vif_priv->is_joining &&
|
||||
ieee80211_is_probe_req(hdr->frame_control))
|
||||
bcast = false;
|
||||
|
||||
if (bcast) {
|
||||
/* broadcast */
|
||||
bd->ub = 1;
|
||||
/* No ack needed not unicast */
|
||||
bd->ack_policy = 1;
|
||||
bd->queue_id = WCN36XX_TX_B_WQ_ID;
|
||||
} else
|
||||
bd->queue_id = WCN36XX_TX_U_WQ_ID;
|
||||
*vif_priv = __vif_priv;
|
||||
}
|
||||
|
||||
int wcn36xx_start_tx(struct wcn36xx *wcn,
|
||||
struct wcn36xx_sta *sta_priv,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
struct wcn36xx_vif *vif_priv = NULL;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
unsigned long flags;
|
||||
bool is_low = ieee80211_is_data(hdr->frame_control);
|
||||
bool bcast = is_broadcast_ether_addr(hdr->addr1) ||
|
||||
is_multicast_ether_addr(hdr->addr1);
|
||||
struct wcn36xx_tx_bd *bd = wcn36xx_dxe_get_next_bd(wcn, is_low);
|
||||
|
||||
if (!bd) {
|
||||
/*
|
||||
* TX DXE are used in pairs. One for the BD and one for the
|
||||
* actual frame. The BD DXE's has a preallocated buffer while
|
||||
* the skb ones does not. If this isn't true something is really
|
||||
* wierd. TODO: Recover from this situation
|
||||
*/
|
||||
|
||||
wcn36xx_err("bd address may not be NULL for BD DXE\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(bd, 0, sizeof(*bd));
|
||||
|
||||
wcn36xx_dbg(WCN36XX_DBG_TX,
|
||||
"tx skb %p len %d fc %04x sn %d %s %s\n",
|
||||
skb, skb->len, __le16_to_cpu(hdr->frame_control),
|
||||
IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl)),
|
||||
is_low ? "low" : "high", bcast ? "bcast" : "ucast");
|
||||
|
||||
wcn36xx_dbg_dump(WCN36XX_DBG_TX_DUMP, "", skb->data, skb->len);
|
||||
|
||||
bd->dpu_rf = WCN36XX_BMU_WQ_TX;
|
||||
|
||||
bd->tx_comp = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS;
|
||||
if (bd->tx_comp) {
|
||||
wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n");
|
||||
spin_lock_irqsave(&wcn->dxe_lock, flags);
|
||||
if (wcn->tx_ack_skb) {
|
||||
spin_unlock_irqrestore(&wcn->dxe_lock, flags);
|
||||
wcn36xx_warn("tx_ack_skb already set\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wcn->tx_ack_skb = skb;
|
||||
spin_unlock_irqrestore(&wcn->dxe_lock, flags);
|
||||
|
||||
/* Only one at a time is supported by fw. Stop the TX queues
|
||||
* until the ack status gets back.
|
||||
*
|
||||
* TODO: Add watchdog in case FW does not answer
|
||||
*/
|
||||
ieee80211_stop_queues(wcn->hw);
|
||||
}
|
||||
|
||||
/* Data frames served first*/
|
||||
if (is_low) {
|
||||
wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, hdr, bcast);
|
||||
wcn36xx_set_tx_pdu(bd,
|
||||
ieee80211_is_data_qos(hdr->frame_control) ?
|
||||
sizeof(struct ieee80211_qos_hdr) :
|
||||
sizeof(struct ieee80211_hdr_3addr),
|
||||
skb->len, sta_priv ? sta_priv->tid : 0);
|
||||
} else {
|
||||
/* MGMT and CTRL frames are handeld here*/
|
||||
wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, hdr, bcast);
|
||||
wcn36xx_set_tx_pdu(bd,
|
||||
ieee80211_is_data_qos(hdr->frame_control) ?
|
||||
sizeof(struct ieee80211_qos_hdr) :
|
||||
sizeof(struct ieee80211_hdr_3addr),
|
||||
skb->len, WCN36XX_TID);
|
||||
}
|
||||
|
||||
buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32));
|
||||
bd->tx_bd_sign = 0xbdbdbdbd;
|
||||
|
||||
return wcn36xx_dxe_tx_frame(wcn, vif_priv, skb, is_low);
|
||||
}
|
160
drivers/net/wireless/ath/wcn36xx/txrx.h
Normal file
160
drivers/net/wireless/ath/wcn36xx/txrx.h
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
*
|
||||
* 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 _TXRX_H_
|
||||
#define _TXRX_H_
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include "wcn36xx.h"
|
||||
|
||||
/* TODO describe all properties */
|
||||
#define WCN36XX_802_11_HEADER_LEN 24
|
||||
#define WCN36XX_BMU_WQ_TX 25
|
||||
#define WCN36XX_TID 7
|
||||
/* broadcast wq ID */
|
||||
#define WCN36XX_TX_B_WQ_ID 0xA
|
||||
#define WCN36XX_TX_U_WQ_ID 0x9
|
||||
/* bd_rate */
|
||||
#define WCN36XX_BD_RATE_DATA 0
|
||||
#define WCN36XX_BD_RATE_MGMT 2
|
||||
#define WCN36XX_BD_RATE_CTRL 3
|
||||
|
||||
struct wcn36xx_pdu {
|
||||
u32 dpu_fb:8;
|
||||
u32 adu_fb:8;
|
||||
u32 pdu_id:16;
|
||||
|
||||
/* 0x04*/
|
||||
u32 tail_pdu_idx:16;
|
||||
u32 head_pdu_idx:16;
|
||||
|
||||
/* 0x08*/
|
||||
u32 pdu_count:7;
|
||||
u32 mpdu_data_off:9;
|
||||
u32 mpdu_header_off:8;
|
||||
u32 mpdu_header_len:8;
|
||||
|
||||
/* 0x0c*/
|
||||
u32 reserved4:8;
|
||||
u32 tid:4;
|
||||
u32 reserved3:4;
|
||||
u32 mpdu_len:16;
|
||||
};
|
||||
|
||||
struct wcn36xx_rx_bd {
|
||||
u32 bdt:2;
|
||||
u32 ft:1;
|
||||
u32 dpu_ne:1;
|
||||
u32 rx_key_id:3;
|
||||
u32 ub:1;
|
||||
u32 rmf:1;
|
||||
u32 uma_bypass:1;
|
||||
u32 csr11:1;
|
||||
u32 reserved0:1;
|
||||
u32 scan_learn:1;
|
||||
u32 rx_ch:4;
|
||||
u32 rtsf:1;
|
||||
u32 bsf:1;
|
||||
u32 a2hf:1;
|
||||
u32 st_auf:1;
|
||||
u32 dpu_sign:3;
|
||||
u32 dpu_rf:8;
|
||||
|
||||
struct wcn36xx_pdu pdu;
|
||||
|
||||
/* 0x14*/
|
||||
u32 addr3:8;
|
||||
u32 addr2:8;
|
||||
u32 addr1:8;
|
||||
u32 dpu_desc_idx:8;
|
||||
|
||||
/* 0x18*/
|
||||
u32 rxp_flags:23;
|
||||
u32 rate_id:9;
|
||||
|
||||
u32 phy_stat0;
|
||||
u32 phy_stat1;
|
||||
|
||||
/* 0x24 */
|
||||
u32 rx_times;
|
||||
|
||||
u32 pmi_cmd[6];
|
||||
|
||||
/* 0x40 */
|
||||
u32 reserved7:4;
|
||||
u32 reorder_slot_id:6;
|
||||
u32 reorder_fwd_id:6;
|
||||
u32 reserved6:12;
|
||||
u32 reorder_code:4;
|
||||
|
||||
/* 0x44 */
|
||||
u32 exp_seq_num:12;
|
||||
u32 cur_seq_num:12;
|
||||
u32 fr_type_subtype:8;
|
||||
|
||||
/* 0x48 */
|
||||
u32 msdu_size:16;
|
||||
u32 sub_fr_id:4;
|
||||
u32 proc_order:4;
|
||||
u32 reserved9:4;
|
||||
u32 aef:1;
|
||||
u32 lsf:1;
|
||||
u32 esf:1;
|
||||
u32 asf:1;
|
||||
};
|
||||
|
||||
struct wcn36xx_tx_bd {
|
||||
u32 bdt:2;
|
||||
u32 ft:1;
|
||||
u32 dpu_ne:1;
|
||||
u32 fw_tx_comp:1;
|
||||
u32 tx_comp:1;
|
||||
u32 reserved1:1;
|
||||
u32 ub:1;
|
||||
u32 rmf:1;
|
||||
u32 reserved0:12;
|
||||
u32 dpu_sign:3;
|
||||
u32 dpu_rf:8;
|
||||
|
||||
struct wcn36xx_pdu pdu;
|
||||
|
||||
/* 0x14*/
|
||||
u32 reserved5:7;
|
||||
u32 queue_id:5;
|
||||
u32 bd_rate:2;
|
||||
u32 ack_policy:2;
|
||||
u32 sta_index:8;
|
||||
u32 dpu_desc_idx:8;
|
||||
|
||||
u32 tx_bd_sign;
|
||||
u32 reserved6;
|
||||
u32 dxe_start_time;
|
||||
u32 dxe_end_time;
|
||||
|
||||
/*u32 tcp_udp_start_off:10;
|
||||
u32 header_cks:16;
|
||||
u32 reserved7:6;*/
|
||||
};
|
||||
|
||||
struct wcn36xx_sta;
|
||||
struct wcn36xx;
|
||||
|
||||
int wcn36xx_rx_skb(struct wcn36xx *wcn, struct sk_buff *skb);
|
||||
int wcn36xx_start_tx(struct wcn36xx *wcn,
|
||||
struct wcn36xx_sta *sta_priv,
|
||||
struct sk_buff *skb);
|
||||
|
||||
#endif /* _TXRX_H_ */
|
238
drivers/net/wireless/ath/wcn36xx/wcn36xx.h
Normal file
238
drivers/net/wireless/ath/wcn36xx/wcn36xx.h
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (c) 2013 Eugene Krasnikov <k.eugene.e@gmail.com>
|
||||
*
|
||||
* 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 _WCN36XX_H_
|
||||
#define _WCN36XX_H_
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <net/mac80211.h>
|
||||
|
||||
#include "hal.h"
|
||||
#include "smd.h"
|
||||
#include "txrx.h"
|
||||
#include "dxe.h"
|
||||
#include "pmc.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define WLAN_NV_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin"
|
||||
#define WCN36XX_AGGR_BUFFER_SIZE 64
|
||||
|
||||
extern unsigned int wcn36xx_dbg_mask;
|
||||
|
||||
enum wcn36xx_debug_mask {
|
||||
WCN36XX_DBG_DXE = 0x00000001,
|
||||
WCN36XX_DBG_DXE_DUMP = 0x00000002,
|
||||
WCN36XX_DBG_SMD = 0x00000004,
|
||||
WCN36XX_DBG_SMD_DUMP = 0x00000008,
|
||||
WCN36XX_DBG_RX = 0x00000010,
|
||||
WCN36XX_DBG_RX_DUMP = 0x00000020,
|
||||
WCN36XX_DBG_TX = 0x00000040,
|
||||
WCN36XX_DBG_TX_DUMP = 0x00000080,
|
||||
WCN36XX_DBG_HAL = 0x00000100,
|
||||
WCN36XX_DBG_HAL_DUMP = 0x00000200,
|
||||
WCN36XX_DBG_MAC = 0x00000400,
|
||||
WCN36XX_DBG_BEACON = 0x00000800,
|
||||
WCN36XX_DBG_BEACON_DUMP = 0x00001000,
|
||||
WCN36XX_DBG_PMC = 0x00002000,
|
||||
WCN36XX_DBG_PMC_DUMP = 0x00004000,
|
||||
WCN36XX_DBG_ANY = 0xffffffff,
|
||||
};
|
||||
|
||||
#define wcn36xx_err(fmt, arg...) \
|
||||
printk(KERN_ERR pr_fmt("ERROR " fmt), ##arg);
|
||||
|
||||
#define wcn36xx_warn(fmt, arg...) \
|
||||
printk(KERN_WARNING pr_fmt("WARNING " fmt), ##arg)
|
||||
|
||||
#define wcn36xx_info(fmt, arg...) \
|
||||
printk(KERN_INFO pr_fmt(fmt), ##arg)
|
||||
|
||||
#define wcn36xx_dbg(mask, fmt, arg...) do { \
|
||||
if (wcn36xx_dbg_mask & mask) \
|
||||
printk(KERN_DEBUG pr_fmt(fmt), ##arg); \
|
||||
} while (0)
|
||||
|
||||
#define wcn36xx_dbg_dump(mask, prefix_str, buf, len) do { \
|
||||
if (wcn36xx_dbg_mask & mask) \
|
||||
print_hex_dump(KERN_DEBUG, pr_fmt(prefix_str), \
|
||||
DUMP_PREFIX_OFFSET, 32, 1, \
|
||||
buf, len, false); \
|
||||
} while (0)
|
||||
|
||||
#define WCN36XX_HW_CHANNEL(__wcn) (__wcn->hw->conf.chandef.chan->hw_value)
|
||||
#define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band)
|
||||
#define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq)
|
||||
#define WCN36XX_LISTEN_INTERVAL(__wcn) (__wcn->hw->conf.listen_interval)
|
||||
#define WCN36XX_FLAGS(__wcn) (__wcn->hw->flags)
|
||||
#define WCN36XX_MAX_POWER(__wcn) (__wcn->hw->conf.chandef.chan->max_power)
|
||||
|
||||
static inline void buff_to_be(u32 *buf, size_t len)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < len; i++)
|
||||
buf[i] = cpu_to_be32(buf[i]);
|
||||
}
|
||||
|
||||
struct nv_data {
|
||||
int is_valid;
|
||||
u8 table;
|
||||
};
|
||||
|
||||
/* Interface for platform control path
|
||||
*
|
||||
* @open: hook must be called when wcn36xx wants to open control channel.
|
||||
* @tx: sends a buffer.
|
||||
*/
|
||||
struct wcn36xx_platform_ctrl_ops {
|
||||
int (*open)(void *drv_priv, void *rsp_cb);
|
||||
void (*close)(void);
|
||||
int (*tx)(char *buf, size_t len);
|
||||
int (*get_hw_mac)(u8 *addr);
|
||||
int (*smsm_change_state)(u32 clear_mask, u32 set_mask);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct wcn36xx_vif - holds VIF related fields
|
||||
*
|
||||
* @bss_index: bss_index is initially set to 0xFF. bss_index is received from
|
||||
* HW after first config_bss call and must be used in delete_bss and
|
||||
* enter/exit_bmps.
|
||||
*/
|
||||
struct wcn36xx_vif {
|
||||
struct list_head list;
|
||||
struct wcn36xx_sta *sta;
|
||||
u8 dtim_period;
|
||||
enum ani_ed_type encrypt_type;
|
||||
bool is_joining;
|
||||
struct wcn36xx_hal_mac_ssid ssid;
|
||||
|
||||
/* Power management */
|
||||
enum wcn36xx_power_state pw_state;
|
||||
|
||||
u8 bss_index;
|
||||
u8 ucast_dpu_signature;
|
||||
/* Returned from WCN36XX_HAL_ADD_STA_SELF_RSP */
|
||||
u8 self_sta_index;
|
||||
u8 self_dpu_desc_index;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct wcn36xx_sta - holds STA related fields
|
||||
*
|
||||
* @tid: traffic ID that is used during AMPDU and in TX BD.
|
||||
* @sta_index: STA index is returned from HW after config_sta call and is
|
||||
* used in both SMD channel and TX BD.
|
||||
* @dpu_desc_index: DPU descriptor index is returned from HW after config_sta
|
||||
* call and is used in TX BD.
|
||||
* @bss_sta_index: STA index is returned from HW after config_bss call and is
|
||||
* used in both SMD channel and TX BD. See table bellow when it is used.
|
||||
* @bss_dpu_desc_index: DPU descriptor index is returned from HW after
|
||||
* config_bss call and is used in TX BD.
|
||||
* ______________________________________________
|
||||
* | | STA | AP |
|
||||
* |______________|_____________|_______________|
|
||||
* | TX BD |bss_sta_index| sta_index |
|
||||
* |______________|_____________|_______________|
|
||||
* |all SMD calls |bss_sta_index| sta_index |
|
||||
* |______________|_____________|_______________|
|
||||
* |smd_delete_sta| sta_index | sta_index |
|
||||
* |______________|_____________|_______________|
|
||||
*/
|
||||
struct wcn36xx_sta {
|
||||
struct wcn36xx_vif *vif;
|
||||
u16 aid;
|
||||
u16 tid;
|
||||
u8 sta_index;
|
||||
u8 dpu_desc_index;
|
||||
u8 bss_sta_index;
|
||||
u8 bss_dpu_desc_index;
|
||||
bool is_data_encrypted;
|
||||
/* Rates */
|
||||
struct wcn36xx_hal_supported_rates supported_rates;
|
||||
};
|
||||
struct wcn36xx_dxe_ch;
|
||||
struct wcn36xx {
|
||||
struct ieee80211_hw *hw;
|
||||
struct device *dev;
|
||||
struct list_head vif_list;
|
||||
|
||||
u8 fw_revision;
|
||||
u8 fw_version;
|
||||
u8 fw_minor;
|
||||
u8 fw_major;
|
||||
|
||||
/* extra byte for the NULL termination */
|
||||
u8 crm_version[WCN36XX_HAL_VERSION_LENGTH + 1];
|
||||
u8 wlan_version[WCN36XX_HAL_VERSION_LENGTH + 1];
|
||||
|
||||
/* IRQs */
|
||||
int tx_irq;
|
||||
int rx_irq;
|
||||
void __iomem *mmio;
|
||||
|
||||
struct wcn36xx_platform_ctrl_ops *ctrl_ops;
|
||||
/*
|
||||
* smd_buf must be protected with smd_mutex to garantee
|
||||
* that all messages are sent one after another
|
||||
*/
|
||||
u8 *hal_buf;
|
||||
size_t hal_rsp_len;
|
||||
struct mutex hal_mutex;
|
||||
struct completion hal_rsp_compl;
|
||||
struct workqueue_struct *hal_ind_wq;
|
||||
struct work_struct hal_ind_work;
|
||||
struct mutex hal_ind_mutex;
|
||||
struct list_head hal_ind_queue;
|
||||
|
||||
/* DXE channels */
|
||||
struct wcn36xx_dxe_ch dxe_tx_l_ch; /* TX low */
|
||||
struct wcn36xx_dxe_ch dxe_tx_h_ch; /* TX high */
|
||||
struct wcn36xx_dxe_ch dxe_rx_l_ch; /* RX low */
|
||||
struct wcn36xx_dxe_ch dxe_rx_h_ch; /* RX high */
|
||||
|
||||
/* For synchronization of DXE resources from BH, IRQ and WQ contexts */
|
||||
spinlock_t dxe_lock;
|
||||
bool queues_stopped;
|
||||
|
||||
/* Memory pools */
|
||||
struct wcn36xx_dxe_mem_pool mgmt_mem_pool;
|
||||
struct wcn36xx_dxe_mem_pool data_mem_pool;
|
||||
|
||||
struct sk_buff *tx_ack_skb;
|
||||
|
||||
#ifdef CONFIG_WCN36XX_DEBUGFS
|
||||
/* Debug file system entry */
|
||||
struct wcn36xx_dfs_entry dfs;
|
||||
#endif /* CONFIG_WCN36XX_DEBUGFS */
|
||||
|
||||
};
|
||||
|
||||
static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn,
|
||||
u8 major,
|
||||
u8 minor,
|
||||
u8 version,
|
||||
u8 revision)
|
||||
{
|
||||
return (wcn->fw_major == major &&
|
||||
wcn->fw_minor == minor &&
|
||||
wcn->fw_version == version &&
|
||||
wcn->fw_revision == revision);
|
||||
}
|
||||
void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates);
|
||||
|
||||
#endif /* _WCN36XX_H_ */
|
@ -5695,7 +5695,7 @@ static bool brcms_c_chipmatch_pci(struct bcma_device *core)
|
||||
return true;
|
||||
if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID))
|
||||
return true;
|
||||
if (device == BCM4313_D11N2G_ID)
|
||||
if (device == BCM4313_D11N2G_ID || device == BCM4313_CHIP_ID)
|
||||
return true;
|
||||
if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID))
|
||||
return true;
|
||||
|
@ -1048,7 +1048,7 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
|
||||
struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL;
|
||||
unsigned long cmd_flags;
|
||||
unsigned long scan_pending_q_flags;
|
||||
uint16_t cancel_scan_cmd = false;
|
||||
bool cancel_scan_cmd = false;
|
||||
|
||||
if ((adapter->curr_cmd) &&
|
||||
(adapter->curr_cmd->wait_q_enabled)) {
|
||||
|
@ -621,7 +621,7 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
|
||||
int ret = 0;
|
||||
struct ieee_types_assoc_rsp *assoc_rsp;
|
||||
struct mwifiex_bssdescriptor *bss_desc;
|
||||
u8 enable_data = true;
|
||||
bool enable_data = true;
|
||||
u16 cap_info, status_code;
|
||||
|
||||
assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params;
|
||||
|
@ -882,7 +882,9 @@ mwifiex_add_card(void *card, struct semaphore *sem,
|
||||
adapter->cmd_wait_q.status = 0;
|
||||
adapter->scan_wait_q_woken = false;
|
||||
|
||||
adapter->workqueue = create_workqueue("MWIFIEX_WORK_QUEUE");
|
||||
adapter->workqueue =
|
||||
alloc_workqueue("MWIFIEX_WORK_QUEUE",
|
||||
WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
|
||||
if (!adapter->workqueue)
|
||||
goto err_kmalloc;
|
||||
|
||||
|
@ -327,7 +327,7 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv,
|
||||
{
|
||||
struct mwifiex_adapter *adapter = priv->adapter;
|
||||
struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg;
|
||||
u16 hs_activate = false;
|
||||
bool hs_activate = false;
|
||||
|
||||
if (!hscfg_param)
|
||||
/* New Activate command */
|
||||
|
@ -708,7 +708,7 @@ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
|
||||
{
|
||||
u8 *curr = (u8 *) &resp->params.get_wmm_status;
|
||||
uint16_t resp_len = le16_to_cpu(resp->size), tlv_len;
|
||||
int valid = true;
|
||||
bool valid = true;
|
||||
|
||||
struct mwifiex_ie_types_data *tlv_hdr;
|
||||
struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus;
|
||||
|
@ -636,7 +636,7 @@ static int p54spi_probe(struct spi_device *spi)
|
||||
gpio_direction_input(p54spi_gpio_irq);
|
||||
|
||||
ret = request_irq(gpio_to_irq(p54spi_gpio_irq),
|
||||
p54spi_interrupt, IRQF_DISABLED, "p54spi",
|
||||
p54spi_interrupt, 0, "p54spi",
|
||||
priv->spi);
|
||||
if (ret < 0) {
|
||||
dev_err(&priv->spi->dev, "request_irq() failed");
|
||||
|
@ -219,6 +219,7 @@ config RT2X00_LIB_USB
|
||||
|
||||
config RT2X00_LIB
|
||||
tristate
|
||||
select AVERAGE
|
||||
|
||||
config RT2X00_LIB_FIRMWARE
|
||||
boolean
|
||||
|
@ -124,7 +124,7 @@
|
||||
/*
|
||||
* MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number.
|
||||
*/
|
||||
#define MAC_CSR0_3290 0x0000
|
||||
#define MAC_CSR0_3290 0x0000
|
||||
|
||||
/*
|
||||
* E2PROM_CSR: PCI EEPROM control register.
|
||||
@ -213,17 +213,17 @@
|
||||
/*
|
||||
* COEX_CFG_0
|
||||
*/
|
||||
#define COEX_CFG0 0x0040
|
||||
#define COEX_CFG0 0x0040
|
||||
#define COEX_CFG_ANT FIELD32(0xff000000)
|
||||
/*
|
||||
* COEX_CFG_1
|
||||
*/
|
||||
#define COEX_CFG1 0x0044
|
||||
#define COEX_CFG1 0x0044
|
||||
|
||||
/*
|
||||
* COEX_CFG_2
|
||||
*/
|
||||
#define COEX_CFG2 0x0048
|
||||
#define COEX_CFG2 0x0048
|
||||
#define BT_COEX_CFG1 FIELD32(0xff000000)
|
||||
#define BT_COEX_CFG0 FIELD32(0x00ff0000)
|
||||
#define WL_COEX_CFG1 FIELD32(0x0000ff00)
|
||||
@ -237,8 +237,8 @@
|
||||
#define PLL_RESERVED_INPUT2 FIELD32(0x0000ff00)
|
||||
#define PLL_CONTROL FIELD32(0x00070000)
|
||||
#define PLL_LPF_R1 FIELD32(0x00080000)
|
||||
#define PLL_LPF_C1_CTRL FIELD32(0x00300000)
|
||||
#define PLL_LPF_C2_CTRL FIELD32(0x00c00000)
|
||||
#define PLL_LPF_C1_CTRL FIELD32(0x00300000)
|
||||
#define PLL_LPF_C2_CTRL FIELD32(0x00c00000)
|
||||
#define PLL_CP_CURRENT_CTRL FIELD32(0x03000000)
|
||||
#define PLL_PFD_DELAY_CTRL FIELD32(0x0c000000)
|
||||
#define PLL_LOCK_CTRL FIELD32(0x70000000)
|
||||
@ -2166,7 +2166,7 @@ struct mac_iveiv_entry {
|
||||
*/
|
||||
#define RFCSR6_R1 FIELD8(0x03)
|
||||
#define RFCSR6_R2 FIELD8(0x40)
|
||||
#define RFCSR6_TXDIV FIELD8(0x0c)
|
||||
#define RFCSR6_TXDIV FIELD8(0x0c)
|
||||
/* bits for RF3053 */
|
||||
#define RFCSR6_VCO_IC FIELD8(0xc0)
|
||||
|
||||
@ -2204,13 +2204,13 @@ struct mac_iveiv_entry {
|
||||
* RFCSR 12:
|
||||
*/
|
||||
#define RFCSR12_TX_POWER FIELD8(0x1f)
|
||||
#define RFCSR12_DR0 FIELD8(0xe0)
|
||||
#define RFCSR12_DR0 FIELD8(0xe0)
|
||||
|
||||
/*
|
||||
* RFCSR 13:
|
||||
*/
|
||||
#define RFCSR13_TX_POWER FIELD8(0x1f)
|
||||
#define RFCSR13_DR0 FIELD8(0xe0)
|
||||
#define RFCSR13_DR0 FIELD8(0xe0)
|
||||
|
||||
/*
|
||||
* RFCSR 15:
|
||||
@ -2228,7 +2228,7 @@ struct mac_iveiv_entry {
|
||||
#define RFCSR17_TXMIXER_GAIN FIELD8(0x07)
|
||||
#define RFCSR17_TX_LO1_EN FIELD8(0x08)
|
||||
#define RFCSR17_R FIELD8(0x20)
|
||||
#define RFCSR17_CODE FIELD8(0x7f)
|
||||
#define RFCSR17_CODE FIELD8(0x7f)
|
||||
|
||||
/* RFCSR 18 */
|
||||
#define RFCSR18_XO_TUNE_BYPASS FIELD8(0x40)
|
||||
@ -2451,7 +2451,7 @@ enum rt2800_eeprom_word {
|
||||
*/
|
||||
#define EEPROM_NIC_CONF0_RXPATH FIELD16(0x000f)
|
||||
#define EEPROM_NIC_CONF0_TXPATH FIELD16(0x00f0)
|
||||
#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00)
|
||||
#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00)
|
||||
|
||||
/*
|
||||
* EEPROM NIC Configuration 1
|
||||
@ -2473,18 +2473,18 @@ enum rt2800_eeprom_word {
|
||||
* DAC_TEST: 0: disable, 1: enable
|
||||
*/
|
||||
#define EEPROM_NIC_CONF1_HW_RADIO FIELD16(0x0001)
|
||||
#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002)
|
||||
#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004)
|
||||
#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008)
|
||||
#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002)
|
||||
#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004)
|
||||
#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008)
|
||||
#define EEPROM_NIC_CONF1_CARDBUS_ACCEL FIELD16(0x0010)
|
||||
#define EEPROM_NIC_CONF1_BW40M_SB_2G FIELD16(0x0020)
|
||||
#define EEPROM_NIC_CONF1_BW40M_SB_5G FIELD16(0x0040)
|
||||
#define EEPROM_NIC_CONF1_WPS_PBC FIELD16(0x0080)
|
||||
#define EEPROM_NIC_CONF1_BW40M_2G FIELD16(0x0100)
|
||||
#define EEPROM_NIC_CONF1_BW40M_5G FIELD16(0x0200)
|
||||
#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400)
|
||||
#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400)
|
||||
#define EEPROM_NIC_CONF1_ANT_DIVERSITY FIELD16(0x1800)
|
||||
#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000)
|
||||
#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000)
|
||||
#define EEPROM_NIC_CONF1_BT_COEXIST FIELD16(0x4000)
|
||||
#define EEPROM_NIC_CONF1_DAC_TEST FIELD16(0x8000)
|
||||
|
||||
@ -2523,9 +2523,9 @@ enum rt2800_eeprom_word {
|
||||
* TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream
|
||||
* CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved
|
||||
*/
|
||||
#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f)
|
||||
#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0)
|
||||
#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600)
|
||||
#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f)
|
||||
#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0)
|
||||
#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600)
|
||||
|
||||
/*
|
||||
* EEPROM LNA
|
||||
@ -2792,7 +2792,7 @@ enum rt2800_eeprom_word {
|
||||
#define MCU_CURRENT 0x36
|
||||
#define MCU_LED 0x50
|
||||
#define MCU_LED_STRENGTH 0x51
|
||||
#define MCU_LED_AG_CONF 0x52
|
||||
#define MCU_LED_AG_CONF 0x52
|
||||
#define MCU_LED_ACT_CONF 0x53
|
||||
#define MCU_LED_LED_POLARITY 0x54
|
||||
#define MCU_RADAR 0x60
|
||||
@ -2801,7 +2801,7 @@ enum rt2800_eeprom_word {
|
||||
#define MCU_FREQ_OFFSET 0x74
|
||||
#define MCU_BBP_SIGNAL 0x80
|
||||
#define MCU_POWER_SAVE 0x83
|
||||
#define MCU_BAND_SELECT 0x91
|
||||
#define MCU_BAND_SELECT 0x91
|
||||
|
||||
/*
|
||||
* MCU mailbox tokens
|
||||
|
@ -278,12 +278,9 @@ static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = {
|
||||
[EEPROM_LNA] = 0x0026,
|
||||
[EEPROM_EXT_LNA2] = 0x0027,
|
||||
[EEPROM_RSSI_BG] = 0x0028,
|
||||
[EEPROM_TXPOWER_DELTA] = 0x0028, /* Overlaps with RSSI_BG */
|
||||
[EEPROM_RSSI_BG2] = 0x0029,
|
||||
[EEPROM_TXMIXER_GAIN_BG] = 0x0029, /* Overlaps with RSSI_BG2 */
|
||||
[EEPROM_RSSI_A] = 0x002a,
|
||||
[EEPROM_RSSI_A2] = 0x002b,
|
||||
[EEPROM_TXMIXER_GAIN_A] = 0x002b, /* Overlaps with RSSI_A2 */
|
||||
[EEPROM_TXPOWER_BG1] = 0x0030,
|
||||
[EEPROM_TXPOWER_BG2] = 0x0037,
|
||||
[EEPROM_EXT_TXPOWER_BG3] = 0x003e,
|
||||
@ -1783,7 +1780,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
|
||||
rt2800_bbp_read(rt2x00dev, 3, &r3);
|
||||
|
||||
if (rt2x00_rt(rt2x00dev, RT3572) &&
|
||||
test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
|
||||
rt2x00_has_cap_bt_coexist(rt2x00dev))
|
||||
rt2800_config_3572bt_ant(rt2x00dev);
|
||||
|
||||
/*
|
||||
@ -1795,7 +1792,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
|
||||
break;
|
||||
case 2:
|
||||
if (rt2x00_rt(rt2x00dev, RT3572) &&
|
||||
test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
|
||||
rt2x00_has_cap_bt_coexist(rt2x00dev))
|
||||
rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1);
|
||||
else
|
||||
rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
|
||||
@ -1825,7 +1822,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
|
||||
break;
|
||||
case 2:
|
||||
if (rt2x00_rt(rt2x00dev, RT3572) &&
|
||||
test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
|
||||
rt2x00_has_cap_bt_coexist(rt2x00dev)) {
|
||||
rt2x00_set_field8(&r3, BBP3_RX_ADC, 1);
|
||||
rt2x00_set_field8(&r3, BBP3_RX_ANTENNA,
|
||||
rt2x00dev->curr_band == IEEE80211_BAND_5GHZ);
|
||||
@ -2029,13 +2026,6 @@ static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev,
|
||||
rt2x00dev->default_ant.tx_chain_num <= 2);
|
||||
rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
|
||||
|
||||
rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
|
||||
rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1);
|
||||
rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
|
||||
msleep(1);
|
||||
rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0);
|
||||
rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
|
||||
|
||||
rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
|
||||
rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset);
|
||||
rt2800_rfcsr_write(rt2x00dev, 23, rfcsr);
|
||||
@ -2141,7 +2131,7 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
|
||||
rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0);
|
||||
rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0);
|
||||
rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0);
|
||||
if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
|
||||
if (rf->channel <= 14) {
|
||||
rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
|
||||
rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1);
|
||||
@ -2674,7 +2664,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev,
|
||||
if (rf->channel <= 14) {
|
||||
int idx = rf->channel-1;
|
||||
|
||||
if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
|
||||
if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) {
|
||||
/* r55/r59 value array of channel 1~14 */
|
||||
static const char r55_bt_rev[] = {0x83, 0x83,
|
||||
@ -3220,8 +3210,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
|
||||
if (rf->channel <= 14) {
|
||||
if (!rt2x00_rt(rt2x00dev, RT5390) &&
|
||||
!rt2x00_rt(rt2x00dev, RT5392)) {
|
||||
if (test_bit(CAPABILITY_EXTERNAL_LNA_BG,
|
||||
&rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
|
||||
rt2800_bbp_write(rt2x00dev, 82, 0x62);
|
||||
rt2800_bbp_write(rt2x00dev, 75, 0x46);
|
||||
} else {
|
||||
@ -3246,7 +3235,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
|
||||
if (rt2x00_rt(rt2x00dev, RT3593))
|
||||
rt2800_bbp_write(rt2x00dev, 83, 0x9a);
|
||||
|
||||
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_external_lna_a(rt2x00dev))
|
||||
rt2800_bbp_write(rt2x00dev, 75, 0x46);
|
||||
else
|
||||
rt2800_bbp_write(rt2x00dev, 75, 0x50);
|
||||
@ -3282,7 +3271,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
|
||||
/* Turn on primary PAs */
|
||||
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN,
|
||||
rf->channel > 14);
|
||||
if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_bt_coexist(rt2x00dev))
|
||||
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1);
|
||||
else
|
||||
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN,
|
||||
@ -3313,9 +3302,18 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
|
||||
|
||||
rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin);
|
||||
|
||||
if (rt2x00_rt(rt2x00dev, RT3572))
|
||||
if (rt2x00_rt(rt2x00dev, RT3572)) {
|
||||
rt2800_rfcsr_write(rt2x00dev, 8, 0x80);
|
||||
|
||||
/* AGC init */
|
||||
if (rf->channel <= 14)
|
||||
reg = 0x1c + (2 * rt2x00dev->lna_gain);
|
||||
else
|
||||
reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3);
|
||||
|
||||
rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg);
|
||||
}
|
||||
|
||||
if (rt2x00_rt(rt2x00dev, RT3593)) {
|
||||
rt2800_register_read(rt2x00dev, GPIO_CTRL, ®);
|
||||
|
||||
@ -3575,7 +3573,7 @@ static int rt2800_get_txpower_reg_delta(struct rt2x00_dev *rt2x00dev,
|
||||
{
|
||||
int delta;
|
||||
|
||||
if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_power_limit(rt2x00dev))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -3604,7 +3602,7 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
|
||||
if (rt2x00_rt(rt2x00dev, RT3593))
|
||||
return min_t(u8, txpower, 0xc);
|
||||
|
||||
if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_power_limit(rt2x00dev)) {
|
||||
/*
|
||||
* Check if eirp txpower exceed txpower_limit.
|
||||
* We use OFDM 6M as criterion and its eirp txpower
|
||||
@ -4416,6 +4414,7 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
|
||||
rt2x00_rt(rt2x00dev, RT3290) ||
|
||||
rt2x00_rt(rt2x00dev, RT3390) ||
|
||||
rt2x00_rt(rt2x00dev, RT3572) ||
|
||||
rt2x00_rt(rt2x00dev, RT3593) ||
|
||||
rt2x00_rt(rt2x00dev, RT5390) ||
|
||||
rt2x00_rt(rt2x00dev, RT5392) ||
|
||||
rt2x00_rt(rt2x00dev, RT5592))
|
||||
@ -4423,8 +4422,8 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
|
||||
else
|
||||
vgc = 0x2e + rt2x00dev->lna_gain;
|
||||
} else { /* 5GHZ band */
|
||||
if (rt2x00_rt(rt2x00dev, RT3572))
|
||||
vgc = 0x22 + (rt2x00dev->lna_gain * 5) / 3;
|
||||
if (rt2x00_rt(rt2x00dev, RT3593))
|
||||
vgc = 0x20 + (rt2x00dev->lna_gain * 5) / 3;
|
||||
else if (rt2x00_rt(rt2x00dev, RT5592))
|
||||
vgc = 0x24 + (2 * rt2x00dev->lna_gain);
|
||||
else {
|
||||
@ -4442,11 +4441,17 @@ static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev,
|
||||
struct link_qual *qual, u8 vgc_level)
|
||||
{
|
||||
if (qual->vgc_level != vgc_level) {
|
||||
if (rt2x00_rt(rt2x00dev, RT5592)) {
|
||||
if (rt2x00_rt(rt2x00dev, RT3572) ||
|
||||
rt2x00_rt(rt2x00dev, RT3593)) {
|
||||
rt2800_bbp_write_with_rx_chain(rt2x00dev, 66,
|
||||
vgc_level);
|
||||
} else if (rt2x00_rt(rt2x00dev, RT5592)) {
|
||||
rt2800_bbp_write(rt2x00dev, 83, qual->rssi > -65 ? 0x4a : 0x7a);
|
||||
rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level);
|
||||
} else
|
||||
} else {
|
||||
rt2800_bbp_write(rt2x00dev, 66, vgc_level);
|
||||
}
|
||||
|
||||
qual->vgc_level = vgc_level;
|
||||
qual->vgc_level_reg = vgc_level;
|
||||
}
|
||||
@ -4465,17 +4470,35 @@ void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
|
||||
|
||||
if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C))
|
||||
return;
|
||||
/*
|
||||
* When RSSI is better then -80 increase VGC level with 0x10, except
|
||||
* for rt5592 chip.
|
||||
|
||||
/* When RSSI is better than a certain threshold, increase VGC
|
||||
* with a chip specific value in order to improve the balance
|
||||
* between sensibility and noise isolation.
|
||||
*/
|
||||
|
||||
vgc = rt2800_get_default_vgc(rt2x00dev);
|
||||
|
||||
if (rt2x00_rt(rt2x00dev, RT5592) && qual->rssi > -65)
|
||||
vgc += 0x20;
|
||||
else if (qual->rssi > -80)
|
||||
vgc += 0x10;
|
||||
switch (rt2x00dev->chip.rt) {
|
||||
case RT3572:
|
||||
case RT3593:
|
||||
if (qual->rssi > -65) {
|
||||
if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ)
|
||||
vgc += 0x20;
|
||||
else
|
||||
vgc += 0x10;
|
||||
}
|
||||
break;
|
||||
|
||||
case RT5592:
|
||||
if (qual->rssi > -65)
|
||||
vgc += 0x20;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (qual->rssi > -80)
|
||||
vgc += 0x10;
|
||||
break;
|
||||
}
|
||||
|
||||
rt2800_set_vgc(rt2x00dev, qual, vgc);
|
||||
}
|
||||
@ -5500,7 +5523,7 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
|
||||
ant = (div_mode == 3) ? 1 : 0;
|
||||
|
||||
/* check if this is a Bluetooth combo card */
|
||||
if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
|
||||
u32 reg;
|
||||
|
||||
rt2800_register_read(rt2x00dev, GPIO_CTRL, ®);
|
||||
@ -5809,7 +5832,7 @@ static void rt2800_normal_mode_setup_3xxx(struct rt2x00_dev *rt2x00dev)
|
||||
rt2x00_rt_rev_lt(rt2x00dev, RT3071, REV_RT3071E) ||
|
||||
rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) ||
|
||||
rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) {
|
||||
if (!test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags))
|
||||
if (!rt2x00_has_cap_external_lna_bg(rt2x00dev))
|
||||
rt2x00_set_field8(&rfcsr, RFCSR17_R, 1);
|
||||
}
|
||||
|
||||
@ -6452,7 +6475,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
|
||||
rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
|
||||
rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
|
||||
|
||||
rt2800_rfcsr_write(rt2x00dev, 30, 0x00);
|
||||
rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
|
||||
rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
|
||||
rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
|
||||
rt2800_rfcsr_write(rt2x00dev, 33, 0x00);
|
||||
@ -6490,7 +6513,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
|
||||
rt2800_rfcsr_write(rt2x00dev, 56, 0x22);
|
||||
rt2800_rfcsr_write(rt2x00dev, 57, 0x80);
|
||||
rt2800_rfcsr_write(rt2x00dev, 58, 0x7f);
|
||||
rt2800_rfcsr_write(rt2x00dev, 59, 0x63);
|
||||
rt2800_rfcsr_write(rt2x00dev, 59, 0x8f);
|
||||
|
||||
rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
|
||||
if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
|
||||
@ -6510,7 +6533,6 @@ static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev)
|
||||
rt2800_rf_init_calibration(rt2x00dev, 2);
|
||||
|
||||
rt2800_rfcsr_write(rt2x00dev, 1, 0x17);
|
||||
rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
|
||||
rt2800_rfcsr_write(rt2x00dev, 3, 0x88);
|
||||
rt2800_rfcsr_write(rt2x00dev, 5, 0x10);
|
||||
rt2800_rfcsr_write(rt2x00dev, 6, 0xe0);
|
||||
@ -7224,7 +7246,7 @@ static const struct rf_channel rf_vals[] = {
|
||||
|
||||
/*
|
||||
* RF value list for rt3xxx
|
||||
* Supports: 2.4 GHz (all) & 5.2 GHz (RF3052)
|
||||
* Supports: 2.4 GHz (all) & 5.2 GHz (RF3052 & RF3053)
|
||||
*/
|
||||
static const struct rf_channel rf_vals_3x[] = {
|
||||
{1, 241, 2, 2 },
|
||||
@ -7420,72 +7442,6 @@ static const struct rf_channel rf_vals_5592_xtal40[] = {
|
||||
{196, 83, 0, 12, 1},
|
||||
};
|
||||
|
||||
static const struct rf_channel rf_vals_3053[] = {
|
||||
/* Channel, N, R, K */
|
||||
{1, 241, 2, 2},
|
||||
{2, 241, 2, 7},
|
||||
{3, 242, 2, 2},
|
||||
{4, 242, 2, 7},
|
||||
{5, 243, 2, 2},
|
||||
{6, 243, 2, 7},
|
||||
{7, 244, 2, 2},
|
||||
{8, 244, 2, 7},
|
||||
{9, 245, 2, 2},
|
||||
{10, 245, 2, 7},
|
||||
{11, 246, 2, 2},
|
||||
{12, 246, 2, 7},
|
||||
{13, 247, 2, 2},
|
||||
{14, 248, 2, 4},
|
||||
|
||||
{36, 0x56, 0, 4},
|
||||
{38, 0x56, 0, 6},
|
||||
{40, 0x56, 0, 8},
|
||||
{44, 0x57, 0, 0},
|
||||
{46, 0x57, 0, 2},
|
||||
{48, 0x57, 0, 4},
|
||||
{52, 0x57, 0, 8},
|
||||
{54, 0x57, 0, 10},
|
||||
{56, 0x58, 0, 0},
|
||||
{60, 0x58, 0, 4},
|
||||
{62, 0x58, 0, 6},
|
||||
{64, 0x58, 0, 8},
|
||||
|
||||
{100, 0x5B, 0, 8},
|
||||
{102, 0x5B, 0, 10},
|
||||
{104, 0x5C, 0, 0},
|
||||
{108, 0x5C, 0, 4},
|
||||
{110, 0x5C, 0, 6},
|
||||
{112, 0x5C, 0, 8},
|
||||
|
||||
/* NOTE: Channel 114 has been removed intentionally.
|
||||
* The EEPROM contains no TX power values for that,
|
||||
* and it is disabled in the vendor driver as well.
|
||||
*/
|
||||
|
||||
{116, 0x5D, 0, 0},
|
||||
{118, 0x5D, 0, 2},
|
||||
{120, 0x5D, 0, 4},
|
||||
{124, 0x5D, 0, 8},
|
||||
{126, 0x5D, 0, 10},
|
||||
{128, 0x5E, 0, 0},
|
||||
{132, 0x5E, 0, 4},
|
||||
{134, 0x5E, 0, 6},
|
||||
{136, 0x5E, 0, 8},
|
||||
{140, 0x5F, 0, 0},
|
||||
|
||||
{149, 0x5F, 0, 9},
|
||||
{151, 0x5F, 0, 11},
|
||||
{153, 0x60, 0, 1},
|
||||
{157, 0x60, 0, 5},
|
||||
{159, 0x60, 0, 7},
|
||||
{161, 0x60, 0, 9},
|
||||
{165, 0x61, 0, 1},
|
||||
{167, 0x61, 0, 3},
|
||||
{169, 0x61, 0, 5},
|
||||
{171, 0x61, 0, 7},
|
||||
{173, 0x61, 0, 9},
|
||||
};
|
||||
|
||||
static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
struct hw_mode_spec *spec = &rt2x00dev->spec;
|
||||
@ -7575,14 +7531,11 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
|
||||
rt2x00_rf(rt2x00dev, RF5392)) {
|
||||
spec->num_channels = 14;
|
||||
spec->channels = rf_vals_3x;
|
||||
} else if (rt2x00_rf(rt2x00dev, RF3052)) {
|
||||
} else if (rt2x00_rf(rt2x00dev, RF3052) ||
|
||||
rt2x00_rf(rt2x00dev, RF3053)) {
|
||||
spec->supported_bands |= SUPPORT_BAND_5GHZ;
|
||||
spec->num_channels = ARRAY_SIZE(rf_vals_3x);
|
||||
spec->channels = rf_vals_3x;
|
||||
} else if (rt2x00_rf(rt2x00dev, RF3053)) {
|
||||
spec->supported_bands |= SUPPORT_BAND_5GHZ;
|
||||
spec->num_channels = ARRAY_SIZE(rf_vals_3053);
|
||||
spec->channels = rf_vals_3053;
|
||||
} else if (rt2x00_rf(rt2x00dev, RF5592)) {
|
||||
spec->supported_bands |= SUPPORT_BAND_5GHZ;
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <linux/input-polldev.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/average.h>
|
||||
|
||||
#include <net/mac80211.h>
|
||||
|
||||
@ -138,17 +139,6 @@
|
||||
#define SHORT_EIFS ( SIFS + SHORT_DIFS + \
|
||||
GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) )
|
||||
|
||||
/*
|
||||
* Structure for average calculation
|
||||
* The avg field contains the actual average value,
|
||||
* but avg_weight is internally used during calculations
|
||||
* to prevent rounding errors.
|
||||
*/
|
||||
struct avg_val {
|
||||
int avg;
|
||||
int avg_weight;
|
||||
};
|
||||
|
||||
enum rt2x00_chip_intf {
|
||||
RT2X00_CHIP_INTF_PCI,
|
||||
RT2X00_CHIP_INTF_PCIE,
|
||||
@ -297,7 +287,7 @@ struct link_ant {
|
||||
* Similar to the avg_rssi in the link_qual structure
|
||||
* this value is updated by using the walking average.
|
||||
*/
|
||||
struct avg_val rssi_ant;
|
||||
struct ewma rssi_ant;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -326,7 +316,7 @@ struct link {
|
||||
/*
|
||||
* Currently active average RSSI value
|
||||
*/
|
||||
struct avg_val avg_rssi;
|
||||
struct ewma avg_rssi;
|
||||
|
||||
/*
|
||||
* Work structure for scheduling periodic link tuning.
|
||||
@ -1179,6 +1169,93 @@ static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev)
|
||||
return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC);
|
||||
}
|
||||
|
||||
/* Helpers for capability flags */
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_flag(struct rt2x00_dev *rt2x00dev,
|
||||
enum rt2x00_capability_flags cap_flag)
|
||||
{
|
||||
return test_bit(cap_flag, &rt2x00dev->cap_flags);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_hw_crypto(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_HW_CRYPTO);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_power_limit(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_POWER_LIMIT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_control_filters(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTERS);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_control_filter_pspoll(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTER_PSPOLL);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_pre_tbtt_interrupt(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_PRE_TBTT_INTERRUPT);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_link_tuning(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_LINK_TUNING);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_frame_type(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_FRAME_TYPE);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_rf_sequence(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_RF_SEQUENCE);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_external_lna_a(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_A);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_external_lna_bg(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_BG);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_DOUBLE_ANTENNA);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_bt_coexist(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_BT_COEXIST);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
rt2x00_has_cap_vco_recalibration(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_VCO_RECALIBRATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* rt2x00queue_map_txskb - Map a skb into DMA for TX purposes.
|
||||
* @entry: Pointer to &struct queue_entry
|
||||
|
@ -52,7 +52,7 @@ void rt2x00crypto_create_tx_descriptor(struct rt2x00_dev *rt2x00dev,
|
||||
struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_key_conf *hw_key = tx_info->control.hw_key;
|
||||
|
||||
if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !hw_key)
|
||||
if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !hw_key)
|
||||
return;
|
||||
|
||||
__set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags);
|
||||
@ -80,7 +80,7 @@ unsigned int rt2x00crypto_tx_overhead(struct rt2x00_dev *rt2x00dev,
|
||||
struct ieee80211_key_conf *key = tx_info->control.hw_key;
|
||||
unsigned int overhead = 0;
|
||||
|
||||
if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !key)
|
||||
if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key)
|
||||
return overhead;
|
||||
|
||||
/*
|
||||
|
@ -750,7 +750,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
|
||||
intf, &rt2x00debug_fop_queue_stats);
|
||||
|
||||
#ifdef CONFIG_RT2X00_LIB_CRYPTO
|
||||
if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_hw_crypto(rt2x00dev))
|
||||
intf->crypto_stats_entry =
|
||||
debugfs_create_file("crypto", S_IRUGO, intf->queue_folder,
|
||||
intf, &rt2x00debug_fop_crypto_stats);
|
||||
|
@ -88,7 +88,7 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
|
||||
rt2x00queue_start_queues(rt2x00dev);
|
||||
rt2x00link_start_tuner(rt2x00dev);
|
||||
rt2x00link_start_agc(rt2x00dev);
|
||||
if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
|
||||
rt2x00link_start_vcocal(rt2x00dev);
|
||||
|
||||
/*
|
||||
@ -113,7 +113,7 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
|
||||
* Stop all queues
|
||||
*/
|
||||
rt2x00link_stop_agc(rt2x00dev);
|
||||
if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
|
||||
rt2x00link_stop_vcocal(rt2x00dev);
|
||||
rt2x00link_stop_tuner(rt2x00dev);
|
||||
rt2x00queue_stop_queues(rt2x00dev);
|
||||
@ -234,7 +234,7 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
|
||||
* here as they will fetch the next beacon directly prior to
|
||||
* transmission.
|
||||
*/
|
||||
if (test_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_pre_tbtt_interrupt(rt2x00dev))
|
||||
return;
|
||||
|
||||
/* fetch next beacon */
|
||||
@ -358,7 +358,7 @@ void rt2x00lib_txdone(struct queue_entry *entry,
|
||||
* mac80211 will expect the same data to be present it the
|
||||
* frame as it was passed to us.
|
||||
*/
|
||||
if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_hw_crypto(rt2x00dev))
|
||||
rt2x00crypto_tx_insert_iv(entry->skb, header_length);
|
||||
|
||||
/*
|
||||
|
@ -35,50 +35,28 @@
|
||||
*/
|
||||
#define DEFAULT_RSSI -128
|
||||
|
||||
/*
|
||||
* Helper struct and macro to work with moving/walking averages.
|
||||
* When adding a value to the average value the following calculation
|
||||
* is needed:
|
||||
*
|
||||
* avg_rssi = ((avg_rssi * 7) + rssi) / 8;
|
||||
*
|
||||
* The advantage of this approach is that we only need 1 variable
|
||||
* to store the average in (No need for a count and a total).
|
||||
* But more importantly, normal average values will over time
|
||||
* move less and less towards newly added values this results
|
||||
* that with link tuning, the device can have a very good RSSI
|
||||
* for a few minutes but when the device is moved away from the AP
|
||||
* the average will not decrease fast enough to compensate.
|
||||
* The walking average compensates this and will move towards
|
||||
* the new values correctly allowing a effective link tuning,
|
||||
* the speed of the average moving towards other values depends
|
||||
* on the value for the number of samples. The higher the number
|
||||
* of samples, the slower the average will move.
|
||||
* We use two variables to keep track of the average value to
|
||||
* compensate for the rounding errors. This can be a significant
|
||||
* error (>5dBm) if the factor is too low.
|
||||
*/
|
||||
#define AVG_SAMPLES 8
|
||||
#define AVG_FACTOR 1000
|
||||
#define MOVING_AVERAGE(__avg, __val) \
|
||||
({ \
|
||||
struct avg_val __new; \
|
||||
__new.avg_weight = \
|
||||
(__avg).avg_weight ? \
|
||||
((((__avg).avg_weight * ((AVG_SAMPLES) - 1)) + \
|
||||
((__val) * (AVG_FACTOR))) / \
|
||||
(AVG_SAMPLES)) : \
|
||||
((__val) * (AVG_FACTOR)); \
|
||||
__new.avg = __new.avg_weight / (AVG_FACTOR); \
|
||||
__new; \
|
||||
})
|
||||
/* Constants for EWMA calculations. */
|
||||
#define RT2X00_EWMA_FACTOR 1024
|
||||
#define RT2X00_EWMA_WEIGHT 8
|
||||
|
||||
static inline int rt2x00link_get_avg_rssi(struct ewma *ewma)
|
||||
{
|
||||
unsigned long avg;
|
||||
|
||||
avg = ewma_read(ewma);
|
||||
if (avg)
|
||||
return -avg;
|
||||
|
||||
return DEFAULT_RSSI;
|
||||
}
|
||||
|
||||
static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
struct link_ant *ant = &rt2x00dev->link.ant;
|
||||
|
||||
if (ant->rssi_ant.avg && rt2x00dev->link.qual.rx_success)
|
||||
return ant->rssi_ant.avg;
|
||||
if (rt2x00dev->link.qual.rx_success)
|
||||
return rt2x00link_get_avg_rssi(&ant->rssi_ant);
|
||||
|
||||
return DEFAULT_RSSI;
|
||||
}
|
||||
|
||||
@ -100,8 +78,8 @@ static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev,
|
||||
|
||||
static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
rt2x00dev->link.ant.rssi_ant.avg = 0;
|
||||
rt2x00dev->link.ant.rssi_ant.avg_weight = 0;
|
||||
ewma_init(&rt2x00dev->link.ant.rssi_ant, RT2X00_EWMA_FACTOR,
|
||||
RT2X00_EWMA_WEIGHT);
|
||||
}
|
||||
|
||||
static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev)
|
||||
@ -249,12 +227,12 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev,
|
||||
/*
|
||||
* Update global RSSI
|
||||
*/
|
||||
link->avg_rssi = MOVING_AVERAGE(link->avg_rssi, rxdesc->rssi);
|
||||
ewma_add(&link->avg_rssi, -rxdesc->rssi);
|
||||
|
||||
/*
|
||||
* Update antenna RSSI
|
||||
*/
|
||||
ant->rssi_ant = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi);
|
||||
ewma_add(&ant->rssi_ant, -rxdesc->rssi);
|
||||
}
|
||||
|
||||
void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev)
|
||||
@ -309,6 +287,8 @@ void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna)
|
||||
*/
|
||||
rt2x00dev->link.count = 0;
|
||||
memset(qual, 0, sizeof(*qual));
|
||||
ewma_init(&rt2x00dev->link.avg_rssi, RT2X00_EWMA_FACTOR,
|
||||
RT2X00_EWMA_WEIGHT);
|
||||
|
||||
/*
|
||||
* Restore the VGC level as stored in the registers,
|
||||
@ -363,17 +343,17 @@ static void rt2x00link_tuner(struct work_struct *work)
|
||||
* collect the RSSI data we could use this. Otherwise we
|
||||
* must fallback to the default RSSI value.
|
||||
*/
|
||||
if (!link->avg_rssi.avg || !qual->rx_success)
|
||||
if (!qual->rx_success)
|
||||
qual->rssi = DEFAULT_RSSI;
|
||||
else
|
||||
qual->rssi = link->avg_rssi.avg;
|
||||
qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi);
|
||||
|
||||
/*
|
||||
* Check if link tuning is supported by the hardware, some hardware
|
||||
* do not support link tuning at all, while other devices can disable
|
||||
* the feature from the EEPROM.
|
||||
*/
|
||||
if (test_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_link_tuning(rt2x00dev))
|
||||
rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count);
|
||||
|
||||
/*
|
||||
@ -513,7 +493,7 @@ static void rt2x00link_vcocal(struct work_struct *work)
|
||||
void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
|
||||
{
|
||||
INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc);
|
||||
if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
|
||||
INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal);
|
||||
INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
|
||||
INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);
|
||||
|
@ -382,11 +382,11 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw,
|
||||
* of different types, but has no a separate filter for PS Poll frames,
|
||||
* FIF_CONTROL flag implies FIF_PSPOLL.
|
||||
*/
|
||||
if (!test_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags)) {
|
||||
if (!rt2x00_has_cap_control_filters(rt2x00dev)) {
|
||||
if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL)
|
||||
*total_flags |= FIF_CONTROL | FIF_PSPOLL;
|
||||
}
|
||||
if (!test_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags)) {
|
||||
if (!rt2x00_has_cap_control_filter_pspoll(rt2x00dev)) {
|
||||
if (*total_flags & FIF_CONTROL)
|
||||
*total_flags |= FIF_PSPOLL;
|
||||
}
|
||||
@ -469,7 +469,7 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
|
||||
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
|
||||
return 0;
|
||||
|
||||
if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
|
||||
if (!rt2x00_has_cap_hw_crypto(rt2x00dev))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/*
|
||||
|
@ -119,7 +119,7 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
|
||||
rt2x00dev->ops = ops;
|
||||
rt2x00dev->hw = hw;
|
||||
rt2x00dev->irq = pci_dev->irq;
|
||||
rt2x00dev->name = pci_name(pci_dev);
|
||||
rt2x00dev->name = ops->name;
|
||||
|
||||
if (pci_is_pcie(pci_dev))
|
||||
rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE);
|
||||
|
@ -61,7 +61,7 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp)
|
||||
* at least 8 bytes bytes available in headroom for IV/EIV
|
||||
* and 8 bytes for ICV data as tailroon.
|
||||
*/
|
||||
if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_hw_crypto(rt2x00dev)) {
|
||||
head_size += 8;
|
||||
tail_size += 8;
|
||||
}
|
||||
@ -1033,38 +1033,21 @@ EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue);
|
||||
|
||||
void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)
|
||||
{
|
||||
bool started;
|
||||
bool tx_queue =
|
||||
(queue->qid == QID_AC_VO) ||
|
||||
(queue->qid == QID_AC_VI) ||
|
||||
(queue->qid == QID_AC_BE) ||
|
||||
(queue->qid == QID_AC_BK);
|
||||
|
||||
mutex_lock(&queue->status_lock);
|
||||
|
||||
/*
|
||||
* If the queue has been started, we must stop it temporarily
|
||||
* to prevent any new frames to be queued on the device. If
|
||||
* we are not dropping the pending frames, the queue must
|
||||
* only be stopped in the software and not the hardware,
|
||||
* otherwise the queue will never become empty on its own.
|
||||
* If we are not supposed to drop any pending
|
||||
* frames, this means we must force a start (=kick)
|
||||
* to the queue to make sure the hardware will
|
||||
* start transmitting.
|
||||
*/
|
||||
started = test_bit(QUEUE_STARTED, &queue->flags);
|
||||
if (started) {
|
||||
/*
|
||||
* Pause the queue
|
||||
*/
|
||||
rt2x00queue_pause_queue(queue);
|
||||
|
||||
/*
|
||||
* If we are not supposed to drop any pending
|
||||
* frames, this means we must force a start (=kick)
|
||||
* to the queue to make sure the hardware will
|
||||
* start transmitting.
|
||||
*/
|
||||
if (!drop && tx_queue)
|
||||
queue->rt2x00dev->ops->lib->kick_queue(queue);
|
||||
}
|
||||
if (!drop && tx_queue)
|
||||
queue->rt2x00dev->ops->lib->kick_queue(queue);
|
||||
|
||||
/*
|
||||
* Check if driver supports flushing, if that is the case we can
|
||||
@ -1080,14 +1063,6 @@ void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)
|
||||
if (unlikely(!rt2x00queue_empty(queue)))
|
||||
rt2x00_warn(queue->rt2x00dev, "Queue %d failed to flush\n",
|
||||
queue->qid);
|
||||
|
||||
/*
|
||||
* Restore the queue to the previous status
|
||||
*/
|
||||
if (started)
|
||||
rt2x00queue_unpause_queue(queue);
|
||||
|
||||
mutex_unlock(&queue->status_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue);
|
||||
|
||||
|
@ -523,7 +523,9 @@ static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
|
||||
rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced forced reset\n",
|
||||
queue->qid);
|
||||
|
||||
rt2x00queue_stop_queue(queue);
|
||||
rt2x00queue_flush_queue(queue, true);
|
||||
rt2x00queue_start_queue(queue);
|
||||
}
|
||||
|
||||
static int rt2x00usb_dma_timeout(struct data_queue *queue)
|
||||
|
@ -685,7 +685,7 @@ static void rt61pci_config_antenna_2x(struct rt2x00_dev *rt2x00dev,
|
||||
|
||||
rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529));
|
||||
rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END,
|
||||
!test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags));
|
||||
!rt2x00_has_cap_frame_type(rt2x00dev));
|
||||
|
||||
/*
|
||||
* Configure the RX antenna.
|
||||
@ -813,10 +813,10 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev,
|
||||
|
||||
if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
|
||||
sel = antenna_sel_a;
|
||||
lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags);
|
||||
lna = rt2x00_has_cap_external_lna_a(rt2x00dev);
|
||||
} else {
|
||||
sel = antenna_sel_bg;
|
||||
lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags);
|
||||
lna = rt2x00_has_cap_external_lna_bg(rt2x00dev);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++)
|
||||
@ -836,7 +836,7 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev,
|
||||
else if (rt2x00_rf(rt2x00dev, RF2527))
|
||||
rt61pci_config_antenna_2x(rt2x00dev, ant);
|
||||
else if (rt2x00_rf(rt2x00dev, RF2529)) {
|
||||
if (test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_double_antenna(rt2x00dev))
|
||||
rt61pci_config_antenna_2x(rt2x00dev, ant);
|
||||
else
|
||||
rt61pci_config_antenna_2529(rt2x00dev, ant);
|
||||
@ -850,13 +850,13 @@ static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev,
|
||||
short lna_gain = 0;
|
||||
|
||||
if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) {
|
||||
if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
|
||||
lna_gain += 14;
|
||||
|
||||
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
|
||||
lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1);
|
||||
} else {
|
||||
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_external_lna_a(rt2x00dev))
|
||||
lna_gain += 14;
|
||||
|
||||
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom);
|
||||
@ -1054,14 +1054,14 @@ static void rt61pci_link_tuner(struct rt2x00_dev *rt2x00dev,
|
||||
if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
|
||||
low_bound = 0x28;
|
||||
up_bound = 0x48;
|
||||
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
|
||||
low_bound += 0x10;
|
||||
up_bound += 0x10;
|
||||
}
|
||||
} else {
|
||||
low_bound = 0x20;
|
||||
up_bound = 0x40;
|
||||
if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
|
||||
low_bound += 0x10;
|
||||
up_bound += 0x10;
|
||||
}
|
||||
@ -2578,7 +2578,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
|
||||
* eeprom word.
|
||||
*/
|
||||
if (rt2x00_rf(rt2x00dev, RF2529) &&
|
||||
!test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags)) {
|
||||
!rt2x00_has_cap_double_antenna(rt2x00dev)) {
|
||||
rt2x00dev->default_ant.rx =
|
||||
ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED);
|
||||
rt2x00dev->default_ant.tx =
|
||||
@ -2793,7 +2793,7 @@ static int rt61pci_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
|
||||
spec->supported_bands = SUPPORT_BAND_2GHZ;
|
||||
spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM;
|
||||
|
||||
if (!test_bit(CAPABILITY_RF_SEQUENCE, &rt2x00dev->cap_flags)) {
|
||||
if (!rt2x00_has_cap_rf_sequence(rt2x00dev)) {
|
||||
spec->num_channels = 14;
|
||||
spec->channels = rf_vals_noseq;
|
||||
} else {
|
||||
|
@ -595,8 +595,8 @@ static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
|
||||
switch (ant->rx) {
|
||||
case ANTENNA_HW_DIVERSITY:
|
||||
rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2);
|
||||
temp = !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags)
|
||||
&& (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ);
|
||||
temp = !rt2x00_has_cap_frame_type(rt2x00dev) &&
|
||||
(rt2x00dev->curr_band != IEEE80211_BAND_5GHZ);
|
||||
rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp);
|
||||
break;
|
||||
case ANTENNA_A:
|
||||
@ -636,7 +636,7 @@ static void rt73usb_config_antenna_2x(struct rt2x00_dev *rt2x00dev,
|
||||
|
||||
rt2x00_set_field8(&r3, BBP_R3_SMART_MODE, 0);
|
||||
rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END,
|
||||
!test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags));
|
||||
!rt2x00_has_cap_frame_type(rt2x00dev));
|
||||
|
||||
/*
|
||||
* Configure the RX antenna.
|
||||
@ -709,10 +709,10 @@ static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev,
|
||||
|
||||
if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
|
||||
sel = antenna_sel_a;
|
||||
lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags);
|
||||
lna = rt2x00_has_cap_external_lna_a(rt2x00dev);
|
||||
} else {
|
||||
sel = antenna_sel_bg;
|
||||
lna = test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags);
|
||||
lna = rt2x00_has_cap_external_lna_bg(rt2x00dev);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(antenna_sel_a); i++)
|
||||
@ -740,7 +740,7 @@ static void rt73usb_config_lna_gain(struct rt2x00_dev *rt2x00dev,
|
||||
short lna_gain = 0;
|
||||
|
||||
if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) {
|
||||
if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags))
|
||||
if (rt2x00_has_cap_external_lna_bg(rt2x00dev))
|
||||
lna_gain += 14;
|
||||
|
||||
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
|
||||
@ -930,7 +930,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev,
|
||||
low_bound = 0x28;
|
||||
up_bound = 0x48;
|
||||
|
||||
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
|
||||
low_bound += 0x10;
|
||||
up_bound += 0x10;
|
||||
}
|
||||
@ -946,7 +946,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev,
|
||||
up_bound = 0x1c;
|
||||
}
|
||||
|
||||
if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
|
||||
low_bound += 0x14;
|
||||
up_bound += 0x10;
|
||||
}
|
||||
@ -1661,7 +1661,7 @@ static int rt73usb_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxd_w1)
|
||||
}
|
||||
|
||||
if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
|
||||
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) {
|
||||
if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
|
||||
if (lna == 3 || lna == 2)
|
||||
offset += 10;
|
||||
} else {
|
||||
|
@ -424,8 +424,8 @@ void wl1251_disable_interrupts(struct wl1251 *wl);
|
||||
#define CHIP_ID_1271_PG10 (0x4030101)
|
||||
#define CHIP_ID_1271_PG20 (0x4030111)
|
||||
|
||||
#define WL1251_FW_NAME "wl1251-fw.bin"
|
||||
#define WL1251_NVS_NAME "wl1251-nvs.bin"
|
||||
#define WL1251_FW_NAME "ti-connectivity/wl1251-fw.bin"
|
||||
#define WL1251_NVS_NAME "ti-connectivity/wl1251-nvs.bin"
|
||||
|
||||
#define WL1251_POWER_ON_SLEEP 10 /* in milliseconds */
|
||||
|
||||
|
@ -1391,8 +1391,8 @@ struct ieee80211_vht_operation {
|
||||
#define IEEE80211_VHT_CAP_RXSTBC_MASK 0x00000700
|
||||
#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800
|
||||
#define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000
|
||||
#define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX 0x00006000
|
||||
#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX 0x00030000
|
||||
#define IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX 0x0000e000
|
||||
#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX 0x00070000
|
||||
#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000
|
||||
#define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000
|
||||
#define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000
|
||||
|
@ -436,6 +436,15 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
u32 prohibited_flags);
|
||||
|
||||
/**
|
||||
* cfg80211_chandef_dfs_required - checks if radar detection is required
|
||||
* @wiphy: the wiphy to validate against
|
||||
* @chandef: the channel definition to check
|
||||
* Return: 1 if radar detection is required, 0 if it is not, < 0 on error
|
||||
*/
|
||||
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef);
|
||||
|
||||
/**
|
||||
* ieee80211_chandef_rate_flags - returns rate flags for a channel
|
||||
*
|
||||
|
@ -829,6 +829,15 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info)
|
||||
* @RX_FLAG_STBC_MASK: STBC 2 bit bitmask. 1 - Nss=1, 2 - Nss=2, 3 - Nss=3
|
||||
* @RX_FLAG_10MHZ: 10 MHz (half channel) was used
|
||||
* @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
|
||||
* @RX_FLAG_AMSDU_MORE: Some drivers may prefer to report separate A-MSDU
|
||||
* subframes instead of a one huge frame for performance reasons.
|
||||
* All, but the last MSDU from an A-MSDU should have this flag set. E.g.
|
||||
* if an A-MSDU has 3 frames, the first 2 must have the flag set, while
|
||||
* the 3rd (last) one must not have this flag set. The flag is used to
|
||||
* deal with retransmission/duplication recovery properly since A-MSDU
|
||||
* subframes share the same sequence number. Reported subframes can be
|
||||
* either regular MSDU or singly A-MSDUs. Subframes must not be
|
||||
* interleaved with other frames.
|
||||
*/
|
||||
enum mac80211_rx_flags {
|
||||
RX_FLAG_MMIC_ERROR = BIT(0),
|
||||
@ -859,6 +868,7 @@ enum mac80211_rx_flags {
|
||||
RX_FLAG_STBC_MASK = BIT(26) | BIT(27),
|
||||
RX_FLAG_10MHZ = BIT(28),
|
||||
RX_FLAG_5MHZ = BIT(29),
|
||||
RX_FLAG_AMSDU_MORE = BIT(30),
|
||||
};
|
||||
|
||||
#define RX_FLAG_STBC_SHIFT 26
|
||||
@ -1492,6 +1502,11 @@ struct ieee80211_tx_control {
|
||||
*
|
||||
* @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
|
||||
* only, to allow getting TBTT of a DTIM beacon.
|
||||
*
|
||||
* @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
|
||||
* for a single active channel while using channel contexts. When support
|
||||
* is not enabled the default action is to disconnect when getting the
|
||||
* CSA frame.
|
||||
*/
|
||||
enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
|
||||
@ -1522,6 +1537,7 @@ enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25,
|
||||
IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26,
|
||||
IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27,
|
||||
IEEE80211_HW_CHANCTX_STA_CSA = 1<<28,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2666,6 +2682,10 @@ enum ieee80211_roc_type {
|
||||
* zero using ieee80211_csa_is_complete() after the beacon has been
|
||||
* transmitted and then call ieee80211_csa_finish().
|
||||
*
|
||||
* @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
|
||||
* information in bss_conf is set up and the beacon can be retrieved. A
|
||||
* channel context is bound before this is called.
|
||||
* @leave_ibss: Leave the IBSS again.
|
||||
*/
|
||||
struct ieee80211_ops {
|
||||
void (*tx)(struct ieee80211_hw *hw,
|
||||
@ -2857,6 +2877,9 @@ struct ieee80211_ops {
|
||||
void (*channel_switch_beacon)(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
|
||||
int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -3919,6 +3942,25 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces
|
||||
*
|
||||
* This function iterates over the interfaces associated with a given
|
||||
* hardware that are currently active and calls the callback for them.
|
||||
* This version can only be used while holding the RTNL.
|
||||
*
|
||||
* @hw: the hardware struct of which the interfaces should be iterated over
|
||||
* @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
|
||||
* @iterator: the iterator function to call, cannot sleep
|
||||
* @data: first argument of the iterator function
|
||||
*/
|
||||
void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw,
|
||||
u32 iter_flags,
|
||||
void (*iterator)(void *data,
|
||||
u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data);
|
||||
|
||||
/**
|
||||
* ieee80211_queue_work - add work onto the mac80211 workqueue
|
||||
*
|
||||
|
@ -2865,30 +2865,43 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
return;
|
||||
|
||||
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
|
||||
return;
|
||||
|
||||
sdata->radar_required = sdata->csa_radar_required;
|
||||
err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
|
||||
&changed);
|
||||
if (WARN_ON(err < 0))
|
||||
return;
|
||||
|
||||
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
|
||||
if (err < 0)
|
||||
return;
|
||||
if (!local->use_chanctx) {
|
||||
local->_oper_chandef = local->csa_chandef;
|
||||
ieee80211_hw_config(local, 0);
|
||||
}
|
||||
|
||||
changed |= err;
|
||||
kfree(sdata->u.ap.next_beacon);
|
||||
sdata->u.ap.next_beacon = NULL;
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
|
||||
if (err < 0)
|
||||
return;
|
||||
changed |= err;
|
||||
kfree(sdata->u.ap.next_beacon);
|
||||
sdata->u.ap.next_beacon = NULL;
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, err);
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
ieee80211_ibss_finish_csa(sdata);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
sdata->vif.csa_active = false;
|
||||
|
||||
ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
|
||||
}
|
||||
|
||||
@ -2936,20 +2949,56 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
if (sdata->vif.csa_active)
|
||||
return -EBUSY;
|
||||
|
||||
/* only handle AP for now. */
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
sdata->csa_counter_offset_beacon =
|
||||
params->counter_offset_beacon;
|
||||
sdata->csa_counter_offset_presp = params->counter_offset_presp;
|
||||
sdata->u.ap.next_beacon =
|
||||
cfg80211_beacon_dup(¶ms->beacon_after);
|
||||
if (!sdata->u.ap.next_beacon)
|
||||
return -ENOMEM;
|
||||
|
||||
err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa);
|
||||
if (err < 0) {
|
||||
kfree(sdata->u.ap.next_beacon);
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
if (!sdata->vif.bss_conf.ibss_joined)
|
||||
return -EINVAL;
|
||||
|
||||
if (params->chandef.width != sdata->u.ibss.chandef.width)
|
||||
return -EINVAL;
|
||||
|
||||
switch (params->chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
if (cfg80211_get_chandef_type(¶ms->chandef) !=
|
||||
cfg80211_get_chandef_type(&sdata->u.ibss.chandef))
|
||||
return -EINVAL;
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* changes into another band are not supported */
|
||||
if (sdata->u.ibss.chandef.chan->band !=
|
||||
params->chandef.chan->band)
|
||||
return -EINVAL;
|
||||
|
||||
err = ieee80211_ibss_csa_beacon(sdata, params);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
sdata->u.ap.next_beacon = cfg80211_beacon_dup(¶ms->beacon_after);
|
||||
if (!sdata->u.ap.next_beacon)
|
||||
return -ENOMEM;
|
||||
|
||||
sdata->csa_counter_offset_beacon = params->counter_offset_beacon;
|
||||
sdata->csa_counter_offset_presp = params->counter_offset_presp;
|
||||
sdata->csa_radar_required = params->radar_required;
|
||||
|
||||
if (params->block_tx)
|
||||
@ -2957,10 +3006,6 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
err = ieee80211_assign_beacon(sdata, ¶ms->beacon_csa);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
local->csa_chandef = params->chandef;
|
||||
sdata->vif.csa_active = true;
|
||||
|
||||
@ -3014,7 +3059,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
need_offchan = true;
|
||||
if (!ieee80211_is_action(mgmt->frame_control) ||
|
||||
mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
|
||||
mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED)
|
||||
mgmt->u.action.category == WLAN_CATEGORY_SELF_PROTECTED ||
|
||||
mgmt->u.action.category == WLAN_CATEGORY_SPECTRUM_MGMT)
|
||||
break;
|
||||
rcu_read_lock();
|
||||
sta = sta_info_get(sdata, mgmt->da);
|
||||
|
@ -453,11 +453,6 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
|
||||
chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
|
||||
drv_change_chanctx(local, ctx, chanctx_changed);
|
||||
|
||||
if (!local->use_chanctx) {
|
||||
local->_oper_chandef = *chandef;
|
||||
ieee80211_hw_config(local, 0);
|
||||
}
|
||||
|
||||
ieee80211_recalc_chanctx_chantype(local, ctx);
|
||||
ieee80211_recalc_smps_chanctx(local, ctx);
|
||||
ieee80211_recalc_radar_chanctx(local, ctx);
|
||||
|
@ -103,54 +103,57 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
|
||||
sf += scnprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
|
||||
if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
|
||||
sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
|
||||
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)
|
||||
sf += snprintf(buf + sf, mxln - sf,
|
||||
"HOST_BCAST_PS_BUFFERING\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"HOST_BCAST_PS_BUFFERING\n");
|
||||
if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)
|
||||
sf += snprintf(buf + sf, mxln - sf,
|
||||
"2GHZ_SHORT_SLOT_INCAPABLE\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"2GHZ_SHORT_SLOT_INCAPABLE\n");
|
||||
if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)
|
||||
sf += snprintf(buf + sf, mxln - sf,
|
||||
"2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
|
||||
if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC)
|
||||
sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_BEFORE_ASSOC\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"NEED_DTIM_BEFORE_ASSOC\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
|
||||
if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
|
||||
sf += snprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
|
||||
sf += snprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
|
||||
sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_SMPS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"SUPPORTS_DYNAMIC_SMPS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
|
||||
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf,
|
||||
"REPORTS_TX_ACK_STATUS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
|
||||
sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
|
||||
if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)
|
||||
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
|
||||
if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
|
||||
sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
|
||||
if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)
|
||||
sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");
|
||||
sf += scnprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");
|
||||
|
||||
rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
|
||||
kfree(buf);
|
||||
|
@ -1085,4 +1085,31 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
}
|
||||
|
||||
static inline int drv_join_ibss(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
might_sleep();
|
||||
check_sdata_in_driver(sdata);
|
||||
|
||||
trace_drv_join_ibss(local, sdata, &sdata->vif.bss_conf);
|
||||
if (local->ops->join_ibss)
|
||||
ret = local->ops->join_ibss(&local->hw, &sdata->vif);
|
||||
trace_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void drv_leave_ibss(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
might_sleep();
|
||||
check_sdata_in_driver(sdata);
|
||||
|
||||
trace_drv_leave_ibss(local, sdata);
|
||||
if (local->ops->leave_ibss)
|
||||
local->ops->leave_ibss(&local->hw, &sdata->vif);
|
||||
trace_drv_return_void(local);
|
||||
}
|
||||
|
||||
#endif /* __MAC80211_DRIVER_OPS */
|
||||
|
@ -39,7 +39,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
|
||||
const int beacon_int, const u32 basic_rates,
|
||||
const u16 capability, u64 tsf,
|
||||
struct cfg80211_chan_def *chandef,
|
||||
bool *have_higher_than_11mbit)
|
||||
bool *have_higher_than_11mbit,
|
||||
struct cfg80211_csa_settings *csa_settings)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
@ -59,6 +60,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
|
||||
2 + 8 /* max Supported Rates */ +
|
||||
3 /* max DS params */ +
|
||||
4 /* IBSS params */ +
|
||||
5 /* Channel Switch Announcement */ +
|
||||
2 + (IEEE80211_MAX_SUPP_RATES - 8) +
|
||||
2 + sizeof(struct ieee80211_ht_cap) +
|
||||
2 + sizeof(struct ieee80211_ht_operation) +
|
||||
@ -135,6 +137,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
|
||||
*pos++ = 0;
|
||||
*pos++ = 0;
|
||||
|
||||
if (csa_settings) {
|
||||
*pos++ = WLAN_EID_CHANNEL_SWITCH;
|
||||
*pos++ = 3;
|
||||
*pos++ = csa_settings->block_tx ? 1 : 0;
|
||||
*pos++ = ieee80211_frequency_to_channel(
|
||||
csa_settings->chandef.chan->center_freq);
|
||||
sdata->csa_counter_offset_beacon = (pos - presp->head);
|
||||
*pos++ = csa_settings->count;
|
||||
}
|
||||
|
||||
/* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
|
||||
if (rates_n > 8) {
|
||||
*pos++ = WLAN_EID_EXT_SUPP_RATES;
|
||||
@ -217,6 +229,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
struct beacon_data *presp;
|
||||
enum nl80211_bss_scan_width scan_width;
|
||||
bool have_higher_than_11mbit;
|
||||
int err;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
@ -235,6 +248,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_bss_info_change_notify(sdata,
|
||||
BSS_CHANGED_IBSS |
|
||||
BSS_CHANGED_BEACON_ENABLED);
|
||||
drv_leave_ibss(local, sdata);
|
||||
}
|
||||
|
||||
presp = rcu_dereference_protected(ifibss->presp,
|
||||
@ -276,7 +290,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
|
||||
capability, tsf, &chandef,
|
||||
&have_higher_than_11mbit);
|
||||
&have_higher_than_11mbit, NULL);
|
||||
if (!presp)
|
||||
return;
|
||||
|
||||
@ -317,11 +331,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
else
|
||||
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
|
||||
|
||||
ieee80211_set_wmm_default(sdata, true);
|
||||
|
||||
sdata->vif.bss_conf.ibss_joined = true;
|
||||
sdata->vif.bss_conf.ibss_creator = creator;
|
||||
ieee80211_bss_info_change_notify(sdata, bss_change);
|
||||
|
||||
ieee80211_set_wmm_default(sdata, true);
|
||||
err = drv_join_ibss(local, sdata);
|
||||
if (err) {
|
||||
sdata->vif.bss_conf.ibss_joined = false;
|
||||
sdata->vif.bss_conf.ibss_creator = false;
|
||||
sdata->vif.bss_conf.enable_beacon = false;
|
||||
sdata->vif.bss_conf.ssid_len = 0;
|
||||
RCU_INIT_POINTER(ifibss->presp, NULL);
|
||||
kfree_rcu(presp, rcu_head);
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n",
|
||||
err);
|
||||
return;
|
||||
}
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, bss_change);
|
||||
|
||||
ifibss->state = IEEE80211_IBSS_MLME_JOINED;
|
||||
mod_timer(&ifibss->timer,
|
||||
@ -416,6 +445,169 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
tsf, false);
|
||||
}
|
||||
|
||||
static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int freq;
|
||||
int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
|
||||
sizeof(mgmt->u.action.u.chan_switch);
|
||||
u8 *pos;
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom + hdr_len +
|
||||
5 + /* channel switch announcement element */
|
||||
3); /* secondary channel offset element */
|
||||
if (!skb)
|
||||
return -1;
|
||||
|
||||
skb_reserve(skb, local->tx_headroom);
|
||||
mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
|
||||
memset(mgmt, 0, hdr_len);
|
||||
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
||||
IEEE80211_STYPE_ACTION);
|
||||
|
||||
eth_broadcast_addr(mgmt->da);
|
||||
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
|
||||
mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
|
||||
mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
|
||||
pos = skb_put(skb, 5);
|
||||
*pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */
|
||||
*pos++ = 3; /* IE length */
|
||||
*pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */
|
||||
freq = csa_settings->chandef.chan->center_freq;
|
||||
*pos++ = ieee80211_frequency_to_channel(freq); /* channel */
|
||||
*pos++ = csa_settings->count; /* count */
|
||||
|
||||
if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
|
||||
enum nl80211_channel_type ch_type;
|
||||
|
||||
skb_put(skb, 3);
|
||||
*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */
|
||||
*pos++ = 1; /* IE length */
|
||||
ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
|
||||
if (ch_type == NL80211_CHAN_HT40PLUS)
|
||||
*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
|
||||
else
|
||||
*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
|
||||
}
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct beacon_data *presp, *old_presp;
|
||||
struct cfg80211_bss *cbss;
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
u16 capability;
|
||||
u64 tsf;
|
||||
int ret = 0;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
capability = WLAN_CAPABILITY_IBSS;
|
||||
|
||||
if (ifibss->privacy)
|
||||
capability |= WLAN_CAPABILITY_PRIVACY;
|
||||
|
||||
cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan,
|
||||
ifibss->bssid, ifibss->ssid,
|
||||
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
|
||||
WLAN_CAPABILITY_PRIVACY,
|
||||
capability);
|
||||
|
||||
if (WARN_ON(!cbss)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
ies = rcu_dereference(cbss->ies);
|
||||
tsf = ies->tsf;
|
||||
rcu_read_unlock();
|
||||
cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
|
||||
|
||||
old_presp = rcu_dereference_protected(ifibss->presp,
|
||||
lockdep_is_held(&sdata->wdev.mtx));
|
||||
|
||||
presp = ieee80211_ibss_build_presp(sdata,
|
||||
sdata->vif.bss_conf.beacon_int,
|
||||
sdata->vif.bss_conf.basic_rates,
|
||||
capability, tsf, &ifibss->chandef,
|
||||
NULL, csa_settings);
|
||||
if (!presp) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rcu_assign_pointer(ifibss->presp, presp);
|
||||
if (old_presp)
|
||||
kfree_rcu(old_presp, rcu_head);
|
||||
|
||||
/* it might not send the beacon for a while. send an action frame
|
||||
* immediately to announce the channel switch.
|
||||
*/
|
||||
if (csa_settings)
|
||||
ieee80211_send_action_csa(sdata, csa_settings);
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct cfg80211_bss *cbss;
|
||||
int err;
|
||||
u16 capability;
|
||||
|
||||
sdata_lock(sdata);
|
||||
/* update cfg80211 bss information with the new channel */
|
||||
if (!is_zero_ether_addr(ifibss->bssid)) {
|
||||
capability = WLAN_CAPABILITY_IBSS;
|
||||
|
||||
if (ifibss->privacy)
|
||||
capability |= WLAN_CAPABILITY_PRIVACY;
|
||||
|
||||
cbss = cfg80211_get_bss(sdata->local->hw.wiphy,
|
||||
ifibss->chandef.chan,
|
||||
ifibss->bssid, ifibss->ssid,
|
||||
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
|
||||
WLAN_CAPABILITY_PRIVACY,
|
||||
capability);
|
||||
/* XXX: should not really modify cfg80211 data */
|
||||
if (cbss) {
|
||||
cbss->channel = sdata->local->csa_chandef.chan;
|
||||
cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
|
||||
}
|
||||
}
|
||||
|
||||
ifibss->chandef = sdata->local->csa_chandef;
|
||||
|
||||
/* generate the beacon */
|
||||
err = ieee80211_ibss_csa_beacon(sdata, NULL);
|
||||
sdata_unlock(sdata);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
|
||||
cancel_work_sync(&ifibss->csa_connection_drop_work);
|
||||
}
|
||||
|
||||
static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
|
||||
__acquires(RCU)
|
||||
{
|
||||
@ -499,6 +691,295 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
|
||||
return ieee80211_ibss_finish_sta(sta);
|
||||
}
|
||||
|
||||
static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int active = 0;
|
||||
struct sta_info *sta;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
if (sta->sdata == sdata &&
|
||||
time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
|
||||
jiffies)) {
|
||||
active++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct cfg80211_bss *cbss;
|
||||
struct beacon_data *presp;
|
||||
struct sta_info *sta;
|
||||
int active_ibss;
|
||||
u16 capability;
|
||||
|
||||
active_ibss = ieee80211_sta_active_ibss(sdata);
|
||||
|
||||
if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
|
||||
capability = WLAN_CAPABILITY_IBSS;
|
||||
|
||||
if (ifibss->privacy)
|
||||
capability |= WLAN_CAPABILITY_PRIVACY;
|
||||
|
||||
cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
|
||||
ifibss->bssid, ifibss->ssid,
|
||||
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
|
||||
WLAN_CAPABILITY_PRIVACY,
|
||||
capability);
|
||||
|
||||
if (cbss) {
|
||||
cfg80211_unlink_bss(local->hw.wiphy, cbss);
|
||||
cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
|
||||
}
|
||||
}
|
||||
|
||||
ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
|
||||
|
||||
sta_info_flush(sdata);
|
||||
|
||||
spin_lock_bh(&ifibss->incomplete_lock);
|
||||
while (!list_empty(&ifibss->incomplete_stations)) {
|
||||
sta = list_first_entry(&ifibss->incomplete_stations,
|
||||
struct sta_info, list);
|
||||
list_del(&sta->list);
|
||||
spin_unlock_bh(&ifibss->incomplete_lock);
|
||||
|
||||
sta_info_free(local, sta);
|
||||
spin_lock_bh(&ifibss->incomplete_lock);
|
||||
}
|
||||
spin_unlock_bh(&ifibss->incomplete_lock);
|
||||
|
||||
netif_carrier_off(sdata->dev);
|
||||
|
||||
sdata->vif.bss_conf.ibss_joined = false;
|
||||
sdata->vif.bss_conf.ibss_creator = false;
|
||||
sdata->vif.bss_conf.enable_beacon = false;
|
||||
sdata->vif.bss_conf.ssid_len = 0;
|
||||
|
||||
/* remove beacon */
|
||||
presp = rcu_dereference_protected(ifibss->presp,
|
||||
lockdep_is_held(&sdata->wdev.mtx));
|
||||
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
|
||||
if (presp)
|
||||
kfree_rcu(presp, rcu_head);
|
||||
|
||||
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
|
||||
BSS_CHANGED_IBSS);
|
||||
drv_leave_ibss(local, sdata);
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
}
|
||||
|
||||
static void ieee80211_csa_connection_drop_work(struct work_struct *work)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata =
|
||||
container_of(work, struct ieee80211_sub_if_data,
|
||||
u.ibss.csa_connection_drop_work);
|
||||
|
||||
ieee80211_ibss_disconnect(sdata);
|
||||
synchronize_rcu();
|
||||
skb_queue_purge(&sdata->skb_queue);
|
||||
|
||||
/* trigger a scan to find another IBSS network to join */
|
||||
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
|
||||
}
|
||||
|
||||
static bool
|
||||
ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems,
|
||||
bool beacon)
|
||||
{
|
||||
struct cfg80211_csa_settings params;
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
enum nl80211_channel_type ch_type;
|
||||
int err, num_chanctx;
|
||||
u32 sta_flags;
|
||||
u8 mode;
|
||||
|
||||
if (sdata->vif.csa_active)
|
||||
return true;
|
||||
|
||||
if (!sdata->vif.bss_conf.ibss_joined)
|
||||
return false;
|
||||
|
||||
sta_flags = IEEE80211_STA_DISABLE_VHT;
|
||||
switch (ifibss->chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
sta_flags |= IEEE80211_STA_DISABLE_HT;
|
||||
/* fall through */
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
|
||||
ifibss->chandef.chan->band,
|
||||
sta_flags, ifibss->bssid,
|
||||
¶ms.count, &mode,
|
||||
¶ms.chandef);
|
||||
|
||||
/* can't switch to destination channel, fail */
|
||||
if (err < 0)
|
||||
goto disconnect;
|
||||
|
||||
/* did not contain a CSA */
|
||||
if (err)
|
||||
return false;
|
||||
|
||||
if (ifibss->chandef.chan->band != params.chandef.chan->band)
|
||||
goto disconnect;
|
||||
|
||||
switch (ifibss->chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
/* keep our current HT mode (HT20/HT40+/HT40-), even if
|
||||
* another mode has been announced. The mode is not adopted
|
||||
* within the beacon while doing CSA and we should therefore
|
||||
* keep the mode which we announce.
|
||||
*/
|
||||
ch_type = cfg80211_get_chandef_type(&ifibss->chandef);
|
||||
cfg80211_chandef_create(¶ms.chandef, params.chandef.chan,
|
||||
ch_type);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
if (params.chandef.width != ifibss->chandef.width) {
|
||||
sdata_info(sdata,
|
||||
"IBSS %pM received channel switch from incompatible channel width (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
||||
ifibss->bssid,
|
||||
params.chandef.chan->center_freq,
|
||||
params.chandef.width,
|
||||
params.chandef.center_freq1,
|
||||
params.chandef.center_freq2);
|
||||
goto disconnect;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* should not happen, sta_flags should prevent VHT modes. */
|
||||
WARN_ON(1);
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, ¶ms.chandef,
|
||||
IEEE80211_CHAN_DISABLED)) {
|
||||
sdata_info(sdata,
|
||||
"IBSS %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
||||
ifibss->bssid,
|
||||
params.chandef.chan->center_freq,
|
||||
params.chandef.width,
|
||||
params.chandef.center_freq1,
|
||||
params.chandef.center_freq2);
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
err = cfg80211_chandef_dfs_required(sdata->local->hw.wiphy,
|
||||
¶ms.chandef);
|
||||
if (err < 0)
|
||||
goto disconnect;
|
||||
if (err) {
|
||||
params.radar_required = true;
|
||||
|
||||
/* TODO: IBSS-DFS not (yet) supported, disconnect. */
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
|
||||
if (!chanctx_conf) {
|
||||
rcu_read_unlock();
|
||||
goto disconnect;
|
||||
}
|
||||
|
||||
/* don't handle for multi-VIF cases */
|
||||
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
|
||||
if (chanctx->refcount > 1) {
|
||||
rcu_read_unlock();
|
||||
goto disconnect;
|
||||
}
|
||||
num_chanctx = 0;
|
||||
list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
|
||||
num_chanctx++;
|
||||
|
||||
if (num_chanctx > 1) {
|
||||
rcu_read_unlock();
|
||||
goto disconnect;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
/* all checks done, now perform the channel switch. */
|
||||
ibss_dbg(sdata,
|
||||
"received channel switch announcement to go to channel %d MHz\n",
|
||||
params.chandef.chan->center_freq);
|
||||
|
||||
params.block_tx = !!mode;
|
||||
|
||||
ieee80211_ibss_csa_beacon(sdata, ¶ms);
|
||||
sdata->csa_radar_required = params.radar_required;
|
||||
|
||||
if (params.block_tx)
|
||||
ieee80211_stop_queues_by_reason(&sdata->local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
sdata->local->csa_chandef = params.chandef;
|
||||
sdata->vif.csa_active = true;
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, err);
|
||||
drv_channel_switch_beacon(sdata, ¶ms.chandef);
|
||||
|
||||
return true;
|
||||
disconnect:
|
||||
ibss_dbg(sdata, "Can't handle channel switch, disconnect\n");
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&ifibss->csa_connection_drop_work);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt, size_t len,
|
||||
struct ieee80211_rx_status *rx_status,
|
||||
struct ieee802_11_elems *elems)
|
||||
{
|
||||
int required_len;
|
||||
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
|
||||
return;
|
||||
|
||||
/* CSA is the only action we handle for now */
|
||||
if (mgmt->u.action.u.measurement.action_code !=
|
||||
WLAN_ACTION_SPCT_CHL_SWITCH)
|
||||
return;
|
||||
|
||||
required_len = IEEE80211_MIN_ACTION_SIZE +
|
||||
sizeof(mgmt->u.action.u.chan_switch);
|
||||
if (len < required_len)
|
||||
return;
|
||||
|
||||
ieee80211_ibss_process_chanswitch(sdata, elems, false);
|
||||
}
|
||||
|
||||
static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
@ -661,10 +1142,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
||||
|
||||
/* check if we need to merge IBSS */
|
||||
|
||||
/* we use a fixed BSSID */
|
||||
if (sdata->u.ibss.fixed_bssid)
|
||||
goto put_bss;
|
||||
|
||||
/* not an IBSS */
|
||||
if (!(cbss->capability & WLAN_CAPABILITY_IBSS))
|
||||
goto put_bss;
|
||||
@ -680,10 +1157,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
||||
sdata->u.ibss.ssid_len))
|
||||
goto put_bss;
|
||||
|
||||
/* process channel switch */
|
||||
if (ieee80211_ibss_process_chanswitch(sdata, elems, true))
|
||||
goto put_bss;
|
||||
|
||||
/* same BSSID */
|
||||
if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))
|
||||
goto put_bss;
|
||||
|
||||
/* we use a fixed BSSID */
|
||||
if (sdata->u.ibss.fixed_bssid)
|
||||
goto put_bss;
|
||||
|
||||
if (ieee80211_have_rx_timestamp(rx_status)) {
|
||||
/* time when timestamp field was received */
|
||||
rx_timestamp =
|
||||
@ -775,30 +1260,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
|
||||
ieee80211_queue_work(&local->hw, &sdata->work);
|
||||
}
|
||||
|
||||
static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
int active = 0;
|
||||
struct sta_info *sta;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
if (sta->sdata == sdata &&
|
||||
time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
|
||||
jiffies)) {
|
||||
active++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
@ -1076,6 +1537,8 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_rx_status *rx_status;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
u16 fc;
|
||||
struct ieee802_11_elems elems;
|
||||
int ies_len;
|
||||
|
||||
rx_status = IEEE80211_SKB_RXCB(skb);
|
||||
mgmt = (struct ieee80211_mgmt *) skb->data;
|
||||
@ -1101,6 +1564,27 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
case IEEE80211_STYPE_DEAUTH:
|
||||
ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
|
||||
break;
|
||||
case IEEE80211_STYPE_ACTION:
|
||||
switch (mgmt->u.action.category) {
|
||||
case WLAN_CATEGORY_SPECTRUM_MGMT:
|
||||
ies_len = skb->len -
|
||||
offsetof(struct ieee80211_mgmt,
|
||||
u.action.u.chan_switch.variable);
|
||||
|
||||
if (ies_len < 0)
|
||||
break;
|
||||
|
||||
ieee802_11_parse_elems(
|
||||
mgmt->u.action.u.chan_switch.variable,
|
||||
ies_len, true, &elems);
|
||||
|
||||
if (elems.parse_error)
|
||||
break;
|
||||
|
||||
ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len,
|
||||
rx_status, &elems);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mgmt_out:
|
||||
@ -1167,6 +1651,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
|
||||
(unsigned long) sdata);
|
||||
INIT_LIST_HEAD(&ifibss->incomplete_stations);
|
||||
spin_lock_init(&ifibss->incomplete_lock);
|
||||
INIT_WORK(&ifibss->csa_connection_drop_work,
|
||||
ieee80211_csa_connection_drop_work);
|
||||
}
|
||||
|
||||
/* scan finished notification */
|
||||
@ -1265,73 +1751,19 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
|
||||
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct cfg80211_bss *cbss;
|
||||
u16 capability;
|
||||
int active_ibss;
|
||||
struct sta_info *sta;
|
||||
struct beacon_data *presp;
|
||||
|
||||
active_ibss = ieee80211_sta_active_ibss(sdata);
|
||||
|
||||
if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
|
||||
capability = WLAN_CAPABILITY_IBSS;
|
||||
|
||||
if (ifibss->privacy)
|
||||
capability |= WLAN_CAPABILITY_PRIVACY;
|
||||
|
||||
cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
|
||||
ifibss->bssid, ifibss->ssid,
|
||||
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
|
||||
WLAN_CAPABILITY_PRIVACY,
|
||||
capability);
|
||||
|
||||
if (cbss) {
|
||||
cfg80211_unlink_bss(local->hw.wiphy, cbss);
|
||||
cfg80211_put_bss(local->hw.wiphy, cbss);
|
||||
}
|
||||
}
|
||||
|
||||
ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
|
||||
memset(ifibss->bssid, 0, ETH_ALEN);
|
||||
ieee80211_ibss_disconnect(sdata);
|
||||
ifibss->ssid_len = 0;
|
||||
|
||||
sta_info_flush(sdata);
|
||||
|
||||
spin_lock_bh(&ifibss->incomplete_lock);
|
||||
while (!list_empty(&ifibss->incomplete_stations)) {
|
||||
sta = list_first_entry(&ifibss->incomplete_stations,
|
||||
struct sta_info, list);
|
||||
list_del(&sta->list);
|
||||
spin_unlock_bh(&ifibss->incomplete_lock);
|
||||
|
||||
sta_info_free(local, sta);
|
||||
spin_lock_bh(&ifibss->incomplete_lock);
|
||||
}
|
||||
spin_unlock_bh(&ifibss->incomplete_lock);
|
||||
|
||||
netif_carrier_off(sdata->dev);
|
||||
memset(ifibss->bssid, 0, ETH_ALEN);
|
||||
|
||||
/* remove beacon */
|
||||
kfree(sdata->u.ibss.ie);
|
||||
presp = rcu_dereference_protected(ifibss->presp,
|
||||
lockdep_is_held(&sdata->wdev.mtx));
|
||||
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
|
||||
|
||||
/* on the next join, re-program HT parameters */
|
||||
memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
|
||||
memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
|
||||
|
||||
sdata->vif.bss_conf.ibss_joined = false;
|
||||
sdata->vif.bss_conf.ibss_creator = false;
|
||||
sdata->vif.bss_conf.enable_beacon = false;
|
||||
sdata->vif.bss_conf.ssid_len = 0;
|
||||
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
|
||||
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
|
||||
BSS_CHANGED_IBSS);
|
||||
ieee80211_vif_release_channel(sdata);
|
||||
synchronize_rcu();
|
||||
kfree(presp);
|
||||
|
||||
skb_queue_purge(&sdata->skb_queue);
|
||||
|
||||
|
@ -322,7 +322,6 @@ struct ieee80211_roc_work {
|
||||
|
||||
/* flags used in struct ieee80211_if_managed.flags */
|
||||
enum ieee80211_sta_flags {
|
||||
IEEE80211_STA_BEACON_POLL = BIT(0),
|
||||
IEEE80211_STA_CONNECTION_POLL = BIT(1),
|
||||
IEEE80211_STA_CONTROL_PORT = BIT(2),
|
||||
IEEE80211_STA_DISABLE_HT = BIT(4),
|
||||
@ -487,6 +486,7 @@ struct ieee80211_if_managed {
|
||||
|
||||
struct ieee80211_if_ibss {
|
||||
struct timer_list timer;
|
||||
struct work_struct csa_connection_drop_work;
|
||||
|
||||
unsigned long last_scan_completed;
|
||||
|
||||
@ -1330,6 +1330,10 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb);
|
||||
int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_csa_settings *csa_settings);
|
||||
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
|
||||
|
||||
/* mesh code */
|
||||
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
|
||||
@ -1481,6 +1485,29 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
|
||||
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt,
|
||||
size_t len);
|
||||
/**
|
||||
* ieee80211_parse_ch_switch_ie - parses channel switch IEs
|
||||
* @sdata: the sdata of the interface which has received the frame
|
||||
* @elems: parsed 802.11 elements received with the frame
|
||||
* @beacon: indicates if the frame was a beacon or probe response
|
||||
* @current_band: indicates the current band
|
||||
* @sta_flags: contains information about own capabilities and restrictions
|
||||
* to decide which channel switch announcements can be accepted. Only the
|
||||
* following subset of &enum ieee80211_sta_flags are evaluated:
|
||||
* %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
|
||||
* %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
|
||||
* %IEEE80211_STA_DISABLE_160MHZ.
|
||||
* @count: to be filled with the counter until the switch (on success only)
|
||||
* @bssid: the currently connected bssid (for reporting)
|
||||
* @mode: to be filled with CSA mode (on success only)
|
||||
* @new_chandef: to be filled with destination chandef (on success only)
|
||||
* Return: 0 on success, <0 on error and >0 if there is nothing to parse.
|
||||
*/
|
||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems, bool beacon,
|
||||
enum ieee80211_band current_band,
|
||||
u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
|
||||
struct cfg80211_chan_def *new_chandef);
|
||||
|
||||
/* Suspend/resume and hw reconfiguration */
|
||||
int ieee80211_reconfig(struct ieee80211_local *local);
|
||||
@ -1654,6 +1681,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
|
||||
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
|
||||
const struct ieee80211_ht_operation *ht_oper,
|
||||
struct cfg80211_chan_def *chandef);
|
||||
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
|
||||
|
||||
int __must_check
|
||||
ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
|
||||
|
@ -766,6 +766,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
ieee80211_mgd_stop(sdata);
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
ieee80211_ibss_stop(sdata);
|
||||
|
||||
|
||||
/*
|
||||
* Remove all stations associated with this interface.
|
||||
*
|
||||
|
@ -879,7 +879,7 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
|
||||
keyconf->keylen, keyconf->key,
|
||||
0, NULL);
|
||||
if (IS_ERR(key))
|
||||
return ERR_PTR(PTR_ERR(key));
|
||||
return ERR_CAST(key);
|
||||
|
||||
if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
|
||||
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;
|
||||
|
@ -145,66 +145,6 @@ static int ecw2cw(int ecw)
|
||||
return (1 << ecw) - 1;
|
||||
}
|
||||
|
||||
static u32 chandef_downgrade(struct cfg80211_chan_def *c)
|
||||
{
|
||||
u32 ret;
|
||||
int tmp;
|
||||
|
||||
switch (c->width) {
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
c->width = NL80211_CHAN_WIDTH_20;
|
||||
c->center_freq1 = c->chan->center_freq;
|
||||
ret = IEEE80211_STA_DISABLE_40MHZ |
|
||||
IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P40 */
|
||||
tmp /= 2;
|
||||
/* freq_P40 */
|
||||
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
|
||||
c->width = NL80211_CHAN_WIDTH_40;
|
||||
ret = IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
c->center_freq2 = 0;
|
||||
c->width = NL80211_CHAN_WIDTH_80;
|
||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
/* n_P20 */
|
||||
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P80 */
|
||||
tmp /= 4;
|
||||
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
|
||||
c->width = NL80211_CHAN_WIDTH_80;
|
||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ;
|
||||
break;
|
||||
default:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
WARN_ON_ONCE(1);
|
||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
WARN_ON_ONCE(1);
|
||||
/* keep c->width */
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32
|
||||
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_supported_band *sband,
|
||||
@ -352,7 +292,7 @@ out:
|
||||
break;
|
||||
}
|
||||
|
||||
ret |= chandef_downgrade(chandef);
|
||||
ret |= ieee80211_chandef_downgrade(chandef);
|
||||
}
|
||||
|
||||
if (chandef->width != vht_chandef.width && !tracking)
|
||||
@ -406,13 +346,13 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
|
||||
*/
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
||||
chandef.width == NL80211_CHAN_WIDTH_80P80)
|
||||
flags |= chandef_downgrade(&chandef);
|
||||
flags |= ieee80211_chandef_downgrade(&chandef);
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
|
||||
chandef.width == NL80211_CHAN_WIDTH_160)
|
||||
flags |= chandef_downgrade(&chandef);
|
||||
flags |= ieee80211_chandef_downgrade(&chandef);
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
|
||||
chandef.width > NL80211_CHAN_WIDTH_20)
|
||||
flags |= chandef_downgrade(&chandef);
|
||||
flags |= ieee80211_chandef_downgrade(&chandef);
|
||||
|
||||
if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
|
||||
return 0;
|
||||
@ -893,8 +833,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
|
||||
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
|
||||
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
|
||||
|
||||
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL))
|
||||
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
||||
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
|
||||
|
||||
ieee80211_tx_skb(sdata, skb);
|
||||
@ -937,6 +876,8 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
||||
container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
u32 changed = 0;
|
||||
int ret;
|
||||
|
||||
if (!ieee80211_sdata_running(sdata))
|
||||
return;
|
||||
@ -945,24 +886,39 @@ static void ieee80211_chswitch_work(struct work_struct *work)
|
||||
if (!ifmgd->associated)
|
||||
goto out;
|
||||
|
||||
local->_oper_chandef = local->csa_chandef;
|
||||
ret = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
|
||||
&changed);
|
||||
if (ret) {
|
||||
sdata_info(sdata,
|
||||
"vif channel switch failed, disconnecting\n");
|
||||
ieee80211_queue_work(&sdata->local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!local->ops->channel_switch) {
|
||||
/* call "hw_config" only if doing sw channel switch */
|
||||
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
|
||||
} else {
|
||||
/* update the device channel directly */
|
||||
local->hw.conf.chandef = local->_oper_chandef;
|
||||
if (!local->use_chanctx) {
|
||||
local->_oper_chandef = local->csa_chandef;
|
||||
/* Call "hw_config" only if doing sw channel switch.
|
||||
* Otherwise update the channel directly
|
||||
*/
|
||||
if (!local->ops->channel_switch)
|
||||
ieee80211_hw_config(local, 0);
|
||||
else
|
||||
local->hw.conf.chandef = local->_oper_chandef;
|
||||
}
|
||||
|
||||
/* XXX: shouldn't really modify cfg80211-owned data! */
|
||||
ifmgd->associated->channel = local->_oper_chandef.chan;
|
||||
ifmgd->associated->channel = local->csa_chandef.chan;
|
||||
|
||||
/* XXX: wait for a beacon first? */
|
||||
ieee80211_wake_queues_by_reason(&local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
|
||||
ieee80211_bss_info_change_notify(sdata, changed);
|
||||
|
||||
out:
|
||||
sdata->vif.csa_active = false;
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
sdata_unlock(sdata);
|
||||
}
|
||||
@ -1000,20 +956,12 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
||||
struct cfg80211_bss *cbss = ifmgd->associated;
|
||||
struct ieee80211_bss *bss;
|
||||
struct ieee80211_chanctx *chanctx;
|
||||
enum ieee80211_band new_band;
|
||||
int new_freq;
|
||||
u8 new_chan_no;
|
||||
enum ieee80211_band current_band;
|
||||
u8 count;
|
||||
u8 mode;
|
||||
struct ieee80211_channel *new_chan;
|
||||
struct cfg80211_chan_def new_chandef = {};
|
||||
struct cfg80211_chan_def new_vht_chandef = {};
|
||||
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
|
||||
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
|
||||
const struct ieee80211_ht_operation *ht_oper;
|
||||
int secondary_channel_offset = -1;
|
||||
int res;
|
||||
|
||||
sdata_assert_lock(sdata);
|
||||
|
||||
@ -1027,162 +975,23 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
|
||||
return;
|
||||
|
||||
sec_chan_offs = elems->sec_chan_offs;
|
||||
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
|
||||
ht_oper = elems->ht_operation;
|
||||
|
||||
if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
|
||||
IEEE80211_STA_DISABLE_40MHZ)) {
|
||||
sec_chan_offs = NULL;
|
||||
wide_bw_chansw_ie = NULL;
|
||||
/* only used for bandwidth here */
|
||||
ht_oper = NULL;
|
||||
}
|
||||
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
|
||||
wide_bw_chansw_ie = NULL;
|
||||
|
||||
if (elems->ext_chansw_ie) {
|
||||
if (!ieee80211_operating_class_to_band(
|
||||
elems->ext_chansw_ie->new_operating_class,
|
||||
&new_band)) {
|
||||
sdata_info(sdata,
|
||||
"cannot understand ECSA IE operating class %d, disconnecting\n",
|
||||
elems->ext_chansw_ie->new_operating_class);
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
}
|
||||
new_chan_no = elems->ext_chansw_ie->new_ch_num;
|
||||
count = elems->ext_chansw_ie->count;
|
||||
mode = elems->ext_chansw_ie->mode;
|
||||
} else if (elems->ch_switch_ie) {
|
||||
new_band = cbss->channel->band;
|
||||
new_chan_no = elems->ch_switch_ie->new_ch_num;
|
||||
count = elems->ch_switch_ie->count;
|
||||
mode = elems->ch_switch_ie->mode;
|
||||
} else {
|
||||
/* nothing here we understand */
|
||||
return;
|
||||
}
|
||||
|
||||
bss = (void *)cbss->priv;
|
||||
|
||||
new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
|
||||
new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
|
||||
if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
|
||||
sdata_info(sdata,
|
||||
"AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
|
||||
ifmgd->associated->bssid, new_freq);
|
||||
current_band = cbss->channel->band;
|
||||
res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
|
||||
ifmgd->flags,
|
||||
ifmgd->associated->bssid, &count,
|
||||
&mode, &new_chandef);
|
||||
if (res < 0)
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
if (res)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!beacon && sec_chan_offs) {
|
||||
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
|
||||
} else if (beacon && ht_oper) {
|
||||
secondary_channel_offset =
|
||||
ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
|
||||
} else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
|
||||
/*
|
||||
* If it's not a beacon, HT is enabled and the IE not present,
|
||||
* it's 20 MHz, 802.11-2012 8.5.2.6:
|
||||
* This element [the Secondary Channel Offset Element] is
|
||||
* present when switching to a 40 MHz channel. It may be
|
||||
* present when switching to a 20 MHz channel (in which
|
||||
* case the secondary channel offset is set to SCN).
|
||||
*/
|
||||
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
|
||||
}
|
||||
|
||||
switch (secondary_channel_offset) {
|
||||
default:
|
||||
/* secondary_channel_offset was present but is invalid */
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
|
||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
||||
NL80211_CHAN_HT20);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
||||
NL80211_CHAN_HT40PLUS);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
||||
NL80211_CHAN_HT40MINUS);
|
||||
break;
|
||||
case -1:
|
||||
cfg80211_chandef_create(&new_chandef, new_chan,
|
||||
NL80211_CHAN_NO_HT);
|
||||
/* keep width for 5/10 MHz channels */
|
||||
switch (sdata->vif.bss_conf.chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
new_chandef.width = sdata->vif.bss_conf.chandef.width;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (wide_bw_chansw_ie) {
|
||||
new_vht_chandef.chan = new_chan;
|
||||
new_vht_chandef.center_freq1 =
|
||||
ieee80211_channel_to_frequency(
|
||||
wide_bw_chansw_ie->new_center_freq_seg0,
|
||||
new_band);
|
||||
|
||||
switch (wide_bw_chansw_ie->new_channel_width) {
|
||||
default:
|
||||
/* hmmm, ignore VHT and use HT if present */
|
||||
case IEEE80211_VHT_CHANWIDTH_USE_HT:
|
||||
new_vht_chandef.chan = NULL;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
||||
/* field is otherwise reserved */
|
||||
new_vht_chandef.center_freq2 =
|
||||
ieee80211_channel_to_frequency(
|
||||
wide_bw_chansw_ie->new_center_freq_seg1,
|
||||
new_band);
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
|
||||
break;
|
||||
}
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
||||
new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
|
||||
chandef_downgrade(&new_vht_chandef);
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
|
||||
new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
|
||||
chandef_downgrade(&new_vht_chandef);
|
||||
if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
|
||||
new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
|
||||
chandef_downgrade(&new_vht_chandef);
|
||||
}
|
||||
|
||||
/* if VHT data is there validate & use it */
|
||||
if (new_vht_chandef.chan) {
|
||||
if (!cfg80211_chandef_compatible(&new_vht_chandef,
|
||||
&new_chandef)) {
|
||||
sdata_info(sdata,
|
||||
"AP %pM CSA has inconsistent channel data, disconnecting\n",
|
||||
ifmgd->associated->bssid);
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
return;
|
||||
}
|
||||
new_chandef = new_vht_chandef;
|
||||
}
|
||||
|
||||
if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
|
||||
IEEE80211_CHAN_DISABLED)) {
|
||||
sdata_info(sdata,
|
||||
"AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
|
||||
ifmgd->associated->bssid, new_freq,
|
||||
ifmgd->associated->bssid,
|
||||
new_chandef.chan->center_freq,
|
||||
new_chandef.width, new_chandef.center_freq1,
|
||||
new_chandef.center_freq2);
|
||||
ieee80211_queue_work(&local->hw,
|
||||
@ -1191,17 +1000,28 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
|
||||
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
|
||||
|
||||
if (local->use_chanctx) {
|
||||
sdata_info(sdata,
|
||||
"not handling channel switch with channel contexts\n");
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
return;
|
||||
}
|
||||
sdata->vif.csa_active = true;
|
||||
|
||||
mutex_lock(&local->chanctx_mtx);
|
||||
if (local->use_chanctx) {
|
||||
u32 num_chanctx = 0;
|
||||
list_for_each_entry(chanctx, &local->chanctx_list, list)
|
||||
num_chanctx++;
|
||||
|
||||
if (num_chanctx > 1 ||
|
||||
!(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
|
||||
sdata_info(sdata,
|
||||
"not handling chan-switch with channel contexts\n");
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
|
||||
ieee80211_queue_work(&local->hw,
|
||||
&ifmgd->csa_connection_drop_work);
|
||||
mutex_unlock(&local->chanctx_mtx);
|
||||
return;
|
||||
}
|
||||
@ -1374,8 +1194,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
|
||||
if (!mgd->associated)
|
||||
return false;
|
||||
|
||||
if (mgd->flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL))
|
||||
if (mgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
||||
return false;
|
||||
|
||||
if (!mgd->have_beacon)
|
||||
@ -1691,8 +1510,7 @@ static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
lockdep_assert_held(&sdata->local->mtx);
|
||||
|
||||
sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
|
||||
IEEE80211_STA_BEACON_POLL);
|
||||
sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;
|
||||
ieee80211_run_deferred_scan(sdata->local);
|
||||
}
|
||||
|
||||
@ -1954,11 +1772,8 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
||||
mutex_lock(&local->mtx);
|
||||
if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL))) {
|
||||
mutex_unlock(&local->mtx);
|
||||
return;
|
||||
}
|
||||
if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL))
|
||||
goto out;
|
||||
|
||||
__ieee80211_stop_poll(sdata);
|
||||
|
||||
@ -2094,15 +1909,9 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
|
||||
* because otherwise we would reset the timer every time and
|
||||
* never check whether we received a probe response!
|
||||
*/
|
||||
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL))
|
||||
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
|
||||
already = true;
|
||||
|
||||
if (beacon)
|
||||
ifmgd->flags |= IEEE80211_STA_BEACON_POLL;
|
||||
else
|
||||
ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
|
||||
|
||||
mutex_unlock(&sdata->local->mtx);
|
||||
|
||||
if (already)
|
||||
@ -2174,6 +1983,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
|
||||
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
|
||||
true, frame_buf);
|
||||
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
|
||||
sdata->vif.csa_active = false;
|
||||
ieee80211_wake_queues_by_reason(&sdata->local->hw,
|
||||
IEEE80211_MAX_QUEUE_MAP,
|
||||
IEEE80211_QUEUE_STOP_REASON_CSA);
|
||||
@ -3061,17 +2871,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
||||
}
|
||||
}
|
||||
|
||||
if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
|
||||
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
|
||||
mlme_dbg_ratelimited(sdata,
|
||||
"cancelling AP probe due to a received beacon\n");
|
||||
mutex_lock(&local->mtx);
|
||||
ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
|
||||
ieee80211_run_deferred_scan(local);
|
||||
mutex_unlock(&local->mtx);
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
ieee80211_recalc_ps(local, -1);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
ieee80211_reset_ap_probe(sdata);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3543,8 +3346,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
||||
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
|
||||
run_again(sdata, ifmgd->assoc_data->timeout);
|
||||
|
||||
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL) &&
|
||||
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
|
||||
ifmgd->associated) {
|
||||
u8 bssid[ETH_ALEN];
|
||||
int max_tries;
|
||||
@ -3876,7 +3678,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
|
||||
return ret;
|
||||
|
||||
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
|
||||
ifmgd->flags |= chandef_downgrade(&chandef);
|
||||
ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);
|
||||
ret = ieee80211_vif_use_channel(sdata, &chandef,
|
||||
IEEE80211_CHANCTX_SHARED);
|
||||
}
|
||||
|
@ -203,6 +203,15 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
|
||||
memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate));
|
||||
mi->max_prob_rate = tmp_prob_rate;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/* use fixed index if set */
|
||||
if (mp->fixed_rate_idx != -1) {
|
||||
mi->max_tp_rate[0] = mp->fixed_rate_idx;
|
||||
mi->max_tp_rate[1] = mp->fixed_rate_idx;
|
||||
mi->max_prob_rate = mp->fixed_rate_idx;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Reset update timer */
|
||||
mi->stats_update = jiffies;
|
||||
|
||||
@ -310,6 +319,11 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
|
||||
/* increase sum packet counter */
|
||||
mi->packet_count++;
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
if (mp->fixed_rate_idx != -1)
|
||||
return;
|
||||
#endif
|
||||
|
||||
delta = (mi->packet_count * sampling_ratio / 100) -
|
||||
(mi->sample_count + mi->sample_deferred / 2);
|
||||
|
||||
|
@ -365,6 +365,14 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/* use fixed index if set */
|
||||
if (mp->fixed_rate_idx != -1) {
|
||||
mi->max_tp_rate = mp->fixed_rate_idx;
|
||||
mi->max_tp_rate2 = mp->fixed_rate_idx;
|
||||
mi->max_prob_rate = mp->fixed_rate_idx;
|
||||
}
|
||||
#endif
|
||||
|
||||
mi->stats_update = jiffies;
|
||||
}
|
||||
@ -774,6 +782,11 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
|
||||
info->flags |= mi->tx_flags;
|
||||
minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
if (mp->fixed_rate_idx != -1)
|
||||
return;
|
||||
#endif
|
||||
|
||||
/* Don't use EAPOL frames for sampling on non-mrr hw */
|
||||
if (mp->hw->max_rates == 1 &&
|
||||
(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
|
||||
@ -781,16 +794,6 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
|
||||
else
|
||||
sample_idx = minstrel_get_sample_rate(mp, mi);
|
||||
|
||||
#ifdef CONFIG_MAC80211_DEBUGFS
|
||||
/* use fixed index if set */
|
||||
if (mp->fixed_rate_idx != -1) {
|
||||
mi->max_tp_rate = mp->fixed_rate_idx;
|
||||
mi->max_tp_rate2 = mp->fixed_rate_idx;
|
||||
mi->max_prob_rate = mp->fixed_rate_idx;
|
||||
sample_idx = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
mi->total_packets++;
|
||||
|
||||
/* wraparound */
|
||||
|
@ -167,29 +167,29 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
|
||||
* provide large enough buffers. */
|
||||
length = length < RC_PID_PRINT_BUF_SIZE ?
|
||||
length : RC_PID_PRINT_BUF_SIZE;
|
||||
p = snprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
|
||||
p = scnprintf(pb, length, "%u %lu ", ev->id, ev->timestamp);
|
||||
switch (ev->type) {
|
||||
case RC_PID_EVENT_TYPE_TX_STATUS:
|
||||
p += snprintf(pb + p, length - p, "tx_status %u %u",
|
||||
!(ev->data.flags & IEEE80211_TX_STAT_ACK),
|
||||
ev->data.tx_status.status.rates[0].idx);
|
||||
p += scnprintf(pb + p, length - p, "tx_status %u %u",
|
||||
!(ev->data.flags & IEEE80211_TX_STAT_ACK),
|
||||
ev->data.tx_status.status.rates[0].idx);
|
||||
break;
|
||||
case RC_PID_EVENT_TYPE_RATE_CHANGE:
|
||||
p += snprintf(pb + p, length - p, "rate_change %d %d",
|
||||
ev->data.index, ev->data.rate);
|
||||
p += scnprintf(pb + p, length - p, "rate_change %d %d",
|
||||
ev->data.index, ev->data.rate);
|
||||
break;
|
||||
case RC_PID_EVENT_TYPE_TX_RATE:
|
||||
p += snprintf(pb + p, length - p, "tx_rate %d %d",
|
||||
ev->data.index, ev->data.rate);
|
||||
p += scnprintf(pb + p, length - p, "tx_rate %d %d",
|
||||
ev->data.index, ev->data.rate);
|
||||
break;
|
||||
case RC_PID_EVENT_TYPE_PF_SAMPLE:
|
||||
p += snprintf(pb + p, length - p,
|
||||
"pf_sample %d %d %d %d",
|
||||
ev->data.pf_sample, ev->data.prop_err,
|
||||
ev->data.int_err, ev->data.der_err);
|
||||
p += scnprintf(pb + p, length - p,
|
||||
"pf_sample %d %d %d %d",
|
||||
ev->data.pf_sample, ev->data.prop_err,
|
||||
ev->data.int_err, ev->data.der_err);
|
||||
break;
|
||||
}
|
||||
p += snprintf(pb + p, length - p, "\n");
|
||||
p += scnprintf(pb + p, length - p, "\n");
|
||||
|
||||
spin_unlock_irqrestore(&events->lock, status);
|
||||
|
||||
|
@ -995,8 +995,9 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
|
||||
rx->sta->num_duplicates++;
|
||||
}
|
||||
return RX_DROP_UNUSABLE;
|
||||
} else
|
||||
} else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
|
||||
rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(rx->skb->len < 16)) {
|
||||
@ -2402,7 +2403,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC &&
|
||||
mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED)
|
||||
mgmt->u.action.category != WLAN_CATEGORY_SELF_PROTECTED &&
|
||||
mgmt->u.action.category != WLAN_CATEGORY_SPECTRUM_MGMT)
|
||||
return RX_DROP_UNUSABLE;
|
||||
|
||||
if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
|
||||
@ -2566,31 +2568,46 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
|
||||
|
||||
goto queue;
|
||||
case WLAN_CATEGORY_SPECTRUM_MGMT:
|
||||
if (status->band != IEEE80211_BAND_5GHZ)
|
||||
break;
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
break;
|
||||
|
||||
/* verify action_code is present */
|
||||
if (len < IEEE80211_MIN_ACTION_SIZE + 1)
|
||||
break;
|
||||
|
||||
switch (mgmt->u.action.u.measurement.action_code) {
|
||||
case WLAN_ACTION_SPCT_MSR_REQ:
|
||||
if (status->band != IEEE80211_BAND_5GHZ)
|
||||
break;
|
||||
|
||||
if (len < (IEEE80211_MIN_ACTION_SIZE +
|
||||
sizeof(mgmt->u.action.u.measurement)))
|
||||
break;
|
||||
ieee80211_process_measurement_req(sdata, mgmt, len);
|
||||
goto handled;
|
||||
case WLAN_ACTION_SPCT_CHL_SWITCH:
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION)
|
||||
break;
|
||||
|
||||
if (!ether_addr_equal(mgmt->bssid, sdata->u.mgd.bssid))
|
||||
ieee80211_process_measurement_req(sdata, mgmt, len);
|
||||
goto handled;
|
||||
case WLAN_ACTION_SPCT_CHL_SWITCH: {
|
||||
u8 *bssid;
|
||||
if (len < (IEEE80211_MIN_ACTION_SIZE +
|
||||
sizeof(mgmt->u.action.u.chan_switch)))
|
||||
break;
|
||||
|
||||
if (sdata->vif.type != NL80211_IFTYPE_STATION &&
|
||||
sdata->vif.type != NL80211_IFTYPE_ADHOC)
|
||||
break;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||||
bssid = sdata->u.mgd.bssid;
|
||||
else if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
|
||||
bssid = sdata->u.ibss.bssid;
|
||||
else
|
||||
break;
|
||||
|
||||
if (!ether_addr_equal(mgmt->bssid, bssid))
|
||||
break;
|
||||
|
||||
goto queue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WLAN_CATEGORY_SA_QUERY:
|
||||
|
@ -391,8 +391,7 @@ static bool ieee80211_can_scan(struct ieee80211_local *local,
|
||||
return false;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
|
||||
sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
|
||||
IEEE80211_STA_CONNECTION_POLL))
|
||||
sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -21,6 +21,168 @@
|
||||
#include "sta_info.h"
|
||||
#include "wme.h"
|
||||
|
||||
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee802_11_elems *elems, bool beacon,
|
||||
enum ieee80211_band current_band,
|
||||
u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
|
||||
struct cfg80211_chan_def *new_chandef)
|
||||
{
|
||||
enum ieee80211_band new_band;
|
||||
int new_freq;
|
||||
u8 new_chan_no;
|
||||
struct ieee80211_channel *new_chan;
|
||||
struct cfg80211_chan_def new_vht_chandef = {};
|
||||
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
|
||||
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
|
||||
const struct ieee80211_ht_operation *ht_oper;
|
||||
int secondary_channel_offset = -1;
|
||||
|
||||
sec_chan_offs = elems->sec_chan_offs;
|
||||
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
|
||||
ht_oper = elems->ht_operation;
|
||||
|
||||
if (sta_flags & (IEEE80211_STA_DISABLE_HT |
|
||||
IEEE80211_STA_DISABLE_40MHZ)) {
|
||||
sec_chan_offs = NULL;
|
||||
wide_bw_chansw_ie = NULL;
|
||||
/* only used for bandwidth here */
|
||||
ht_oper = NULL;
|
||||
}
|
||||
|
||||
if (sta_flags & IEEE80211_STA_DISABLE_VHT)
|
||||
wide_bw_chansw_ie = NULL;
|
||||
|
||||
if (elems->ext_chansw_ie) {
|
||||
if (!ieee80211_operating_class_to_band(
|
||||
elems->ext_chansw_ie->new_operating_class,
|
||||
&new_band)) {
|
||||
sdata_info(sdata,
|
||||
"cannot understand ECSA IE operating class %d, disconnecting\n",
|
||||
elems->ext_chansw_ie->new_operating_class);
|
||||
return -EINVAL;
|
||||
}
|
||||
new_chan_no = elems->ext_chansw_ie->new_ch_num;
|
||||
*count = elems->ext_chansw_ie->count;
|
||||
*mode = elems->ext_chansw_ie->mode;
|
||||
} else if (elems->ch_switch_ie) {
|
||||
new_band = current_band;
|
||||
new_chan_no = elems->ch_switch_ie->new_ch_num;
|
||||
*count = elems->ch_switch_ie->count;
|
||||
*mode = elems->ch_switch_ie->mode;
|
||||
} else {
|
||||
/* nothing here we understand */
|
||||
return 1;
|
||||
}
|
||||
|
||||
new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
|
||||
new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
|
||||
if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
|
||||
sdata_info(sdata,
|
||||
"BSS %pM switches to unsupported channel (%d MHz), disconnecting\n",
|
||||
bssid, new_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!beacon && sec_chan_offs) {
|
||||
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
|
||||
} else if (beacon && ht_oper) {
|
||||
secondary_channel_offset =
|
||||
ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
|
||||
} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
|
||||
/* If it's not a beacon, HT is enabled and the IE not present,
|
||||
* it's 20 MHz, 802.11-2012 8.5.2.6:
|
||||
* This element [the Secondary Channel Offset Element] is
|
||||
* present when switching to a 40 MHz channel. It may be
|
||||
* present when switching to a 20 MHz channel (in which
|
||||
* case the secondary channel offset is set to SCN).
|
||||
*/
|
||||
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
|
||||
}
|
||||
|
||||
switch (secondary_channel_offset) {
|
||||
default:
|
||||
/* secondary_channel_offset was present but is invalid */
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
NL80211_CHAN_HT20);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
NL80211_CHAN_HT40PLUS);
|
||||
break;
|
||||
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
NL80211_CHAN_HT40MINUS);
|
||||
break;
|
||||
case -1:
|
||||
cfg80211_chandef_create(new_chandef, new_chan,
|
||||
NL80211_CHAN_NO_HT);
|
||||
/* keep width for 5/10 MHz channels */
|
||||
switch (sdata->vif.bss_conf.chandef.width) {
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
new_chandef->width = sdata->vif.bss_conf.chandef.width;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (wide_bw_chansw_ie) {
|
||||
new_vht_chandef.chan = new_chan;
|
||||
new_vht_chandef.center_freq1 =
|
||||
ieee80211_channel_to_frequency(
|
||||
wide_bw_chansw_ie->new_center_freq_seg0,
|
||||
new_band);
|
||||
|
||||
switch (wide_bw_chansw_ie->new_channel_width) {
|
||||
default:
|
||||
/* hmmm, ignore VHT and use HT if present */
|
||||
case IEEE80211_VHT_CHANWIDTH_USE_HT:
|
||||
new_vht_chandef.chan = NULL;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80MHZ:
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_160MHZ:
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
|
||||
break;
|
||||
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
|
||||
/* field is otherwise reserved */
|
||||
new_vht_chandef.center_freq2 =
|
||||
ieee80211_channel_to_frequency(
|
||||
wide_bw_chansw_ie->new_center_freq_seg1,
|
||||
new_band);
|
||||
new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
|
||||
break;
|
||||
}
|
||||
if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ &&
|
||||
new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
|
||||
ieee80211_chandef_downgrade(&new_vht_chandef);
|
||||
if (sta_flags & IEEE80211_STA_DISABLE_160MHZ &&
|
||||
new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
|
||||
ieee80211_chandef_downgrade(&new_vht_chandef);
|
||||
if (sta_flags & IEEE80211_STA_DISABLE_40MHZ &&
|
||||
new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
|
||||
ieee80211_chandef_downgrade(&new_vht_chandef);
|
||||
}
|
||||
|
||||
/* if VHT data is there validate & use it */
|
||||
if (new_vht_chandef.chan) {
|
||||
if (!cfg80211_chandef_compatible(&new_vht_chandef,
|
||||
new_chandef)) {
|
||||
sdata_info(sdata,
|
||||
"BSS %pM: CSA has inconsistent channel data, disconnecting\n",
|
||||
bssid);
|
||||
return -EINVAL;
|
||||
}
|
||||
*new_chandef = new_vht_chandef;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_msrment_ie *request_ie,
|
||||
const u8 *da, const u8 *bssid,
|
||||
|
@ -1475,6 +1475,41 @@ DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change,
|
||||
);
|
||||
#endif
|
||||
|
||||
TRACE_EVENT(drv_join_ibss,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_bss_conf *info),
|
||||
|
||||
TP_ARGS(local, sdata, info),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
VIF_ENTRY
|
||||
__field(u8, dtimper)
|
||||
__field(u16, bcnint)
|
||||
__dynamic_array(u8, ssid, info->ssid_len);
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
VIF_ASSIGN;
|
||||
__entry->dtimper = info->dtim_period;
|
||||
__entry->bcnint = info->beacon_int;
|
||||
memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
|
||||
),
|
||||
|
||||
TP_printk(
|
||||
LOCAL_PR_FMT VIF_PR_FMT,
|
||||
LOCAL_PR_ARG, VIF_PR_ARG
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_sdata_evt, drv_leave_ibss,
|
||||
TP_PROTO(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata),
|
||||
TP_ARGS(local, sdata)
|
||||
);
|
||||
|
||||
/*
|
||||
* Tracing for API calls that drivers call.
|
||||
*/
|
||||
|
@ -1981,7 +1981,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
* EAPOL frames from the local station.
|
||||
*/
|
||||
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
|
||||
!is_multicast_ether_addr(hdr.addr1) && !authorized &&
|
||||
!multicast && !authorized &&
|
||||
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
|
||||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
|
||||
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
||||
@ -2357,15 +2357,31 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
|
||||
struct probe_resp *resp;
|
||||
int counter_offset_beacon = sdata->csa_counter_offset_beacon;
|
||||
int counter_offset_presp = sdata->csa_counter_offset_presp;
|
||||
u8 *beacon_data;
|
||||
size_t beacon_data_len;
|
||||
|
||||
/* warn if the driver did not check for/react to csa completeness */
|
||||
if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
beacon_data = beacon->tail;
|
||||
beacon_data_len = beacon->tail_len;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
beacon_data = beacon->head;
|
||||
beacon_data_len = beacon->head_len;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
if (WARN_ON(counter_offset_beacon >= beacon_data_len))
|
||||
return;
|
||||
|
||||
((u8 *)beacon->tail)[counter_offset_beacon]--;
|
||||
/* warn if the driver did not check for/react to csa completeness */
|
||||
if (WARN_ON(beacon_data[counter_offset_beacon] == 0))
|
||||
return;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP &&
|
||||
counter_offset_presp) {
|
||||
beacon_data[counter_offset_beacon]--;
|
||||
|
||||
if (sdata->vif.type == NL80211_IFTYPE_AP && counter_offset_presp) {
|
||||
rcu_read_lock();
|
||||
resp = rcu_dereference(sdata->u.ap.probe_resp);
|
||||
|
||||
@ -2400,6 +2416,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
|
||||
goto out;
|
||||
beacon_data = beacon->tail;
|
||||
beacon_data_len = beacon->tail_len;
|
||||
} else if (vif->type == NL80211_IFTYPE_ADHOC) {
|
||||
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
|
||||
beacon = rcu_dereference(ifibss->presp);
|
||||
if (!beacon)
|
||||
goto out;
|
||||
|
||||
beacon_data = beacon->head;
|
||||
beacon_data_len = beacon->head_len;
|
||||
} else {
|
||||
WARN_ON(1);
|
||||
goto out;
|
||||
@ -2484,6 +2509,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
|
||||
if (!presp)
|
||||
goto out;
|
||||
|
||||
if (sdata->vif.csa_active)
|
||||
ieee80211_update_csa(sdata, presp);
|
||||
|
||||
|
||||
skb = dev_alloc_skb(local->tx_headroom + presp->head_len);
|
||||
if (!skb)
|
||||
goto out;
|
||||
|
@ -567,58 +567,14 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
|
||||
IEEE80211_QUEUE_STOP_REASON_FLUSH);
|
||||
}
|
||||
|
||||
void ieee80211_iterate_active_interfaces(
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
static void __iterate_active_interfaces(struct ieee80211_local *local,
|
||||
u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
|
||||
list_for_each_entry(sdata, &local->interfaces, list) {
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
|
||||
continue;
|
||||
break;
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
|
||||
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
||||
continue;
|
||||
if (ieee80211_sdata_running(sdata))
|
||||
iterator(data, sdata->vif.addr,
|
||||
&sdata->vif);
|
||||
}
|
||||
|
||||
sdata = rcu_dereference_protected(local->monitor_sdata,
|
||||
lockdep_is_held(&local->iflist_mtx));
|
||||
if (sdata &&
|
||||
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
|
||||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
||||
iterator(data, sdata->vif.addr, &sdata->vif);
|
||||
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
|
||||
|
||||
void ieee80211_iterate_active_interfaces_atomic(
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
@ -638,16 +594,57 @@ void ieee80211_iterate_active_interfaces_atomic(
|
||||
&sdata->vif);
|
||||
}
|
||||
|
||||
sdata = rcu_dereference(local->monitor_sdata);
|
||||
sdata = rcu_dereference_check(local->monitor_sdata,
|
||||
lockdep_is_held(&local->iflist_mtx) ||
|
||||
lockdep_rtnl_is_held());
|
||||
if (sdata &&
|
||||
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
|
||||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
||||
iterator(data, sdata->vif.addr, &sdata->vif);
|
||||
}
|
||||
|
||||
void ieee80211_iterate_active_interfaces(
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
mutex_lock(&local->iflist_mtx);
|
||||
__iterate_active_interfaces(local, iter_flags, iterator, data);
|
||||
mutex_unlock(&local->iflist_mtx);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
|
||||
|
||||
void ieee80211_iterate_active_interfaces_atomic(
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
rcu_read_lock();
|
||||
__iterate_active_interfaces(local, iter_flags, iterator, data);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
|
||||
|
||||
void ieee80211_iterate_active_interfaces_rtnl(
|
||||
struct ieee80211_hw *hw, u32 iter_flags,
|
||||
void (*iterator)(void *data, u8 *mac,
|
||||
struct ieee80211_vif *vif),
|
||||
void *data)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
__iterate_active_interfaces(local, iter_flags, iterator, data);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
|
||||
|
||||
/*
|
||||
* Nothing should have been stuffed into the workqueue during
|
||||
* the suspend->resume cycle. If this WARN is seen then there
|
||||
@ -1007,14 +1004,21 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||
*/
|
||||
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
/* Set defaults according to 802.11-2007 Table 7-37 */
|
||||
aCWmax = 1023;
|
||||
if (use_11b)
|
||||
aCWmin = 31;
|
||||
else
|
||||
aCWmin = 15;
|
||||
/* Set defaults according to 802.11-2007 Table 7-37 */
|
||||
aCWmax = 1023;
|
||||
if (use_11b)
|
||||
aCWmin = 31;
|
||||
else
|
||||
aCWmin = 15;
|
||||
|
||||
/* Confiure old 802.11b/g medium access rules. */
|
||||
qparam.cw_max = aCWmax;
|
||||
qparam.cw_min = aCWmin;
|
||||
qparam.txop = 0;
|
||||
qparam.aifs = 2;
|
||||
|
||||
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
/* Update if QoS is enabled. */
|
||||
if (enable_qos) {
|
||||
switch (ac) {
|
||||
case IEEE80211_AC_BK:
|
||||
@ -1050,12 +1054,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
|
||||
qparam.aifs = 2;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Confiure old 802.11b/g medium access rules. */
|
||||
qparam.cw_max = aCWmax;
|
||||
qparam.cw_min = aCWmin;
|
||||
qparam.txop = 0;
|
||||
qparam.aifs = 2;
|
||||
}
|
||||
|
||||
qparam.uapsd = false;
|
||||
@ -1084,8 +1082,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
int err;
|
||||
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
|
||||
sizeof(*mgmt) + 6 + extra_len);
|
||||
/* 24 + 6 = header + auth_algo + auth_transaction + status_code */
|
||||
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
@ -2292,3 +2290,63 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw)
|
||||
ieee80211_queue_work(hw, &local->radar_detected_work);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_radar_detected);
|
||||
|
||||
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
|
||||
{
|
||||
u32 ret;
|
||||
int tmp;
|
||||
|
||||
switch (c->width) {
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
c->width = NL80211_CHAN_WIDTH_20;
|
||||
c->center_freq1 = c->chan->center_freq;
|
||||
ret = IEEE80211_STA_DISABLE_40MHZ |
|
||||
IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P40 */
|
||||
tmp /= 2;
|
||||
/* freq_P40 */
|
||||
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
|
||||
c->width = NL80211_CHAN_WIDTH_40;
|
||||
ret = IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80P80:
|
||||
c->center_freq2 = 0;
|
||||
c->width = NL80211_CHAN_WIDTH_80;
|
||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
/* n_P20 */
|
||||
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
|
||||
/* n_P80 */
|
||||
tmp /= 4;
|
||||
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
|
||||
c->width = NL80211_CHAN_WIDTH_80;
|
||||
ret = IEEE80211_STA_DISABLE_80P80MHZ |
|
||||
IEEE80211_STA_DISABLE_160MHZ;
|
||||
break;
|
||||
default:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
WARN_ON_ONCE(1);
|
||||
c->width = NL80211_CHAN_WIDTH_20_NOHT;
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_5:
|
||||
case NL80211_CHAN_WIDTH_10:
|
||||
WARN_ON_ONCE(1);
|
||||
/* keep c->width */
|
||||
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
|
||||
break;
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -185,13 +185,13 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
|
||||
if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) {
|
||||
vht_cap->cap |= cap_info &
|
||||
(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
|
||||
IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX |
|
||||
IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX);
|
||||
}
|
||||
|
||||
if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
|
||||
vht_cap->cap |= cap_info &
|
||||
IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
|
||||
(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
|
||||
IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX);
|
||||
|
||||
if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
|
||||
vht_cap->cap |= cap_info &
|
||||
|
@ -328,6 +328,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||
return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
|
||||
width);
|
||||
}
|
||||
EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
|
||||
|
||||
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
|
||||
u32 center_freq, u32 bandwidth,
|
||||
|
@ -382,15 +382,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
|
||||
enum cfg80211_chan_mode chanmode,
|
||||
u8 radar_detect);
|
||||
|
||||
/**
|
||||
* cfg80211_chandef_dfs_required - checks if radar detection is required
|
||||
* @wiphy: the wiphy to validate against
|
||||
* @chandef: the channel definition to check
|
||||
* Return: 1 if radar detection is required, 0 if it is not, < 0 on error
|
||||
*/
|
||||
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *c);
|
||||
|
||||
void cfg80211_set_dfs_state(struct wiphy *wiphy,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
enum nl80211_dfs_state dfs_state);
|
||||
|
@ -47,17 +47,19 @@ static int ht_print_chan(struct ieee80211_channel *chan,
|
||||
return 0;
|
||||
|
||||
if (chan->flags & IEEE80211_CHAN_DISABLED)
|
||||
return snprintf(buf + offset,
|
||||
buf_size - offset,
|
||||
"%d Disabled\n",
|
||||
chan->center_freq);
|
||||
return scnprintf(buf + offset,
|
||||
buf_size - offset,
|
||||
"%d Disabled\n",
|
||||
chan->center_freq);
|
||||
|
||||
return snprintf(buf + offset,
|
||||
buf_size - offset,
|
||||
"%d HT40 %c%c\n",
|
||||
chan->center_freq,
|
||||
(chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-',
|
||||
(chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+');
|
||||
return scnprintf(buf + offset,
|
||||
buf_size - offset,
|
||||
"%d HT40 %c%c\n",
|
||||
chan->center_freq,
|
||||
(chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ?
|
||||
' ' : '-',
|
||||
(chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ?
|
||||
' ' : '+');
|
||||
}
|
||||
|
||||
static ssize_t ht40allow_map_read(struct file *file,
|
||||
|
@ -46,6 +46,12 @@ BEGIN {
|
||||
sub(/:/, "", country)
|
||||
printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
|
||||
printf "\t.alpha2 = \"%s\",\n", country
|
||||
if ($NF ~ /DFS-ETSI/)
|
||||
printf "\t.dfs_region = NL80211_DFS_ETSI,\n"
|
||||
else if ($NF ~ /DFS-FCC/)
|
||||
printf "\t.dfs_region = NL80211_DFS_FCC,\n"
|
||||
else if ($NF ~ /DFS-JP/)
|
||||
printf "\t.dfs_region = NL80211_DFS_JP,\n"
|
||||
printf "\t.reg_rules = {\n"
|
||||
active = 1
|
||||
regdb = regdb "\t®dom_" country ",\n"
|
||||
|
@ -5591,6 +5591,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (netif_carrier_ok(dev))
|
||||
return -EBUSY;
|
||||
|
||||
if (wdev->cac_started)
|
||||
return -EBUSY;
|
||||
|
||||
@ -5634,15 +5637,26 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
|
||||
u8 radar_detect_width = 0;
|
||||
int err;
|
||||
bool need_new_beacon = false;
|
||||
|
||||
if (!rdev->ops->channel_switch ||
|
||||
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* may add IBSS support later */
|
||||
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
|
||||
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
|
||||
switch (dev->ieee80211_ptr->iftype) {
|
||||
case NL80211_IFTYPE_AP:
|
||||
case NL80211_IFTYPE_P2P_GO:
|
||||
need_new_beacon = true;
|
||||
|
||||
/* useless if AP is not running */
|
||||
if (!wdev->beacon_interval)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
|
||||
@ -5651,15 +5665,16 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
|
||||
/* only important for AP, IBSS and mesh create IEs internally */
|
||||
if (!info->attrs[NL80211_ATTR_CSA_IES])
|
||||
return -EINVAL;
|
||||
|
||||
/* useless if AP is not running */
|
||||
if (!wdev->beacon_interval)
|
||||
if (need_new_beacon &&
|
||||
(!info->attrs[NL80211_ATTR_CSA_IES] ||
|
||||
!info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON]))
|
||||
return -EINVAL;
|
||||
|
||||
params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
|
||||
|
||||
if (!need_new_beacon)
|
||||
goto skip_beacons;
|
||||
|
||||
err = nl80211_parse_beacon(info->attrs, ¶ms.beacon_after);
|
||||
if (err)
|
||||
return err;
|
||||
@ -5699,6 +5714,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
skip_beacons:
|
||||
err = nl80211_parse_chandef(rdev, info, ¶ms.chandef);
|
||||
if (err)
|
||||
return err;
|
||||
@ -5706,12 +5722,17 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef))
|
||||
return -EINVAL;
|
||||
|
||||
err = cfg80211_chandef_dfs_required(wdev->wiphy, ¶ms.chandef);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
} else if (err) {
|
||||
radar_detect_width = BIT(params.chandef.width);
|
||||
params.radar_required = true;
|
||||
/* DFS channels are only supported for AP/P2P GO ... for now. */
|
||||
if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
|
||||
dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
|
||||
err = cfg80211_chandef_dfs_required(wdev->wiphy,
|
||||
¶ms.chandef);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
} else if (err) {
|
||||
radar_detect_width = BIT(params.chandef.width);
|
||||
params.radar_required = true;
|
||||
}
|
||||
}
|
||||
|
||||
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
|
||||
@ -10740,7 +10761,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
|
||||
wdev_lock(wdev);
|
||||
|
||||
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
|
||||
wdev->iftype != NL80211_IFTYPE_P2P_GO))
|
||||
wdev->iftype != NL80211_IFTYPE_P2P_GO &&
|
||||
wdev->iftype != NL80211_IFTYPE_ADHOC))
|
||||
goto out;
|
||||
|
||||
wdev->channel = chandef->chan;
|
||||
|
@ -172,11 +172,21 @@ static const struct ieee80211_regdomain world_regdom = {
|
||||
NL80211_RRF_NO_IBSS |
|
||||
NL80211_RRF_NO_OFDM),
|
||||
/* IEEE 802.11a, channel 36..48 */
|
||||
REG_RULE(5180-10, 5240+10, 80, 6, 20,
|
||||
REG_RULE(5180-10, 5240+10, 160, 6, 20,
|
||||
NL80211_RRF_PASSIVE_SCAN |
|
||||
NL80211_RRF_NO_IBSS),
|
||||
|
||||
/* NB: 5260 MHz - 5700 MHz requires DFS */
|
||||
/* IEEE 802.11a, channel 52..64 - DFS required */
|
||||
REG_RULE(5260-10, 5320+10, 160, 6, 20,
|
||||
NL80211_RRF_PASSIVE_SCAN |
|
||||
NL80211_RRF_NO_IBSS |
|
||||
NL80211_RRF_DFS),
|
||||
|
||||
/* IEEE 802.11a, channel 100..144 - DFS required */
|
||||
REG_RULE(5500-10, 5720+10, 160, 6, 20,
|
||||
NL80211_RRF_PASSIVE_SCAN |
|
||||
NL80211_RRF_NO_IBSS |
|
||||
NL80211_RRF_DFS),
|
||||
|
||||
/* IEEE 802.11a, channel 149..165 */
|
||||
REG_RULE(5745-10, 5825+10, 80, 6, 20,
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <net/cfg80211.h>
|
||||
#include <net/ip.h>
|
||||
#include <net/dsfield.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include "core.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
@ -691,6 +692,7 @@ EXPORT_SYMBOL(ieee80211_amsdu_to_8023s);
|
||||
unsigned int cfg80211_classify8021d(struct sk_buff *skb)
|
||||
{
|
||||
unsigned int dscp;
|
||||
unsigned char vlan_priority;
|
||||
|
||||
/* skb->priority values from 256->263 are magic values to
|
||||
* directly indicate a specific 802.1d priority. This is used
|
||||
@ -700,6 +702,13 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb)
|
||||
if (skb->priority >= 256 && skb->priority <= 263)
|
||||
return skb->priority - 256;
|
||||
|
||||
if (vlan_tx_tag_present(skb)) {
|
||||
vlan_priority = (vlan_tx_tag_get(skb) & VLAN_PRIO_MASK)
|
||||
>> VLAN_PRIO_SHIFT;
|
||||
if (vlan_priority > 0)
|
||||
return vlan_priority;
|
||||
}
|
||||
|
||||
switch (skb->protocol) {
|
||||
case htons(ETH_P_IP):
|
||||
dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;
|
||||
|
Loading…
Reference in New Issue
Block a user