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:
David S. Miller 2013-10-17 16:14:29 -04:00
commit 5cda73b68e
89 changed files with 11952 additions and 1324 deletions

View File

@ -6830,6 +6830,14 @@ L: linux-hexagon@vger.kernel.org
S: Supported S: Supported
F: arch/hexagon/ 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 QUICKCAM PARALLEL PORT WEBCAMS
M: Hans Verkuil <hverkuil@xs4all.nl> M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org L: linux-media@vger.kernel.org

View File

@ -188,8 +188,11 @@ static int bcma_host_pci_probe(struct pci_dev *dev,
pci_write_config_dword(dev, 0x40, val & 0xffff00ff); pci_write_config_dword(dev, 0x40, val & 0xffff00ff);
/* SSB needed additional powering up, do we have any AMBA PCI cards? */ /* SSB needed additional powering up, do we have any AMBA PCI cards? */
if (!pci_is_pcie(dev)) if (!pci_is_pcie(dev)) {
bcma_err(bus, "PCI card detected, report problems.\n"); bcma_err(bus, "PCI card detected, they are not supported.\n");
err = -ENXIO;
goto err_pci_release_regions;
}
/* Map MMIO */ /* Map MMIO */
err = -ENOMEM; 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) = { static DEFINE_PCI_DEVICE_TABLE(bcma_pci_bridge_tbl) = {
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x0576) }, { 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, 43224) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },

View File

@ -32,5 +32,6 @@ source "drivers/net/wireless/ath/ath6kl/Kconfig"
source "drivers/net/wireless/ath/ar5523/Kconfig" source "drivers/net/wireless/ath/ar5523/Kconfig"
source "drivers/net/wireless/ath/wil6210/Kconfig" source "drivers/net/wireless/ath/wil6210/Kconfig"
source "drivers/net/wireless/ath/ath10k/Kconfig" source "drivers/net/wireless/ath/ath10k/Kconfig"
source "drivers/net/wireless/ath/wcn36xx/Kconfig"
endif endif

View File

@ -5,6 +5,7 @@ obj-$(CONFIG_ATH6KL) += ath6kl/
obj-$(CONFIG_AR5523) += ar5523/ obj-$(CONFIG_AR5523) += ar5523/
obj-$(CONFIG_WIL6210) += wil6210/ obj-$(CONFIG_WIL6210) += wil6210/
obj-$(CONFIG_ATH10K) += ath10k/ obj-$(CONFIG_ATH10K) += ath10k/
obj-$(CONFIG_WCN36XX) += wcn36xx/
obj-$(CONFIG_ATH_COMMON) += ath.o obj-$(CONFIG_ATH_COMMON) += ath.o

View File

@ -338,10 +338,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
aniState->cckNoiseImmunityLevel != aniState->cckNoiseImmunityLevel !=
ATH9K_ANI_CCK_DEF_LEVEL) { ATH9K_ANI_CCK_DEF_LEVEL) {
ath_dbg(common, ANI, 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, ah->opmode,
chan->channel, chan->channel,
chan->channelFlags,
is_scanning, is_scanning,
aniState->ofdmNoiseImmunityLevel, aniState->ofdmNoiseImmunityLevel,
aniState->cckNoiseImmunityLevel); aniState->cckNoiseImmunityLevel);
@ -354,10 +353,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
* restore historical levels for this channel * restore historical levels for this channel
*/ */
ath_dbg(common, ANI, 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, ah->opmode,
chan->channel, chan->channel,
chan->channelFlags,
is_scanning, is_scanning,
aniState->ofdmNoiseImmunityLevel, aniState->ofdmNoiseImmunityLevel,
aniState->cckNoiseImmunityLevel); aniState->cckNoiseImmunityLevel);

View File

@ -666,14 +666,13 @@ static void ar5008_hw_set_channel_regs(struct ath_hw *ah,
if (IS_CHAN_HT40(chan)) { if (IS_CHAN_HT40(chan)) {
phymode |= AR_PHY_FC_DYN2040_EN; phymode |= AR_PHY_FC_DYN2040_EN;
if ((chan->chanmode == CHANNEL_A_HT40PLUS) || if (IS_CHAN_HT40PLUS(chan))
(chan->chanmode == CHANNEL_G_HT40PLUS))
phymode |= AR_PHY_FC_DYN2040_PRI_CH; phymode |= AR_PHY_FC_DYN2040_PRI_CH;
} }
REG_WRITE(ah, AR_PHY_TURBO, phymode); REG_WRITE(ah, AR_PHY_TURBO, phymode);
ath9k_hw_set11nmac2040(ah); ath9k_hw_set11nmac2040(ah, chan);
ENABLE_REGWRITE_BUFFER(ah); ENABLE_REGWRITE_BUFFER(ah);
@ -691,31 +690,12 @@ static int ar5008_hw_process_ini(struct ath_hw *ah,
int i, regWrites = 0; int i, regWrites = 0;
u32 modesIndex, freqIndex; u32 modesIndex, freqIndex;
switch (chan->chanmode) { if (IS_CHAN_5GHZ(chan)) {
case CHANNEL_A:
case CHANNEL_A_HT20:
modesIndex = 1;
freqIndex = 1; freqIndex = 1;
break; modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
case CHANNEL_A_HT40PLUS: } else {
case CHANNEL_A_HT40MINUS:
modesIndex = 2;
freqIndex = 1;
break;
case CHANNEL_G:
case CHANNEL_G_HT20:
case CHANNEL_B:
modesIndex = 4;
freqIndex = 2; freqIndex = 2;
break; modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
case CHANNEL_G_HT40PLUS:
case CHANNEL_G_HT40MINUS:
modesIndex = 3;
freqIndex = 2;
break;
default:
return -EINVAL;
} }
/* /*
@ -814,8 +794,10 @@ static void ar5008_hw_set_rfmode(struct ath_hw *ah, struct ath9k_channel *chan)
if (chan == NULL) if (chan == NULL)
return; return;
rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) if (IS_CHAN_2GHZ(chan))
? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; rfMode |= AR_PHY_MODE_DYNAMIC;
else
rfMode |= AR_PHY_MODE_OFDM;
if (!AR_SREV_9280_20_OR_LATER(ah)) if (!AR_SREV_9280_20_OR_LATER(ah))
rfMode |= (IS_CHAN_5GHZ(chan)) ? rfMode |= (IS_CHAN_5GHZ(chan)) ?
@ -1218,12 +1200,11 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
iniDef = &aniState->iniDef; 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.macVersion,
ah->hw_version.macRev, ah->hw_version.macRev,
ah->opmode, ah->opmode,
chan->channel, chan->channel);
chan->channelFlags);
val = REG_READ(ah, AR_PHY_SFCORR); val = REG_READ(ah, AR_PHY_SFCORR);
iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH); iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);

View File

@ -33,15 +33,12 @@ static bool ar9002_hw_is_cal_supported(struct ath_hw *ah,
bool supported = false; bool supported = false;
switch (ah->supp_cals & cal_type) { switch (ah->supp_cals & cal_type) {
case IQ_MISMATCH_CAL: case IQ_MISMATCH_CAL:
/* Run IQ Mismatch for non-CCK only */
if (!IS_CHAN_B(chan))
supported = true; supported = true;
break; break;
case ADC_GAIN_CAL: case ADC_GAIN_CAL:
case ADC_DC_CAL: case ADC_DC_CAL:
/* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */ /* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */
if (!IS_CHAN_B(chan) && if (!((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) &&
!((IS_CHAN_2GHZ(chan) || IS_CHAN_A_FAST_CLOCK(ah, chan)) &&
IS_CHAN_HT20(chan))) IS_CHAN_HT20(chan)))
supported = true; supported = true;
break; break;

View File

@ -419,28 +419,10 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan)
u32 modesIndex; u32 modesIndex;
int i; int i;
switch (chan->chanmode) { if (IS_CHAN_5GHZ(chan))
case CHANNEL_A: modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
case CHANNEL_A_HT20: else
modesIndex = 1; modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
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;
}
ENABLE_REGWRITE_BUFFER(ah); ENABLE_REGWRITE_BUFFER(ah);

View File

@ -551,8 +551,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah,
if (IS_CHAN_HT40(chan)) { if (IS_CHAN_HT40(chan)) {
phymode |= AR_PHY_GC_DYN2040_EN; phymode |= AR_PHY_GC_DYN2040_EN;
/* Configure control (primary) channel at +-10MHz */ /* Configure control (primary) channel at +-10MHz */
if ((chan->chanmode == CHANNEL_A_HT40PLUS) || if (IS_CHAN_HT40PLUS(chan))
(chan->chanmode == CHANNEL_G_HT40PLUS))
phymode |= AR_PHY_GC_DYN2040_PRI_CH; 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); REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode);
/* Configure MAC for 20/40 operation */ /* Configure MAC for 20/40 operation */
ath9k_hw_set11nmac2040(ah); ath9k_hw_set11nmac2040(ah, chan);
/* global transmit timeout (25 TUs default)*/ /* global transmit timeout (25 TUs default)*/
REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S); REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
@ -682,41 +681,22 @@ static int ar9550_hw_get_modes_txgain_index(struct ath_hw *ah,
{ {
int ret; int ret;
switch (chan->chanmode) { if (IS_CHAN_2GHZ(chan)) {
case CHANNEL_A: if (IS_CHAN_HT40(chan))
case CHANNEL_A_HT20: return 7;
else
return 8;
}
if (chan->channel <= 5350) if (chan->channel <= 5350)
ret = 1; ret = 1;
else if ((chan->channel > 5350) && (chan->channel <= 5600)) else if ((chan->channel > 5350) && (chan->channel <= 5600))
ret = 3; ret = 3;
else else
ret = 5; ret = 5;
break;
case CHANNEL_A_HT40PLUS: if (IS_CHAN_HT40(chan))
case CHANNEL_A_HT40MINUS: ret++;
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 ret; return ret;
} }
@ -727,28 +707,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
unsigned int regWrites = 0, i; unsigned int regWrites = 0, i;
u32 modesIndex; u32 modesIndex;
switch (chan->chanmode) { if (IS_CHAN_5GHZ(chan))
case CHANNEL_A: modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
case CHANNEL_A_HT20: else
modesIndex = 1; modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
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;
}
/* /*
* SOC, MAC, BB, RADIO initvals. * SOC, MAC, BB, RADIO initvals.
@ -846,8 +808,10 @@ static void ar9003_hw_set_rfmode(struct ath_hw *ah,
if (chan == NULL) if (chan == NULL)
return; return;
rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan)) if (IS_CHAN_2GHZ(chan))
? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM; rfMode |= AR_PHY_MODE_DYNAMIC;
else
rfMode |= AR_PHY_MODE_OFDM;
if (IS_CHAN_A_FAST_CLOCK(ah, chan)) if (IS_CHAN_A_FAST_CLOCK(ah, chan))
rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE); 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; aniState = &ah->ani;
iniDef = &aniState->iniDef; 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.macVersion,
ah->hw_version.macRev, ah->hw_version.macRev,
ah->opmode, ah->opmode,
chan->channel, chan->channel);
chan->channelFlags);
val = REG_READ(ah, AR_PHY_SFCORR); val = REG_READ(ah, AR_PHY_SFCORR);
iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH); 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; unsigned int regWrites = 0;
u32 modesIndex; u32 modesIndex;
switch (chan->chanmode) { if (IS_CHAN_5GHZ(chan))
case CHANNEL_A: modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
case CHANNEL_A_HT20: else
modesIndex = 1; modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
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 (modesIndex == ah->modes_index) { if (modesIndex == ah->modes_index) {
*ini_reloaded = false; *ini_reloaded = false;

View File

@ -64,7 +64,6 @@ struct ath_node;
struct ath_config { struct ath_config {
u16 txpowlimit; u16 txpowlimit;
u8 cabqReadytime;
}; };
/*************************/ /*************************/
@ -207,6 +206,14 @@ struct ath_frame_info {
u8 baw_tracked : 1; 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 { struct ath_buf_state {
u8 bf_type; u8 bf_type;
u8 bfs_paprd; u8 bfs_paprd;
@ -307,7 +314,7 @@ struct ath_rx {
struct ath_descdma rxdma; struct ath_descdma rxdma;
struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX]; struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
struct ath_buf *buf_hold; struct ath_rxbuf *buf_hold;
struct sk_buff *frag; struct sk_buff *frag;
u32 ampdu_ref; 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_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
void ath9k_reload_chainmask_settings(struct ath_softc *sc); void ath9k_reload_chainmask_settings(struct ath_softc *sc);
bool ath9k_uses_beacons(int type);
void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw); void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
int ath9k_spectral_scan_config(struct ieee80211_hw *hw, int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
enum spectral_mode spectral_mode); enum spectral_mode spectral_mode);

View File

@ -186,7 +186,6 @@ void ath9k_hw_reset_calibration(struct ath_hw *ah,
bool ath9k_hw_reset_calvalid(struct ath_hw *ah) bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
{ {
struct ath_common *common = ath9k_hw_common(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; struct ath9k_cal_list *currCal = ah->cal_list_curr;
if (!ah->caldata) if (!ah->caldata)
@ -208,7 +207,7 @@ bool ath9k_hw_reset_calvalid(struct ath_hw *ah)
return true; return true;
ath_dbg(common, CALIBRATE, "Resetting Cal %d state for channel %u\n", 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; ah->caldata->CalValid &= ~currCal->calData->calType;
currCal->calState = CAL_WAITING; currCal->calState = CAL_WAITING;
@ -242,7 +241,6 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
int32_t val; int32_t val;
u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask; u8 chainmask = (ah->rxchainmask << 3) | ah->rxchainmask;
struct ath_common *common = ath9k_hw_common(ah); 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); s16 default_nf = ath9k_hw_get_default_nf(ah, chan);
if (ah->caldata) if (ah->caldata)
@ -252,7 +250,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
if (chainmask & (1 << i)) { if (chainmask & (1 << i)) {
s16 nfval; s16 nfval;
if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
continue; continue;
if (h) if (h)
@ -314,7 +312,7 @@ void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
ENABLE_REGWRITE_BUFFER(ah); ENABLE_REGWRITE_BUFFER(ah);
for (i = 0; i < NUM_NF_READINGS; i++) { for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) { if (chainmask & (1 << i)) {
if ((i >= AR5416_MAX_CHAINS) && !conf_is_ht40(conf)) if ((i >= AR5416_MAX_CHAINS) && !IS_CHAN_HT40(chan))
continue; continue;
val = REG_READ(ah, ah->nf_regs[i]); 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->channel = chan->channel;
ah->caldata->channelFlags = chan->channelFlags; ah->caldata->channelFlags = chan->channelFlags;
ah->caldata->chanmode = chan->chanmode;
h = ah->caldata->nfCalHist; h = ah->caldata->nfCalHist;
default_nf = ath9k_hw_get_default_nf(ah, chan); default_nf = ath9k_hw_get_default_nf(ah, chan);
for (i = 0; i < NUM_NF_READINGS; i++) { for (i = 0; i < NUM_NF_READINGS; i++) {

View File

@ -49,103 +49,64 @@ int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb)
} }
EXPORT_SYMBOL(ath9k_cmn_get_hw_crypto_keytype); 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. * Update internal channel flags.
*/ */
void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, static void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan,
struct cfg80211_chan_def *chandef) struct cfg80211_chan_def *chandef)
{ {
ichan->channel = chandef->chan->center_freq; struct ieee80211_channel *chan = chandef->chan;
ichan->chan = chandef->chan; u16 flags = 0;
if (chandef->chan->band == IEEE80211_BAND_2GHZ) { ichan->channel = chan->center_freq;
ichan->chanmode = CHANNEL_G; ichan->chan = chan;
ichan->channelFlags = CHANNEL_2GHZ | CHANNEL_OFDM;
} else { if (chan->band == IEEE80211_BAND_5GHZ)
ichan->chanmode = CHANNEL_A; flags |= CHANNEL_5GHZ;
ichan->channelFlags = CHANNEL_5GHZ | CHANNEL_OFDM;
}
switch (chandef->width) { switch (chandef->width) {
case NL80211_CHAN_WIDTH_5: case NL80211_CHAN_WIDTH_5:
ichan->channelFlags |= CHANNEL_QUARTER; flags |= CHANNEL_QUARTER;
break; break;
case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_10:
ichan->channelFlags |= CHANNEL_HALF; flags |= CHANNEL_HALF;
break; break;
case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20_NOHT:
break; break;
case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20:
flags |= CHANNEL_HT;
break;
case NL80211_CHAN_WIDTH_40: 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; break;
default: default:
WARN_ON(1); WARN_ON(1);
} }
ichan->channelFlags = flags;
} }
EXPORT_SYMBOL(ath9k_cmn_update_ichannel);
/* /*
* Get the internal channel reference. * Get the internal channel reference.
*/ */
struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw, struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
struct ath_hw *ah) 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; struct ath9k_channel *channel;
u8 chan_idx; u8 chan_idx;
chan_idx = curchan->hw_value; chan_idx = curchan->hw_value;
channel = &ah->channels[chan_idx]; channel = &ah->channels[chan_idx];
ath9k_cmn_update_ichannel(channel, &hw->conf.chandef); ath9k_cmn_update_ichannel(channel, chandef);
return channel; return channel;
} }
EXPORT_SYMBOL(ath9k_cmn_get_curchannel); EXPORT_SYMBOL(ath9k_cmn_get_channel);
int ath9k_cmn_count_streams(unsigned int chainmask, int max) int ath9k_cmn_count_streams(unsigned int chainmask, int max)
{ {

View File

@ -43,10 +43,9 @@
(((x) + ((mul)/2)) / (mul)) (((x) + ((mul)/2)) / (mul))
int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb); int ath9k_cmn_get_hw_crypto_keytype(struct sk_buff *skb);
void ath9k_cmn_update_ichannel(struct ath9k_channel *ichan, struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw,
struct ath_hw *ah,
struct cfg80211_chan_def *chandef); struct cfg80211_chan_def *chandef);
struct ath9k_channel *ath9k_cmn_get_curchannel(struct ieee80211_hw *hw,
struct ath_hw *ah);
int ath9k_cmn_count_streams(unsigned int chainmask, int max); int ath9k_cmn_count_streams(unsigned int chainmask, int max);
void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common, void ath9k_cmn_btcoex_bt_stomp(struct ath_common *common,
enum ath_stomp_type stomp_type); enum ath_stomp_type stomp_type);

View File

@ -24,30 +24,10 @@
static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
struct ath9k_channel *ichan) struct ath9k_channel *ichan)
{ {
enum htc_phymode mode; if (IS_CHAN_5GHZ(ichan))
return HTC_MODE_11NA;
mode = -EINVAL; return HTC_MODE_11NG;
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;
} }
bool ath9k_htc_setpower(struct ath9k_htc_priv *priv, 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); WMI_CMD(WMI_FLUSH_RECV_CMDID);
/* setup initial channel */ /* 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); ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
if (ret) { 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", ath_dbg(common, CONFIG, "Set channel: %d MHz\n",
curchan->center_freq); curchan->center_freq);
ath9k_cmn_update_ichannel(&priv->ah->channels[pos], ath9k_cmn_get_channel(hw, priv->ah, &hw->conf.chandef);
&hw->conf.chandef);
if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
ath_err(common, "Unable to set channel\n"); ath_err(common, "Unable to set channel\n");
ret = -EINVAL; ret = -EINVAL;

View File

@ -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) 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 ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan;
unsigned int clockrate; unsigned int clockrate;
/* AR9287 v1.3+ uses async FIFO and runs the MAC at 117 MHz */ /* 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)) if (AR_SREV_9287(ah) && AR_SREV_9287_13_OR_LATER(ah))
clockrate = 117; 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; 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; clockrate = ATH9K_CLOCK_RATE_2GHZ_OFDM;
else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK) else if (ah->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK)
clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM; clockrate = ATH9K_CLOCK_FAST_RATE_5GHZ_OFDM;
else else
clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM; clockrate = ATH9K_CLOCK_RATE_5GHZ_OFDM;
if (conf_is_ht40(conf)) if (IS_CHAN_HT40(chan))
clockrate *= 2; clockrate *= 2;
if (ah->curchan) { if (ah->curchan) {
if (IS_CHAN_HALF_RATE(ah->curchan)) if (IS_CHAN_HALF_RATE(chan))
clockrate /= 2; clockrate /= 2;
if (IS_CHAN_QUARTER_RATE(ah->curchan)) if (IS_CHAN_QUARTER_RATE(chan))
clockrate /= 4; clockrate /= 4;
} }
@ -190,9 +190,6 @@ EXPORT_SYMBOL(ath9k_hw_wait);
void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan, void ath9k_hw_synth_delay(struct ath_hw *ah, struct ath9k_channel *chan,
int hw_delay) 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)) if (IS_CHAN_HALF_RATE(chan))
@ -294,8 +291,7 @@ void ath9k_hw_get_channel_centers(struct ath_hw *ah,
return; return;
} }
if ((chan->chanmode == CHANNEL_A_HT40PLUS) || if (IS_CHAN_HT40PLUS(chan)) {
(chan->chanmode == CHANNEL_G_HT40PLUS)) {
centers->synth_center = centers->synth_center =
chan->channel + HT40_CHANNEL_CENTER_SHIFT; chan->channel + HT40_CHANNEL_CENTER_SHIFT;
extoff = 1; 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) void ath9k_hw_init_global_settings(struct ath_hw *ah)
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &common->hw->conf;
const struct ath9k_channel *chan = ah->curchan; const struct ath9k_channel *chan = ah->curchan;
int acktimeout, ctstimeout, ack_offset = 0; int acktimeout, ctstimeout, ack_offset = 0;
int slottime; 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 * BA frames in some implementations, but it has been found to fix ACK
* timeout issues in other cases as well. * timeout issues in other cases as well.
*/ */
if (conf->chandef.chan && if (IS_CHAN_2GHZ(chan) &&
conf->chandef.chan->band == IEEE80211_BAND_2GHZ &&
!IS_CHAN_HALF_RATE(chan) && !IS_CHAN_QUARTER_RATE(chan)) { !IS_CHAN_HALF_RATE(chan) && !IS_CHAN_QUARTER_RATE(chan)) {
acktimeout += 64 - sifstime - ah->slottime; acktimeout += 64 - sifstime - ah->slottime;
ctstimeout += 48 - 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); u32 ctl = ath_regd_get_band_ctl(reg, chan->chan->band);
if (IS_CHAN_B(chan)) if (IS_CHAN_2GHZ(chan))
ctl |= CTL_11B;
else if (IS_CHAN_G(chan))
ctl |= CTL_11G; ctl |= CTL_11G;
else else
ctl |= CTL_11A; ctl |= CTL_11A;
@ -1510,10 +1502,8 @@ static bool ath9k_hw_channel_change(struct ath_hw *ah,
int r; int r;
if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) { if (pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) {
u32 cur = ah->curchan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ); band_switch = IS_CHAN_5GHZ(ah->curchan) != IS_CHAN_5GHZ(chan);
u32 new = chan->channelFlags & (CHANNEL_2GHZ | CHANNEL_5GHZ); mode_diff = (chan->channelFlags != ah->curchan->channelFlags);
band_switch = (cur != new);
mode_diff = (chan->chanmode != ah->curchan->chanmode);
} }
for (qnum = 0; qnum < AR_NUM_QCU; qnum++) { 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_set_clockrate(ah);
ath9k_hw_apply_txpower(ah, chan, false); 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); ath9k_hw_spur_mitigate_freq(ah, chan);
if (band_switch || ini_reloaded) if (band_switch || ini_reloaded)
@ -1824,21 +1812,12 @@ static int ath9k_hw_do_fastcc(struct ath_hw *ah, struct ath9k_channel *chan)
goto fail; goto fail;
/* /*
* If cross-band fcc is not supoprted, bail out if * If cross-band fcc is not supoprted, bail out if channelFlags differ.
* either channelFlags or chanmode differ.
*
* chanmode will be different if the HT operating mode
* changes because of CSA.
*/ */
if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH)) { if (!(pCap->hw_caps & ATH9K_HW_CAP_FCC_BAND_SWITCH) &&
if ((chan->channelFlags & CHANNEL_ALL) != chan->channelFlags != ah->curchan->channelFlags)
(ah->curchan->channelFlags & CHANNEL_ALL))
goto fail; goto fail;
if (chan->chanmode != ah->curchan->chanmode)
goto fail;
}
if (!ath9k_hw_check_alive(ah)) if (!ath9k_hw_check_alive(ah))
goto fail; goto fail;
@ -1899,8 +1878,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
ah->caldata = caldata; ah->caldata = caldata;
if (caldata && (chan->channel != caldata->channel || if (caldata && (chan->channel != caldata->channel ||
chan->channelFlags != caldata->channelFlags || chan->channelFlags != caldata->channelFlags)) {
chan->chanmode != caldata->chanmode)) {
/* Operating channel changed, reset channel calibration data */ /* Operating channel changed, reset channel calibration data */
memset(caldata, 0, sizeof(*caldata)); memset(caldata, 0, sizeof(*caldata));
ath9k_init_nfcal_hist_buffer(ah, chan); 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); 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); ath9k_hw_spur_mitigate_freq(ah, chan);
ah->eep_ops->set_board_values(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); 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; 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; macmode = AR_2040_JOINED_RX_CLEAR;
else else
macmode = 0; macmode = 0;

View File

@ -369,36 +369,6 @@ enum ath9k_int {
ATH9K_INT_NOCARD = 0xffffffff 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_RTT_TABLE_ENTRY 6
#define MAX_IQCAL_MEASUREMENT 8 #define MAX_IQCAL_MEASUREMENT 8
#define MAX_CL_TAB_ENTRY 16 #define MAX_CL_TAB_ENTRY 16
@ -417,8 +387,7 @@ enum ath9k_cal_flags {
struct ath9k_hw_cal_data { struct ath9k_hw_cal_data {
u16 channel; u16 channel;
u32 channelFlags; u16 channelFlags;
u32 chanmode;
unsigned long cal_flags; unsigned long cal_flags;
int32_t CalValid; int32_t CalValid;
int8_t iCoff; int8_t iCoff;
@ -436,33 +405,34 @@ struct ath9k_hw_cal_data {
struct ath9k_channel { struct ath9k_channel {
struct ieee80211_channel *chan; struct ieee80211_channel *chan;
u16 channel; u16 channel;
u32 channelFlags; u16 channelFlags;
u32 chanmode;
s16 noisefloor; s16 noisefloor;
}; };
#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \ #define CHANNEL_5GHZ BIT(0)
(((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \ #define CHANNEL_HALF BIT(1)
(((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \ #define CHANNEL_QUARTER BIT(2)
(((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS)) #define CHANNEL_HT BIT(3)
#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0) #define CHANNEL_HT40PLUS BIT(4)
#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0) #define CHANNEL_HT40MINUS BIT(5)
#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))
/* These macros check chanmode and not channelFlags */ #define IS_CHAN_5GHZ(_c) (!!((_c)->channelFlags & CHANNEL_5GHZ))
#define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B) #define IS_CHAN_2GHZ(_c) (!IS_CHAN_5GHZ(_c))
#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) || \
((_c)->chanmode == CHANNEL_G_HT20)) #define IS_CHAN_HALF_RATE(_c) (!!((_c)->channelFlags & CHANNEL_HALF))
#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) || \ #define IS_CHAN_QUARTER_RATE(_c) (!!((_c)->channelFlags & CHANNEL_QUARTER))
((_c)->chanmode == CHANNEL_A_HT40MINUS) || \ #define IS_CHAN_A_FAST_CLOCK(_ah, _c) \
((_c)->chanmode == CHANNEL_G_HT40PLUS) || \ (IS_CHAN_5GHZ(_c) && ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK))
((_c)->chanmode == CHANNEL_G_HT40MINUS))
#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c))) #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 { enum ath9k_power_mode {
ATH9K_PM_AWAKE = 0, 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_set_tsfadjust(struct ath_hw *ah, bool set);
void ath9k_hw_init_global_settings(struct ath_hw *ah); void ath9k_hw_init_global_settings(struct ath_hw *ah);
u32 ar9003_get_pll_sqsum_dvc(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_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period);
void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
const struct ath9k_beacon_state *bs); const struct ath9k_beacon_state *bs);

View File

@ -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); struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u8 *ds; u8 *ds;
struct ath_buf *bf;
int i, bsize, desc_len; int i, bsize, desc_len;
ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n", ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n",
@ -399,6 +398,9 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
/* allocate buffers */ /* allocate buffers */
if (is_tx) {
struct ath_buf *bf;
bsize = sizeof(struct ath_buf) * nbuf; bsize = sizeof(struct ath_buf) * nbuf;
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL); bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
if (!bf) if (!bf)
@ -427,6 +429,38 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
} }
list_add_tail(&bf->list, head); 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);
}
}
return 0; 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.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah);
sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
ath_cabq_update(sc); ath_cabq_update(sc);
sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0); 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]; chan = &sband->channels[i];
ah->curchan = &ah->channels[chan->hw_value]; ah->curchan = &ah->channels[chan->hw_value];
cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20); 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); ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true);
} }
} }

View File

@ -374,7 +374,6 @@ EXPORT_SYMBOL(ath9k_hw_releasetxqueue);
bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
{ {
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_channel *chan = ah->curchan;
struct ath9k_tx_queue_info *qi; struct ath9k_tx_queue_info *qi;
u32 cwMin, chanCwMin, value; u32 cwMin, chanCwMin, value;
@ -387,9 +386,6 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q)
ath_dbg(common, QUEUE, "Reset TX queue: %u\n", q); ath_dbg(common, QUEUE, "Reset TX queue: %u\n", q);
if (qi->tqi_cwmin == ATH9K_TXQ_USEDEFAULT) { 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); for (cwMin = 1; cwMin < chanCwMin; cwMin = (cwMin << 1) | 1);

View File

@ -603,8 +603,6 @@ enum ath9k_tx_queue_flags {
#define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001 #define ATH9K_TXQ_USE_LOCKOUT_BKOFF_DIS 0x00000001
#define ATH9K_DECOMP_MASK_SIZE 128 #define ATH9K_DECOMP_MASK_SIZE 128
#define ATH9K_READY_TIME_LO_BOUND 50
#define ATH9K_READY_TIME_HI_BOUND 96
enum ath9k_pkt_type { enum ath9k_pkt_type {
ATH9K_PKT_TYPE_NORMAL = 0, ATH9K_PKT_TYPE_NORMAL = 0,

View File

@ -302,17 +302,91 @@ out:
* by reseting the chip. To accomplish this we must first cleanup any pending * by reseting the chip. To accomplish this we must first cleanup any pending
* DMA, then restart stuff. * DMA, then restart stuff.
*/ */
static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef)
struct ath9k_channel *hchan)
{ {
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; int r;
if (test_bit(SC_OP_INVALID, &sc->sc_flags)) if (test_bit(SC_OP_INVALID, &sc->sc_flags))
return -EIO; return -EIO;
r = ath_reset_internal(sc, hchan); offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
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; 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, 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); ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex); 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 */ /* Reset SERDES registers */
ath9k_hw_configpcipowersave(ah, false); ath9k_hw_configpcipowersave(ah, false);
@ -804,7 +878,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
} }
if (!ah->curchan) 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_reset(ah, ah->curchan, ah->caldata, false);
ath9k_hw_phy_disable(ah); ath9k_hw_phy_disable(ah);
@ -823,7 +897,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath_dbg(common, CONFIG, "Driver halt\n"); ath_dbg(common, CONFIG, "Driver halt\n");
} }
bool ath9k_uses_beacons(int type) static bool ath9k_uses_beacons(int type)
{ {
switch (type) { switch (type) {
case NL80211_IFTYPE_AP: 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) { if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
struct ieee80211_channel *curchan = hw->conf.chandef.chan; if (ath_set_channel(sc, &hw->conf.chandef) < 0) {
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) {
ath_err(common, "Unable to set channel\n"); ath_err(common, "Unable to set channel\n");
mutex_unlock(&sc->mutex); mutex_unlock(&sc->mutex);
ath9k_ps_restore(sc); ath9k_ps_restore(sc);
return -EINVAL; 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) { if (changed & IEEE80211_CONF_CHANGE_POWER) {

View File

@ -661,9 +661,9 @@ void ath9k_mci_update_wlan_channels(struct ath_softc *sc, bool allow_all)
chan_start = wlan_chan - 10; chan_start = wlan_chan - 10;
chan_end = wlan_chan + 10; chan_end = wlan_chan + 10;
if (chan->chanmode == CHANNEL_G_HT40PLUS) if (IS_CHAN_HT40PLUS(chan))
chan_end += 20; chan_end += 20;
else if (chan->chanmode == CHANNEL_G_HT40MINUS) else if (IS_CHAN_HT40MINUS(chan))
chan_start -= 20; chan_start -= 20;
/* adjust side band */ /* adjust side band */
@ -707,11 +707,11 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
if (setchannel) { if (setchannel) {
struct ath9k_hw_cal_data *caldata = &sc->caldata; 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) &&
(ah->curchan->channel <= caldata->channel + 20)) (ah->curchan->channel <= caldata->channel + 20))
return; return;
if ((caldata->chanmode == CHANNEL_G_HT40MINUS) && if (IS_CHAN_HT40MINUS(ah->curchan) &&
(ah->curchan->channel < caldata->channel) && (ah->curchan->channel < caldata->channel) &&
(ah->curchan->channel >= caldata->channel - 20)) (ah->curchan->channel >= caldata->channel - 20))
return; return;

View File

@ -19,7 +19,7 @@
#include "ath9k.h" #include "ath9k.h"
#include "ar9003_mac.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) 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 * buffer (or rx fifo). This can incorrectly acknowledge packets
* to a sender if last desc is self-linked. * 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_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(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; 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) if (sc->rx.buf_hold)
ath_rx_buf_link(sc, 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_hw *ah = sc->sc_ah;
struct ath_rx_edma *rx_edma; struct ath_rx_edma *rx_edma;
struct sk_buff *skb; struct sk_buff *skb;
struct ath_buf *bf; struct ath_rxbuf *bf;
rx_edma = &sc->rx.rx_edma[qtype]; rx_edma = &sc->rx.rx_edma[qtype];
if (skb_queue_len(&rx_edma->rx_fifo) >= rx_edma->rx_fifo_hwsize) if (skb_queue_len(&rx_edma->rx_fifo) >= rx_edma->rx_fifo_hwsize)
return false; 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); list_del_init(&bf->list);
skb = bf->bf_mpdu; skb = bf->bf_mpdu;
@ -138,7 +138,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc,
enum ath9k_rx_qtype qtype) enum ath9k_rx_qtype qtype)
{ {
struct ath_common *common = ath9k_hw_common(sc->sc_ah); 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)) { if (list_empty(&sc->rx.rxbuf)) {
ath_dbg(common, QUEUE, "No free rx buf available\n"); 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, static void ath_rx_remove_buffer(struct ath_softc *sc,
enum ath9k_rx_qtype qtype) enum ath9k_rx_qtype qtype)
{ {
struct ath_buf *bf; struct ath_rxbuf *bf;
struct ath_rx_edma *rx_edma; struct ath_rx_edma *rx_edma;
struct sk_buff *skb; 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_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(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_LP);
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP); 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_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct sk_buff *skb; struct sk_buff *skb;
struct ath_buf *bf; struct ath_rxbuf *bf;
int error = 0, i; int error = 0, i;
u32 size; 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], ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP],
ah->caps.rx_hp_qdepth); 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); bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
if (!bf) if (!bf)
return -ENOMEM; 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 ath_common *common = ath9k_hw_common(sc->sc_ah);
struct sk_buff *skb; struct sk_buff *skb;
struct ath_buf *bf; struct ath_rxbuf *bf;
int error = 0; int error = 0;
spin_lock_init(&sc->sc_pcu_lock); 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_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct sk_buff *skb; struct sk_buff *skb;
struct ath_buf *bf; struct ath_rxbuf *bf;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
ath_rx_edma_cleanup(sc); ath_rx_edma_cleanup(sc);
@ -427,7 +427,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
int ath_startrecv(struct ath_softc *sc) int ath_startrecv(struct ath_softc *sc)
{ {
struct ath_hw *ah = sc->sc_ah; 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) { if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
ath_edma_start_recv(sc); ath_edma_start_recv(sc);
@ -447,7 +447,7 @@ int ath_startrecv(struct ath_softc *sc)
if (list_empty(&sc->rx.rxbuf)) if (list_empty(&sc->rx.rxbuf))
goto start_recv; 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_putrxbuf(ah, bf->bf_daddr);
ath9k_hw_rxena(ah); 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, static bool ath_edma_get_buffers(struct ath_softc *sc,
enum ath9k_rx_qtype qtype, enum ath9k_rx_qtype qtype,
struct ath_rx_status *rs, 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_rx_edma *rx_edma = &sc->rx.rx_edma[qtype];
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct sk_buff *skb; struct sk_buff *skb;
struct ath_buf *bf; struct ath_rxbuf *bf;
int ret; int ret;
skb = skb_peek(&rx_edma->rx_fifo); skb = skb_peek(&rx_edma->rx_fifo);
@ -653,11 +653,11 @@ static bool ath_edma_get_buffers(struct ath_softc *sc,
return true; 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, struct ath_rx_status *rs,
enum ath9k_rx_qtype qtype) enum ath9k_rx_qtype qtype)
{ {
struct ath_buf *bf = NULL; struct ath_rxbuf *bf = NULL;
while (ath_edma_get_buffers(sc, qtype, rs, &bf)) { while (ath_edma_get_buffers(sc, qtype, rs, &bf)) {
if (!bf) if (!bf)
@ -668,13 +668,13 @@ static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
return NULL; 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_rx_status *rs)
{ {
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah); struct ath_common *common = ath9k_hw_common(ah);
struct ath_desc *ds; struct ath_desc *ds;
struct ath_buf *bf; struct ath_rxbuf *bf;
int ret; int ret;
if (list_empty(&sc->rx.rxbuf)) { 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; 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) if (bf == sc->rx.buf_hold)
return NULL; 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); ret = ath9k_hw_rxprocdesc(ah, ds, rs);
if (ret == -EINPROGRESS) { if (ret == -EINPROGRESS) {
struct ath_rx_status trs; struct ath_rx_status trs;
struct ath_buf *tbf; struct ath_rxbuf *tbf;
struct ath_desc *tds; struct ath_desc *tds;
memset(&trs, 0, sizeof(trs)); memset(&trs, 0, sizeof(trs));
@ -711,7 +711,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
return NULL; 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 * 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) 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 sk_buff *skb = NULL, *requeue_skb, *hdr_skb;
struct ieee80211_rx_status *rxs; struct ieee80211_rx_status *rxs;
struct ath_hw *ah = sc->sc_ah; struct ath_hw *ah = sc->sc_ah;

View File

@ -1704,16 +1704,9 @@ int ath_cabq_update(struct ath_softc *sc)
int qnum = sc->beacon.cabq->axq_qnum; int qnum = sc->beacon.cabq->axq_qnum;
ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi); 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 * qi.tqi_readyTime = (cur_conf->beacon_interval *
sc->config.cabqReadytime) / 100; ATH_CABQ_READY_TIME) / 100;
ath_txq_update(sc, qnum, &qi); ath_txq_update(sc, qnum, &qi);
return 0; 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 ath_hw *ah = sc->sc_ah;
struct ath9k_channel *curchan = ah->curchan; struct ath9k_channel *curchan = ah->curchan;
if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && IS_CHAN_5GHZ(curchan) &&
(curchan->channelFlags & CHANNEL_5GHZ) &&
(chainmask == 0x7) && (rate < 0x90)) (chainmask == 0x7) && (rate < 0x90))
return 0x3; return 0x3;
else if (AR_SREV_9462(ah) && ath9k_hw_btcoex_is_enabled(ah) && else if (AR_SREV_9462(ah) && ath9k_hw_btcoex_is_enabled(ah) &&

View 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.

View File

@ -0,0 +1,7 @@
obj-$(CONFIG_WCN36XX) := wcn36xx.o
wcn36xx-y += main.o \
dxe.o \
txrx.o \
smd.o \
pmc.o \
debug.o

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

View 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_ */

View 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,
&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, &reg_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, &reg_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);
}

View 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_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

View 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_ */

File diff suppressed because it is too large Load Diff

View 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_ */

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

View 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_ */

View 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_ */

View File

@ -5695,7 +5695,7 @@ static bool brcms_c_chipmatch_pci(struct bcma_device *core)
return true; return true;
if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID)) if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID))
return true; return true;
if (device == BCM4313_D11N2G_ID) if (device == BCM4313_D11N2G_ID || device == BCM4313_CHIP_ID)
return true; return true;
if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID)) if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID))
return true; return true;

View File

@ -1048,7 +1048,7 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter)
struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL;
unsigned long cmd_flags; unsigned long cmd_flags;
unsigned long scan_pending_q_flags; unsigned long scan_pending_q_flags;
uint16_t cancel_scan_cmd = false; bool cancel_scan_cmd = false;
if ((adapter->curr_cmd) && if ((adapter->curr_cmd) &&
(adapter->curr_cmd->wait_q_enabled)) { (adapter->curr_cmd->wait_q_enabled)) {

View File

@ -621,7 +621,7 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv,
int ret = 0; int ret = 0;
struct ieee_types_assoc_rsp *assoc_rsp; struct ieee_types_assoc_rsp *assoc_rsp;
struct mwifiex_bssdescriptor *bss_desc; struct mwifiex_bssdescriptor *bss_desc;
u8 enable_data = true; bool enable_data = true;
u16 cap_info, status_code; u16 cap_info, status_code;
assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params;

View File

@ -882,7 +882,9 @@ mwifiex_add_card(void *card, struct semaphore *sem,
adapter->cmd_wait_q.status = 0; adapter->cmd_wait_q.status = 0;
adapter->scan_wait_q_woken = false; 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) if (!adapter->workqueue)
goto err_kmalloc; goto err_kmalloc;

View File

@ -327,7 +327,7 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv,
{ {
struct mwifiex_adapter *adapter = priv->adapter; struct mwifiex_adapter *adapter = priv->adapter;
struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; 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) if (!hscfg_param)
/* New Activate command */ /* New Activate command */

View File

@ -708,7 +708,7 @@ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
{ {
u8 *curr = (u8 *) &resp->params.get_wmm_status; u8 *curr = (u8 *) &resp->params.get_wmm_status;
uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; 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_data *tlv_hdr;
struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus;

View File

@ -636,7 +636,7 @@ static int p54spi_probe(struct spi_device *spi)
gpio_direction_input(p54spi_gpio_irq); gpio_direction_input(p54spi_gpio_irq);
ret = request_irq(gpio_to_irq(p54spi_gpio_irq), ret = request_irq(gpio_to_irq(p54spi_gpio_irq),
p54spi_interrupt, IRQF_DISABLED, "p54spi", p54spi_interrupt, 0, "p54spi",
priv->spi); priv->spi);
if (ret < 0) { if (ret < 0) {
dev_err(&priv->spi->dev, "request_irq() failed"); dev_err(&priv->spi->dev, "request_irq() failed");

View File

@ -219,6 +219,7 @@ config RT2X00_LIB_USB
config RT2X00_LIB config RT2X00_LIB
tristate tristate
select AVERAGE
config RT2X00_LIB_FIRMWARE config RT2X00_LIB_FIRMWARE
boolean boolean

View File

@ -278,12 +278,9 @@ static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = {
[EEPROM_LNA] = 0x0026, [EEPROM_LNA] = 0x0026,
[EEPROM_EXT_LNA2] = 0x0027, [EEPROM_EXT_LNA2] = 0x0027,
[EEPROM_RSSI_BG] = 0x0028, [EEPROM_RSSI_BG] = 0x0028,
[EEPROM_TXPOWER_DELTA] = 0x0028, /* Overlaps with RSSI_BG */
[EEPROM_RSSI_BG2] = 0x0029, [EEPROM_RSSI_BG2] = 0x0029,
[EEPROM_TXMIXER_GAIN_BG] = 0x0029, /* Overlaps with RSSI_BG2 */
[EEPROM_RSSI_A] = 0x002a, [EEPROM_RSSI_A] = 0x002a,
[EEPROM_RSSI_A2] = 0x002b, [EEPROM_RSSI_A2] = 0x002b,
[EEPROM_TXMIXER_GAIN_A] = 0x002b, /* Overlaps with RSSI_A2 */
[EEPROM_TXPOWER_BG1] = 0x0030, [EEPROM_TXPOWER_BG1] = 0x0030,
[EEPROM_TXPOWER_BG2] = 0x0037, [EEPROM_TXPOWER_BG2] = 0x0037,
[EEPROM_EXT_TXPOWER_BG3] = 0x003e, [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); rt2800_bbp_read(rt2x00dev, 3, &r3);
if (rt2x00_rt(rt2x00dev, RT3572) && if (rt2x00_rt(rt2x00dev, RT3572) &&
test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) rt2x00_has_cap_bt_coexist(rt2x00dev))
rt2800_config_3572bt_ant(rt2x00dev); rt2800_config_3572bt_ant(rt2x00dev);
/* /*
@ -1795,7 +1792,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
break; break;
case 2: case 2:
if (rt2x00_rt(rt2x00dev, RT3572) && 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); rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1);
else else
rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2); 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; break;
case 2: case 2:
if (rt2x00_rt(rt2x00dev, RT3572) && 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_ADC, 1);
rt2x00_set_field8(&r3, BBP3_RX_ANTENNA, rt2x00_set_field8(&r3, BBP3_RX_ANTENNA,
rt2x00dev->curr_band == IEEE80211_BAND_5GHZ); 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); rt2x00dev->default_ant.tx_chain_num <= 2);
rt2800_rfcsr_write(rt2x00dev, 1, rfcsr); 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); rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset); rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset);
rt2800_rfcsr_write(rt2x00dev, 23, rfcsr); 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_TX1_PD, 0);
rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0); rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0);
rt2x00_set_field8(&rfcsr, RFCSR1_TX2_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) { if (rf->channel <= 14) {
rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1); rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
rt2x00_set_field8(&rfcsr, RFCSR1_TX0_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) { if (rf->channel <= 14) {
int idx = rf->channel-1; 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)) { if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) {
/* r55/r59 value array of channel 1~14 */ /* r55/r59 value array of channel 1~14 */
static const char r55_bt_rev[] = {0x83, 0x83, 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 (rf->channel <= 14) {
if (!rt2x00_rt(rt2x00dev, RT5390) && if (!rt2x00_rt(rt2x00dev, RT5390) &&
!rt2x00_rt(rt2x00dev, RT5392)) { !rt2x00_rt(rt2x00dev, RT5392)) {
if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
&rt2x00dev->cap_flags)) {
rt2800_bbp_write(rt2x00dev, 82, 0x62); rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 75, 0x46); rt2800_bbp_write(rt2x00dev, 75, 0x46);
} else { } else {
@ -3246,7 +3235,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
if (rt2x00_rt(rt2x00dev, RT3593)) if (rt2x00_rt(rt2x00dev, RT3593))
rt2800_bbp_write(rt2x00dev, 83, 0x9a); 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); rt2800_bbp_write(rt2x00dev, 75, 0x46);
else else
rt2800_bbp_write(rt2x00dev, 75, 0x50); rt2800_bbp_write(rt2x00dev, 75, 0x50);
@ -3282,7 +3271,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
/* Turn on primary PAs */ /* Turn on primary PAs */
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN, rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN,
rf->channel > 14); 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); rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1);
else else
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 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); 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); 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)) { if (rt2x00_rt(rt2x00dev, RT3593)) {
rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg); rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
@ -3575,7 +3573,7 @@ static int rt2800_get_txpower_reg_delta(struct rt2x00_dev *rt2x00dev,
{ {
int delta; int delta;
if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) if (rt2x00_has_cap_power_limit(rt2x00dev))
return 0; return 0;
/* /*
@ -3604,7 +3602,7 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
if (rt2x00_rt(rt2x00dev, RT3593)) if (rt2x00_rt(rt2x00dev, RT3593))
return min_t(u8, txpower, 0xc); 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. * Check if eirp txpower exceed txpower_limit.
* We use OFDM 6M as criterion and its eirp txpower * 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, RT3290) ||
rt2x00_rt(rt2x00dev, RT3390) || rt2x00_rt(rt2x00dev, RT3390) ||
rt2x00_rt(rt2x00dev, RT3572) || rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT3593) ||
rt2x00_rt(rt2x00dev, RT5390) || rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392) || rt2x00_rt(rt2x00dev, RT5392) ||
rt2x00_rt(rt2x00dev, RT5592)) rt2x00_rt(rt2x00dev, RT5592))
@ -4423,8 +4422,8 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
else else
vgc = 0x2e + rt2x00dev->lna_gain; vgc = 0x2e + rt2x00dev->lna_gain;
} else { /* 5GHZ band */ } else { /* 5GHZ band */
if (rt2x00_rt(rt2x00dev, RT3572)) if (rt2x00_rt(rt2x00dev, RT3593))
vgc = 0x22 + (rt2x00dev->lna_gain * 5) / 3; vgc = 0x20 + (rt2x00dev->lna_gain * 5) / 3;
else if (rt2x00_rt(rt2x00dev, RT5592)) else if (rt2x00_rt(rt2x00dev, RT5592))
vgc = 0x24 + (2 * rt2x00dev->lna_gain); vgc = 0x24 + (2 * rt2x00dev->lna_gain);
else { else {
@ -4442,11 +4441,17 @@ static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev,
struct link_qual *qual, u8 vgc_level) struct link_qual *qual, u8 vgc_level)
{ {
if (qual->vgc_level != 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(rt2x00dev, 83, qual->rssi > -65 ? 0x4a : 0x7a);
rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level); rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level);
} else } else {
rt2800_bbp_write(rt2x00dev, 66, vgc_level); rt2800_bbp_write(rt2x00dev, 66, vgc_level);
}
qual->vgc_level = vgc_level; qual->vgc_level = vgc_level;
qual->vgc_level_reg = 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)) if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C))
return; return;
/*
* When RSSI is better then -80 increase VGC level with 0x10, except /* When RSSI is better than a certain threshold, increase VGC
* for rt5592 chip. * with a chip specific value in order to improve the balance
* between sensibility and noise isolation.
*/ */
vgc = rt2800_get_default_vgc(rt2x00dev); vgc = rt2800_get_default_vgc(rt2x00dev);
if (rt2x00_rt(rt2x00dev, RT5592) && qual->rssi > -65) switch (rt2x00dev->chip.rt) {
case RT3572:
case RT3593:
if (qual->rssi > -65) {
if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ)
vgc += 0x20; vgc += 0x20;
else if (qual->rssi > -80) else
vgc += 0x10; 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); 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; ant = (div_mode == 3) ? 1 : 0;
/* check if this is a Bluetooth combo card */ /* 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; u32 reg;
rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg); rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
@ -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, RT3071, REV_RT3071E) ||
rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) || rt2x00_rt_rev_lt(rt2x00dev, RT3090, REV_RT3090E) ||
rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) { 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); 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, 28, 0x00);
rt2800_rfcsr_write(rt2x00dev, 29, 0x10); 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, 31, 0x80);
rt2800_rfcsr_write(rt2x00dev, 32, 0x80); rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
rt2800_rfcsr_write(rt2x00dev, 33, 0x00); 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, 56, 0x22);
rt2800_rfcsr_write(rt2x00dev, 57, 0x80); rt2800_rfcsr_write(rt2x00dev, 57, 0x80);
rt2800_rfcsr_write(rt2x00dev, 58, 0x7f); rt2800_rfcsr_write(rt2x00dev, 58, 0x7f);
rt2800_rfcsr_write(rt2x00dev, 59, 0x63); rt2800_rfcsr_write(rt2x00dev, 59, 0x8f);
rt2800_rfcsr_write(rt2x00dev, 60, 0x45); rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) 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_rf_init_calibration(rt2x00dev, 2);
rt2800_rfcsr_write(rt2x00dev, 1, 0x17); rt2800_rfcsr_write(rt2x00dev, 1, 0x17);
rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
rt2800_rfcsr_write(rt2x00dev, 3, 0x88); rt2800_rfcsr_write(rt2x00dev, 3, 0x88);
rt2800_rfcsr_write(rt2x00dev, 5, 0x10); rt2800_rfcsr_write(rt2x00dev, 5, 0x10);
rt2800_rfcsr_write(rt2x00dev, 6, 0xe0); rt2800_rfcsr_write(rt2x00dev, 6, 0xe0);
@ -7224,7 +7246,7 @@ static const struct rf_channel rf_vals[] = {
/* /*
* RF value list for rt3xxx * 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[] = { static const struct rf_channel rf_vals_3x[] = {
{1, 241, 2, 2 }, {1, 241, 2, 2 },
@ -7420,72 +7442,6 @@ static const struct rf_channel rf_vals_5592_xtal40[] = {
{196, 83, 0, 12, 1}, {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) static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
{ {
struct hw_mode_spec *spec = &rt2x00dev->spec; 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)) { rt2x00_rf(rt2x00dev, RF5392)) {
spec->num_channels = 14; spec->num_channels = 14;
spec->channels = rf_vals_3x; 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->supported_bands |= SUPPORT_BAND_5GHZ;
spec->num_channels = ARRAY_SIZE(rf_vals_3x); spec->num_channels = ARRAY_SIZE(rf_vals_3x);
spec->channels = 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)) { } else if (rt2x00_rf(rt2x00dev, RF5592)) {
spec->supported_bands |= SUPPORT_BAND_5GHZ; spec->supported_bands |= SUPPORT_BAND_5GHZ;

View File

@ -39,6 +39,7 @@
#include <linux/input-polldev.h> #include <linux/input-polldev.h>
#include <linux/kfifo.h> #include <linux/kfifo.h>
#include <linux/hrtimer.h> #include <linux/hrtimer.h>
#include <linux/average.h>
#include <net/mac80211.h> #include <net/mac80211.h>
@ -138,17 +139,6 @@
#define SHORT_EIFS ( SIFS + SHORT_DIFS + \ #define SHORT_EIFS ( SIFS + SHORT_DIFS + \
GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) ) 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 { enum rt2x00_chip_intf {
RT2X00_CHIP_INTF_PCI, RT2X00_CHIP_INTF_PCI,
RT2X00_CHIP_INTF_PCIE, RT2X00_CHIP_INTF_PCIE,
@ -297,7 +287,7 @@ struct link_ant {
* Similar to the avg_rssi in the link_qual structure * Similar to the avg_rssi in the link_qual structure
* this value is updated by using the walking average. * 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 * Currently active average RSSI value
*/ */
struct avg_val avg_rssi; struct ewma avg_rssi;
/* /*
* Work structure for scheduling periodic link tuning. * 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); 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. * rt2x00queue_map_txskb - Map a skb into DMA for TX purposes.
* @entry: Pointer to &struct queue_entry * @entry: Pointer to &struct queue_entry

View File

@ -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_tx_info *tx_info = IEEE80211_SKB_CB(skb);
struct ieee80211_key_conf *hw_key = tx_info->control.hw_key; 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; return;
__set_bit(ENTRY_TXD_ENCRYPT, &txdesc->flags); __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; struct ieee80211_key_conf *key = tx_info->control.hw_key;
unsigned int overhead = 0; unsigned int overhead = 0;
if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags) || !key) if (!rt2x00_has_cap_hw_crypto(rt2x00dev) || !key)
return overhead; return overhead;
/* /*

View File

@ -750,7 +750,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
intf, &rt2x00debug_fop_queue_stats); intf, &rt2x00debug_fop_queue_stats);
#ifdef CONFIG_RT2X00_LIB_CRYPTO #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 = intf->crypto_stats_entry =
debugfs_create_file("crypto", S_IRUGO, intf->queue_folder, debugfs_create_file("crypto", S_IRUGO, intf->queue_folder,
intf, &rt2x00debug_fop_crypto_stats); intf, &rt2x00debug_fop_crypto_stats);

View File

@ -88,7 +88,7 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
rt2x00queue_start_queues(rt2x00dev); rt2x00queue_start_queues(rt2x00dev);
rt2x00link_start_tuner(rt2x00dev); rt2x00link_start_tuner(rt2x00dev);
rt2x00link_start_agc(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); rt2x00link_start_vcocal(rt2x00dev);
/* /*
@ -113,7 +113,7 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
* Stop all queues * Stop all queues
*/ */
rt2x00link_stop_agc(rt2x00dev); 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_vcocal(rt2x00dev);
rt2x00link_stop_tuner(rt2x00dev); rt2x00link_stop_tuner(rt2x00dev);
rt2x00queue_stop_queues(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 * here as they will fetch the next beacon directly prior to
* transmission. * transmission.
*/ */
if (test_bit(CAPABILITY_PRE_TBTT_INTERRUPT, &rt2x00dev->cap_flags)) if (rt2x00_has_cap_pre_tbtt_interrupt(rt2x00dev))
return; return;
/* fetch next beacon */ /* 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 * mac80211 will expect the same data to be present it the
* frame as it was passed to us. * 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); rt2x00crypto_tx_insert_iv(entry->skb, header_length);
/* /*

View File

@ -35,50 +35,28 @@
*/ */
#define DEFAULT_RSSI -128 #define DEFAULT_RSSI -128
/* /* Constants for EWMA calculations. */
* Helper struct and macro to work with moving/walking averages. #define RT2X00_EWMA_FACTOR 1024
* When adding a value to the average value the following calculation #define RT2X00_EWMA_WEIGHT 8
* is needed:
* static inline int rt2x00link_get_avg_rssi(struct ewma *ewma)
* avg_rssi = ((avg_rssi * 7) + rssi) / 8; {
* unsigned long avg;
* 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). avg = ewma_read(ewma);
* But more importantly, normal average values will over time if (avg)
* move less and less towards newly added values this results return -avg;
* 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 return DEFAULT_RSSI;
* 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; \
})
static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev) static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev)
{ {
struct link_ant *ant = &rt2x00dev->link.ant; struct link_ant *ant = &rt2x00dev->link.ant;
if (ant->rssi_ant.avg && rt2x00dev->link.qual.rx_success) if (rt2x00dev->link.qual.rx_success)
return ant->rssi_ant.avg; return rt2x00link_get_avg_rssi(&ant->rssi_ant);
return DEFAULT_RSSI; 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) static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev)
{ {
rt2x00dev->link.ant.rssi_ant.avg = 0; ewma_init(&rt2x00dev->link.ant.rssi_ant, RT2X00_EWMA_FACTOR,
rt2x00dev->link.ant.rssi_ant.avg_weight = 0; RT2X00_EWMA_WEIGHT);
} }
static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev) 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 * Update global RSSI
*/ */
link->avg_rssi = MOVING_AVERAGE(link->avg_rssi, rxdesc->rssi); ewma_add(&link->avg_rssi, -rxdesc->rssi);
/* /*
* Update antenna 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) 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; rt2x00dev->link.count = 0;
memset(qual, 0, sizeof(*qual)); 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, * 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 * collect the RSSI data we could use this. Otherwise we
* must fallback to the default RSSI value. * must fallback to the default RSSI value.
*/ */
if (!link->avg_rssi.avg || !qual->rx_success) if (!qual->rx_success)
qual->rssi = DEFAULT_RSSI; qual->rssi = DEFAULT_RSSI;
else 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 * Check if link tuning is supported by the hardware, some hardware
* do not support link tuning at all, while other devices can disable * do not support link tuning at all, while other devices can disable
* the feature from the EEPROM. * 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); 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) void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
{ {
INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc); 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.vco_work, rt2x00link_vcocal);
INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog); INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);

View File

@ -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, * of different types, but has no a separate filter for PS Poll frames,
* FIF_CONTROL flag implies FIF_PSPOLL. * 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) if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL)
*total_flags |= FIF_CONTROL | 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) if (*total_flags & FIF_CONTROL)
*total_flags |= FIF_PSPOLL; *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)) if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
return 0; return 0;
if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) if (!rt2x00_has_cap_hw_crypto(rt2x00dev))
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* /*

View File

@ -119,7 +119,7 @@ int rt2x00pci_probe(struct pci_dev *pci_dev, const struct rt2x00_ops *ops)
rt2x00dev->ops = ops; rt2x00dev->ops = ops;
rt2x00dev->hw = hw; rt2x00dev->hw = hw;
rt2x00dev->irq = pci_dev->irq; rt2x00dev->irq = pci_dev->irq;
rt2x00dev->name = pci_name(pci_dev); rt2x00dev->name = ops->name;
if (pci_is_pcie(pci_dev)) if (pci_is_pcie(pci_dev))
rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE); rt2x00_set_chip_intf(rt2x00dev, RT2X00_CHIP_INTF_PCIE);

View File

@ -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 * at least 8 bytes bytes available in headroom for IV/EIV
* and 8 bytes for ICV data as tailroon. * 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; head_size += 8;
tail_size += 8; tail_size += 8;
} }
@ -1033,28 +1033,12 @@ EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue);
void rt2x00queue_flush_queue(struct data_queue *queue, bool drop) void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)
{ {
bool started;
bool tx_queue = bool tx_queue =
(queue->qid == QID_AC_VO) || (queue->qid == QID_AC_VO) ||
(queue->qid == QID_AC_VI) || (queue->qid == QID_AC_VI) ||
(queue->qid == QID_AC_BE) || (queue->qid == QID_AC_BE) ||
(queue->qid == QID_AC_BK); (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.
*/
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 * If we are not supposed to drop any pending
@ -1064,7 +1048,6 @@ void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)
*/ */
if (!drop && tx_queue) if (!drop && tx_queue)
queue->rt2x00dev->ops->lib->kick_queue(queue); queue->rt2x00dev->ops->lib->kick_queue(queue);
}
/* /*
* Check if driver supports flushing, if that is the case we can * 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))) if (unlikely(!rt2x00queue_empty(queue)))
rt2x00_warn(queue->rt2x00dev, "Queue %d failed to flush\n", rt2x00_warn(queue->rt2x00dev, "Queue %d failed to flush\n",
queue->qid); 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); EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue);

View File

@ -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", rt2x00_warn(queue->rt2x00dev, "TX queue %d DMA timed out, invoke forced forced reset\n",
queue->qid); queue->qid);
rt2x00queue_stop_queue(queue);
rt2x00queue_flush_queue(queue, true); rt2x00queue_flush_queue(queue, true);
rt2x00queue_start_queue(queue);
} }
static int rt2x00usb_dma_timeout(struct data_queue *queue) static int rt2x00usb_dma_timeout(struct data_queue *queue)

View File

@ -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(&r3, BBP_R3_SMART_MODE, rt2x00_rf(rt2x00dev, RF2529));
rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 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. * Configure the RX antenna.
@ -813,10 +813,10 @@ static void rt61pci_config_ant(struct rt2x00_dev *rt2x00dev,
if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
sel = antenna_sel_a; sel = antenna_sel_a;
lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); lna = rt2x00_has_cap_external_lna_a(rt2x00dev);
} else { } else {
sel = antenna_sel_bg; 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++) 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)) else if (rt2x00_rf(rt2x00dev, RF2527))
rt61pci_config_antenna_2x(rt2x00dev, ant); rt61pci_config_antenna_2x(rt2x00dev, ant);
else if (rt2x00_rf(rt2x00dev, RF2529)) { 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); rt61pci_config_antenna_2x(rt2x00dev, ant);
else else
rt61pci_config_antenna_2529(rt2x00dev, ant); rt61pci_config_antenna_2529(rt2x00dev, ant);
@ -850,13 +850,13 @@ static void rt61pci_config_lna_gain(struct rt2x00_dev *rt2x00dev,
short lna_gain = 0; short lna_gain = 0;
if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { 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; lna_gain += 14;
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom);
lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1); lna_gain -= rt2x00_get_field16(eeprom, EEPROM_RSSI_OFFSET_BG_1);
} else { } else {
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) if (rt2x00_has_cap_external_lna_a(rt2x00dev))
lna_gain += 14; lna_gain += 14;
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_A, &eeprom); 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) { if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
low_bound = 0x28; low_bound = 0x28;
up_bound = 0x48; up_bound = 0x48;
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
low_bound += 0x10; low_bound += 0x10;
up_bound += 0x10; up_bound += 0x10;
} }
} else { } else {
low_bound = 0x20; low_bound = 0x20;
up_bound = 0x40; up_bound = 0x40;
if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) { if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
low_bound += 0x10; low_bound += 0x10;
up_bound += 0x10; up_bound += 0x10;
} }
@ -2578,7 +2578,7 @@ static int rt61pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
* eeprom word. * eeprom word.
*/ */
if (rt2x00_rf(rt2x00dev, RF2529) && if (rt2x00_rf(rt2x00dev, RF2529) &&
!test_bit(CAPABILITY_DOUBLE_ANTENNA, &rt2x00dev->cap_flags)) { !rt2x00_has_cap_double_antenna(rt2x00dev)) {
rt2x00dev->default_ant.rx = rt2x00dev->default_ant.rx =
ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED); ANTENNA_A + rt2x00_get_field16(eeprom, EEPROM_NIC_RX_FIXED);
rt2x00dev->default_ant.tx = 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_bands = SUPPORT_BAND_2GHZ;
spec->supported_rates = SUPPORT_RATE_CCK | SUPPORT_RATE_OFDM; 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->num_channels = 14;
spec->channels = rf_vals_noseq; spec->channels = rf_vals_noseq;
} else { } else {

View File

@ -595,8 +595,8 @@ static void rt73usb_config_antenna_5x(struct rt2x00_dev *rt2x00dev,
switch (ant->rx) { switch (ant->rx) {
case ANTENNA_HW_DIVERSITY: case ANTENNA_HW_DIVERSITY:
rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2); rt2x00_set_field8(&r4, BBP_R4_RX_ANTENNA_CONTROL, 2);
temp = !test_bit(CAPABILITY_FRAME_TYPE, &rt2x00dev->cap_flags) temp = !rt2x00_has_cap_frame_type(rt2x00dev) &&
&& (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ); (rt2x00dev->curr_band != IEEE80211_BAND_5GHZ);
rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp); rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, temp);
break; break;
case ANTENNA_A: 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(&r3, BBP_R3_SMART_MODE, 0);
rt2x00_set_field8(&r4, BBP_R4_RX_FRAME_END, 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. * Configure the RX antenna.
@ -709,10 +709,10 @@ static void rt73usb_config_ant(struct rt2x00_dev *rt2x00dev,
if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) { if (rt2x00dev->curr_band == IEEE80211_BAND_5GHZ) {
sel = antenna_sel_a; sel = antenna_sel_a;
lna = test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags); lna = rt2x00_has_cap_external_lna_a(rt2x00dev);
} else { } else {
sel = antenna_sel_bg; 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++) 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; short lna_gain = 0;
if (libconf->conf->chandef.chan->band == IEEE80211_BAND_2GHZ) { 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; lna_gain += 14;
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_OFFSET_BG, &eeprom); 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; low_bound = 0x28;
up_bound = 0x48; up_bound = 0x48;
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags)) { if (rt2x00_has_cap_external_lna_a(rt2x00dev)) {
low_bound += 0x10; low_bound += 0x10;
up_bound += 0x10; up_bound += 0x10;
} }
@ -946,7 +946,7 @@ static void rt73usb_link_tuner(struct rt2x00_dev *rt2x00dev,
up_bound = 0x1c; up_bound = 0x1c;
} }
if (test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags)) { if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
low_bound += 0x14; low_bound += 0x14;
up_bound += 0x10; 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 (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) if (lna == 3 || lna == 2)
offset += 10; offset += 10;
} else { } else {

View File

@ -424,8 +424,8 @@ void wl1251_disable_interrupts(struct wl1251 *wl);
#define CHIP_ID_1271_PG10 (0x4030101) #define CHIP_ID_1271_PG10 (0x4030101)
#define CHIP_ID_1271_PG20 (0x4030111) #define CHIP_ID_1271_PG20 (0x4030111)
#define WL1251_FW_NAME "wl1251-fw.bin" #define WL1251_FW_NAME "ti-connectivity/wl1251-fw.bin"
#define WL1251_NVS_NAME "wl1251-nvs.bin" #define WL1251_NVS_NAME "ti-connectivity/wl1251-nvs.bin"
#define WL1251_POWER_ON_SLEEP 10 /* in milliseconds */ #define WL1251_POWER_ON_SLEEP 10 /* in milliseconds */

View File

@ -1391,8 +1391,8 @@ struct ieee80211_vht_operation {
#define IEEE80211_VHT_CAP_RXSTBC_MASK 0x00000700 #define IEEE80211_VHT_CAP_RXSTBC_MASK 0x00000700
#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800 #define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800
#define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000 #define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000
#define IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX 0x00006000 #define IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX 0x0000e000
#define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX 0x00030000 #define IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX 0x00070000
#define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000 #define IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE 0x00080000
#define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000 #define IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE 0x00100000
#define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000 #define IEEE80211_VHT_CAP_VHT_TXOP_PS 0x00200000

View File

@ -436,6 +436,15 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef, const struct cfg80211_chan_def *chandef,
u32 prohibited_flags); 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 * ieee80211_chandef_rate_flags - returns rate flags for a channel
* *

View File

@ -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_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_10MHZ: 10 MHz (half channel) was used
* @RX_FLAG_5MHZ: 5 MHz (quarter 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 { enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0), RX_FLAG_MMIC_ERROR = BIT(0),
@ -859,6 +868,7 @@ enum mac80211_rx_flags {
RX_FLAG_STBC_MASK = BIT(26) | BIT(27), RX_FLAG_STBC_MASK = BIT(26) | BIT(27),
RX_FLAG_10MHZ = BIT(28), RX_FLAG_10MHZ = BIT(28),
RX_FLAG_5MHZ = BIT(29), RX_FLAG_5MHZ = BIT(29),
RX_FLAG_AMSDU_MORE = BIT(30),
}; };
#define RX_FLAG_STBC_SHIFT 26 #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 * @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
* only, to allow getting TBTT of a DTIM beacon. * 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 { enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, 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_P2P_DEV_ADDR_FOR_INTF = 1<<25,
IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26, IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26,
IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27, 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 * zero using ieee80211_csa_is_complete() after the beacon has been
* transmitted and then call ieee80211_csa_finish(). * 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 { struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw, void (*tx)(struct ieee80211_hw *hw,
@ -2857,6 +2877,9 @@ struct ieee80211_ops {
void (*channel_switch_beacon)(struct ieee80211_hw *hw, void (*channel_switch_beacon)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct cfg80211_chan_def *chandef); 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), struct ieee80211_vif *vif),
void *data); 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 * ieee80211_queue_work - add work onto the mac80211 workqueue
* *

View File

@ -2865,30 +2865,43 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata))
return; return;
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
return;
sdata->radar_required = sdata->csa_radar_required; sdata->radar_required = sdata->csa_radar_required;
err = ieee80211_vif_change_channel(sdata, &local->csa_chandef, err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
&changed); &changed);
if (WARN_ON(err < 0)) if (WARN_ON(err < 0))
return; return;
if (!local->use_chanctx) {
local->_oper_chandef = local->csa_chandef;
ieee80211_hw_config(local, 0);
}
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); err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
if (err < 0) if (err < 0)
return; return;
changed |= err; changed |= err;
kfree(sdata->u.ap.next_beacon); kfree(sdata->u.ap.next_beacon);
sdata->u.ap.next_beacon = NULL; 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; sdata->vif.csa_active = false;
ieee80211_wake_queues_by_reason(&sdata->local->hw, ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_MAX_QUEUE_MAP, IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA); IEEE80211_QUEUE_STOP_REASON_CSA);
ieee80211_bss_info_change_notify(sdata, changed);
cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef); 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) if (sdata->vif.csa_active)
return -EBUSY; return -EBUSY;
/* only handle AP for now. */
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_AP: 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(&params->beacon_after);
if (!sdata->u.ap.next_beacon)
return -ENOMEM;
err = ieee80211_assign_beacon(sdata, &params->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(&params->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; break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
sdata->u.ap.next_beacon = cfg80211_beacon_dup(&params->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; sdata->csa_radar_required = params->radar_required;
if (params->block_tx) 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_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA); IEEE80211_QUEUE_STOP_REASON_CSA);
err = ieee80211_assign_beacon(sdata, &params->beacon_csa);
if (err < 0)
return err;
local->csa_chandef = params->chandef; local->csa_chandef = params->chandef;
sdata->vif.csa_active = true; sdata->vif.csa_active = true;
@ -3014,7 +3059,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
need_offchan = true; need_offchan = true;
if (!ieee80211_is_action(mgmt->frame_control) || if (!ieee80211_is_action(mgmt->frame_control) ||
mgmt->u.action.category == WLAN_CATEGORY_PUBLIC || 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; break;
rcu_read_lock(); rcu_read_lock();
sta = sta_info_get(sdata, mgmt->da); sta = sta_info_get(sdata, mgmt->da);

View File

@ -453,11 +453,6 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL; chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
drv_change_chanctx(local, ctx, chanctx_changed); 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_chanctx_chantype(local, ctx);
ieee80211_recalc_smps_chanctx(local, ctx); ieee80211_recalc_smps_chanctx(local, ctx);
ieee80211_recalc_radar_chanctx(local, ctx); ieee80211_recalc_radar_chanctx(local, ctx);

View File

@ -103,54 +103,57 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
if (!buf) if (!buf)
return 0; 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) 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) 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) if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)
sf += snprintf(buf + sf, mxln - sf, sf += scnprintf(buf + sf, mxln - sf,
"HOST_BCAST_PS_BUFFERING\n"); "HOST_BCAST_PS_BUFFERING\n");
if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE) if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)
sf += snprintf(buf + sf, mxln - sf, sf += scnprintf(buf + sf, mxln - sf,
"2GHZ_SHORT_SLOT_INCAPABLE\n"); "2GHZ_SHORT_SLOT_INCAPABLE\n");
if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE) if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)
sf += snprintf(buf + sf, mxln - sf, sf += scnprintf(buf + sf, mxln - sf,
"2GHZ_SHORT_PREAMBLE_INCAPABLE\n"); "2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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)); rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
kfree(buf); kfree(buf);

View File

@ -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 */ #endif /* __MAC80211_DRIVER_OPS */

View File

@ -39,7 +39,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
const int beacon_int, const u32 basic_rates, const int beacon_int, const u32 basic_rates,
const u16 capability, u64 tsf, const u16 capability, u64 tsf,
struct cfg80211_chan_def *chandef, 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_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local; 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 */ + 2 + 8 /* max Supported Rates */ +
3 /* max DS params */ + 3 /* max DS params */ +
4 /* IBSS params */ + 4 /* IBSS params */ +
5 /* Channel Switch Announcement */ +
2 + (IEEE80211_MAX_SUPP_RATES - 8) + 2 + (IEEE80211_MAX_SUPP_RATES - 8) +
2 + sizeof(struct ieee80211_ht_cap) + 2 + sizeof(struct ieee80211_ht_cap) +
2 + sizeof(struct ieee80211_ht_operation) + 2 + sizeof(struct ieee80211_ht_operation) +
@ -135,6 +137,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
*pos++ = 0; *pos++ = 0;
*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 */ /* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
if (rates_n > 8) { if (rates_n > 8) {
*pos++ = WLAN_EID_EXT_SUPP_RATES; *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; struct beacon_data *presp;
enum nl80211_bss_scan_width scan_width; enum nl80211_bss_scan_width scan_width;
bool have_higher_than_11mbit; bool have_higher_than_11mbit;
int err;
sdata_assert_lock(sdata); 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, ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_IBSS | BSS_CHANGED_IBSS |
BSS_CHANGED_BEACON_ENABLED); BSS_CHANGED_BEACON_ENABLED);
drv_leave_ibss(local, sdata);
} }
presp = rcu_dereference_protected(ifibss->presp, 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, presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
capability, tsf, &chandef, capability, tsf, &chandef,
&have_higher_than_11mbit); &have_higher_than_11mbit, NULL);
if (!presp) if (!presp)
return; return;
@ -317,11 +331,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
else else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE; sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
ieee80211_set_wmm_default(sdata, true);
sdata->vif.bss_conf.ibss_joined = true; sdata->vif.bss_conf.ibss_joined = true;
sdata->vif.bss_conf.ibss_creator = creator; 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; ifibss->state = IEEE80211_IBSS_MLME_JOINED;
mod_timer(&ifibss->timer, mod_timer(&ifibss->timer,
@ -416,6 +445,169 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
tsf, false); 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) static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
__acquires(RCU) __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); 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(&params, 0, sizeof(params));
err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
ifibss->chandef.chan->band,
sta_flags, ifibss->bssid,
&params.count, &mode,
&params.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(&params.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, &params.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,
&params.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, &params);
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, &params.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, static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, struct ieee80211_mgmt *mgmt,
size_t len) 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 */ /* check if we need to merge IBSS */
/* we use a fixed BSSID */
if (sdata->u.ibss.fixed_bssid)
goto put_bss;
/* not an IBSS */ /* not an IBSS */
if (!(cbss->capability & WLAN_CAPABILITY_IBSS)) if (!(cbss->capability & WLAN_CAPABILITY_IBSS))
goto put_bss; goto put_bss;
@ -680,10 +1157,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sdata->u.ibss.ssid_len)) sdata->u.ibss.ssid_len))
goto put_bss; goto put_bss;
/* process channel switch */
if (ieee80211_ibss_process_chanswitch(sdata, elems, true))
goto put_bss;
/* same BSSID */ /* same BSSID */
if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid)) if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))
goto put_bss; goto put_bss;
/* we use a fixed BSSID */
if (sdata->u.ibss.fixed_bssid)
goto put_bss;
if (ieee80211_have_rx_timestamp(rx_status)) { if (ieee80211_have_rx_timestamp(rx_status)) {
/* time when timestamp field was received */ /* time when timestamp field was received */
rx_timestamp = 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); 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) static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_local *local = sdata->local; 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_rx_status *rx_status;
struct ieee80211_mgmt *mgmt; struct ieee80211_mgmt *mgmt;
u16 fc; u16 fc;
struct ieee802_11_elems elems;
int ies_len;
rx_status = IEEE80211_SKB_RXCB(skb); rx_status = IEEE80211_SKB_RXCB(skb);
mgmt = (struct ieee80211_mgmt *) skb->data; 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: case IEEE80211_STYPE_DEAUTH:
ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len); ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
break; 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: mgmt_out:
@ -1167,6 +1651,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
(unsigned long) sdata); (unsigned long) sdata);
INIT_LIST_HEAD(&ifibss->incomplete_stations); INIT_LIST_HEAD(&ifibss->incomplete_stations);
spin_lock_init(&ifibss->incomplete_lock); spin_lock_init(&ifibss->incomplete_lock);
INIT_WORK(&ifibss->csa_connection_drop_work,
ieee80211_csa_connection_drop_work);
} }
/* scan finished notification */ /* 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) int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; 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); ieee80211_ibss_disconnect(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);
ifibss->ssid_len = 0; ifibss->ssid_len = 0;
memset(ifibss->bssid, 0, ETH_ALEN);
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);
/* remove beacon */ /* remove beacon */
kfree(sdata->u.ibss.ie); 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 */ /* on the next join, re-program HT parameters */
memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa)); memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask)); 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(); synchronize_rcu();
kfree(presp);
skb_queue_purge(&sdata->skb_queue); skb_queue_purge(&sdata->skb_queue);

View File

@ -322,7 +322,6 @@ struct ieee80211_roc_work {
/* flags used in struct ieee80211_if_managed.flags */ /* flags used in struct ieee80211_if_managed.flags */
enum ieee80211_sta_flags { enum ieee80211_sta_flags {
IEEE80211_STA_BEACON_POLL = BIT(0),
IEEE80211_STA_CONNECTION_POLL = BIT(1), IEEE80211_STA_CONNECTION_POLL = BIT(1),
IEEE80211_STA_CONTROL_PORT = BIT(2), IEEE80211_STA_CONTROL_PORT = BIT(2),
IEEE80211_STA_DISABLE_HT = BIT(4), IEEE80211_STA_DISABLE_HT = BIT(4),
@ -487,6 +486,7 @@ struct ieee80211_if_managed {
struct ieee80211_if_ibss { struct ieee80211_if_ibss {
struct timer_list timer; struct timer_list timer;
struct work_struct csa_connection_drop_work;
unsigned long last_scan_completed; 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_work(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb); 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 */ /* mesh code */
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata); 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, void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, struct ieee80211_mgmt *mgmt,
size_t len); 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 */ /* Suspend/resume and hw reconfiguration */
int ieee80211_reconfig(struct ieee80211_local *local); 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, void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
const struct ieee80211_ht_operation *ht_oper, const struct ieee80211_ht_operation *ht_oper,
struct cfg80211_chan_def *chandef); struct cfg80211_chan_def *chandef);
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
int __must_check int __must_check
ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,

View File

@ -766,6 +766,10 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
if (sdata->vif.type == NL80211_IFTYPE_STATION) if (sdata->vif.type == NL80211_IFTYPE_STATION)
ieee80211_mgd_stop(sdata); ieee80211_mgd_stop(sdata);
if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
ieee80211_ibss_stop(sdata);
/* /*
* Remove all stations associated with this interface. * Remove all stations associated with this interface.
* *

View File

@ -879,7 +879,7 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
keyconf->keylen, keyconf->key, keyconf->keylen, keyconf->key,
0, NULL); 0, NULL);
if (IS_ERR(key)) if (IS_ERR(key))
return ERR_PTR(PTR_ERR(key)); return ERR_CAST(key);
if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED) if (sdata->u.mgd.mfp != IEEE80211_MFP_DISABLED)
key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT; key->conf.flags |= IEEE80211_KEY_FLAG_RX_MGMT;

View File

@ -145,66 +145,6 @@ static int ecw2cw(int ecw)
return (1 << ecw) - 1; 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 static u32
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata, ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband, struct ieee80211_supported_band *sband,
@ -352,7 +292,7 @@ out:
break; break;
} }
ret |= chandef_downgrade(chandef); ret |= ieee80211_chandef_downgrade(chandef);
} }
if (chandef->width != vht_chandef.width && !tracking) 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 && if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
chandef.width == NL80211_CHAN_WIDTH_80P80) chandef.width == NL80211_CHAN_WIDTH_80P80)
flags |= chandef_downgrade(&chandef); flags |= ieee80211_chandef_downgrade(&chandef);
if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ && if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
chandef.width == NL80211_CHAN_WIDTH_160) chandef.width == NL80211_CHAN_WIDTH_160)
flags |= chandef_downgrade(&chandef); flags |= ieee80211_chandef_downgrade(&chandef);
if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ && if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
chandef.width > NL80211_CHAN_WIDTH_20) 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)) if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
return 0; return 0;
@ -893,8 +833,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS; IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
IEEE80211_STA_CONNECTION_POLL))
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE; IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
ieee80211_tx_skb(sdata, skb); 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); container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u32 changed = 0;
int ret;
if (!ieee80211_sdata_running(sdata)) if (!ieee80211_sdata_running(sdata))
return; return;
@ -945,24 +886,39 @@ static void ieee80211_chswitch_work(struct work_struct *work)
if (!ifmgd->associated) if (!ifmgd->associated)
goto out; 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) { if (!local->use_chanctx) {
/* call "hw_config" only if doing sw channel switch */ local->_oper_chandef = local->csa_chandef;
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); /* Call "hw_config" only if doing sw channel switch.
} else { * Otherwise update the channel directly
/* update the device channel directly */ */
if (!local->ops->channel_switch)
ieee80211_hw_config(local, 0);
else
local->hw.conf.chandef = local->_oper_chandef; local->hw.conf.chandef = local->_oper_chandef;
} }
/* XXX: shouldn't really modify cfg80211-owned data! */ /* 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? */ /* XXX: wait for a beacon first? */
ieee80211_wake_queues_by_reason(&local->hw, ieee80211_wake_queues_by_reason(&local->hw,
IEEE80211_MAX_QUEUE_MAP, IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA); IEEE80211_QUEUE_STOP_REASON_CSA);
ieee80211_bss_info_change_notify(sdata, changed);
out: out:
sdata->vif.csa_active = false;
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
sdata_unlock(sdata); 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_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct cfg80211_bss *cbss = ifmgd->associated; struct cfg80211_bss *cbss = ifmgd->associated;
struct ieee80211_bss *bss;
struct ieee80211_chanctx *chanctx; struct ieee80211_chanctx *chanctx;
enum ieee80211_band new_band; enum ieee80211_band current_band;
int new_freq;
u8 new_chan_no;
u8 count; u8 count;
u8 mode; u8 mode;
struct ieee80211_channel *new_chan;
struct cfg80211_chan_def new_chandef = {}; struct cfg80211_chan_def new_chandef = {};
struct cfg80211_chan_def new_vht_chandef = {}; int res;
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;
sdata_assert_lock(sdata); 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) if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
return; return;
sec_chan_offs = elems->sec_chan_offs; current_band = cbss->channel->band;
wide_bw_chansw_ie = elems->wide_bw_chansw_ie; res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
ht_oper = elems->ht_operation; ifmgd->flags,
ifmgd->associated->bssid, &count,
if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT | &mode, &new_chandef);
IEEE80211_STA_DISABLE_40MHZ)) { if (res < 0)
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, ieee80211_queue_work(&local->hw,
&ifmgd->csa_connection_drop_work); &ifmgd->csa_connection_drop_work);
} if (res)
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; 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);
ieee80211_queue_work(&local->hw,
&ifmgd->csa_connection_drop_work);
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, if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
IEEE80211_CHAN_DISABLED)) { IEEE80211_CHAN_DISABLED)) {
sdata_info(sdata, sdata_info(sdata,
"AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n", "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.width, new_chandef.center_freq1,
new_chandef.center_freq2); new_chandef.center_freq2);
ieee80211_queue_work(&local->hw, 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; ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
sdata->vif.csa_active = true;
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;
}
mutex_lock(&local->chanctx_mtx); 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))) { 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); mutex_unlock(&local->chanctx_mtx);
return; return;
} }
@ -1374,8 +1194,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
if (!mgd->associated) if (!mgd->associated)
return false; return false;
if (mgd->flags & (IEEE80211_STA_BEACON_POLL | if (mgd->flags & IEEE80211_STA_CONNECTION_POLL)
IEEE80211_STA_CONNECTION_POLL))
return false; return false;
if (!mgd->have_beacon) 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); lockdep_assert_held(&sdata->local->mtx);
sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL | sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;
IEEE80211_STA_BEACON_POLL);
ieee80211_run_deferred_scan(sdata->local); 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; struct ieee80211_local *local = sdata->local;
mutex_lock(&local->mtx); mutex_lock(&local->mtx);
if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL | if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL))
IEEE80211_STA_CONNECTION_POLL))) { goto out;
mutex_unlock(&local->mtx);
return;
}
__ieee80211_stop_poll(sdata); __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 * because otherwise we would reset the timer every time and
* never check whether we received a probe response! * never check whether we received a probe response!
*/ */
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
IEEE80211_STA_CONNECTION_POLL))
already = true; already = true;
if (beacon)
ifmgd->flags |= IEEE80211_STA_BEACON_POLL;
else
ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
mutex_unlock(&sdata->local->mtx); mutex_unlock(&sdata->local->mtx);
if (already) if (already)
@ -2174,6 +1983,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
true, frame_buf); true, frame_buf);
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
sdata->vif.csa_active = false;
ieee80211_wake_queues_by_reason(&sdata->local->hw, ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_MAX_QUEUE_MAP, IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA); 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, mlme_dbg_ratelimited(sdata,
"cancelling AP probe due to a received beacon\n"); "cancelling AP probe due to a received beacon\n");
mutex_lock(&local->mtx); ieee80211_reset_ap_probe(sdata);
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);
} }
/* /*
@ -3543,8 +3346,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
run_again(sdata, ifmgd->assoc_data->timeout); run_again(sdata, ifmgd->assoc_data->timeout);
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL | if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
IEEE80211_STA_CONNECTION_POLL) &&
ifmgd->associated) { ifmgd->associated) {
u8 bssid[ETH_ALEN]; u8 bssid[ETH_ALEN];
int max_tries; int max_tries;
@ -3876,7 +3678,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
return ret; return ret;
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) { 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, ret = ieee80211_vif_use_channel(sdata, &chandef,
IEEE80211_CHANCTX_SHARED); IEEE80211_CHANCTX_SHARED);
} }

View File

@ -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)); memcpy(mi->max_tp_rate, tmp_tp_rate, sizeof(mi->max_tp_rate));
mi->max_prob_rate = tmp_prob_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 */ /* Reset update timer */
mi->stats_update = jiffies; mi->stats_update = jiffies;
@ -310,6 +319,11 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
/* increase sum packet counter */ /* increase sum packet counter */
mi->packet_count++; mi->packet_count++;
#ifdef CONFIG_MAC80211_DEBUGFS
if (mp->fixed_rate_idx != -1)
return;
#endif
delta = (mi->packet_count * sampling_ratio / 100) - delta = (mi->packet_count * sampling_ratio / 100) -
(mi->sample_count + mi->sample_deferred / 2); (mi->sample_count + mi->sample_deferred / 2);

View File

@ -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; 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; info->flags |= mi->tx_flags;
minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble); 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 */ /* Don't use EAPOL frames for sampling on non-mrr hw */
if (mp->hw->max_rates == 1 && if (mp->hw->max_rates == 1 &&
(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO)) (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 else
sample_idx = minstrel_get_sample_rate(mp, mi); 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++; mi->total_packets++;
/* wraparound */ /* wraparound */

View File

@ -167,29 +167,29 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
* provide large enough buffers. */ * provide large enough buffers. */
length = length < RC_PID_PRINT_BUF_SIZE ? length = length < RC_PID_PRINT_BUF_SIZE ?
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) { switch (ev->type) {
case RC_PID_EVENT_TYPE_TX_STATUS: case RC_PID_EVENT_TYPE_TX_STATUS:
p += snprintf(pb + p, length - p, "tx_status %u %u", p += scnprintf(pb + p, length - p, "tx_status %u %u",
!(ev->data.flags & IEEE80211_TX_STAT_ACK), !(ev->data.flags & IEEE80211_TX_STAT_ACK),
ev->data.tx_status.status.rates[0].idx); ev->data.tx_status.status.rates[0].idx);
break; break;
case RC_PID_EVENT_TYPE_RATE_CHANGE: case RC_PID_EVENT_TYPE_RATE_CHANGE:
p += snprintf(pb + p, length - p, "rate_change %d %d", p += scnprintf(pb + p, length - p, "rate_change %d %d",
ev->data.index, ev->data.rate); ev->data.index, ev->data.rate);
break; break;
case RC_PID_EVENT_TYPE_TX_RATE: case RC_PID_EVENT_TYPE_TX_RATE:
p += snprintf(pb + p, length - p, "tx_rate %d %d", p += scnprintf(pb + p, length - p, "tx_rate %d %d",
ev->data.index, ev->data.rate); ev->data.index, ev->data.rate);
break; break;
case RC_PID_EVENT_TYPE_PF_SAMPLE: case RC_PID_EVENT_TYPE_PF_SAMPLE:
p += snprintf(pb + p, length - p, p += scnprintf(pb + p, length - p,
"pf_sample %d %d %d %d", "pf_sample %d %d %d %d",
ev->data.pf_sample, ev->data.prop_err, ev->data.pf_sample, ev->data.prop_err,
ev->data.int_err, ev->data.der_err); ev->data.int_err, ev->data.der_err);
break; break;
} }
p += snprintf(pb + p, length - p, "\n"); p += scnprintf(pb + p, length - p, "\n");
spin_unlock_irqrestore(&events->lock, status); spin_unlock_irqrestore(&events->lock, status);

View File

@ -995,9 +995,10 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
rx->sta->num_duplicates++; rx->sta->num_duplicates++;
} }
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
} else } else if (!(status->flag & RX_FLAG_AMSDU_MORE)) {
rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl; rx->sta->last_seq_ctrl[rx->seqno_idx] = hdr->seq_ctrl;
} }
}
if (unlikely(rx->skb->len < 16)) { if (unlikely(rx->skb->len < 16)) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_short); I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
@ -2402,7 +2403,8 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
return RX_DROP_UNUSABLE; return RX_DROP_UNUSABLE;
if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC && 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; return RX_DROP_UNUSABLE;
if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) if (!(status->rx_flags & IEEE80211_RX_RA_MATCH))
@ -2566,32 +2568,47 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
goto queue; goto queue;
case WLAN_CATEGORY_SPECTRUM_MGMT: 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 */ /* verify action_code is present */
if (len < IEEE80211_MIN_ACTION_SIZE + 1) if (len < IEEE80211_MIN_ACTION_SIZE + 1)
break; break;
switch (mgmt->u.action.u.measurement.action_code) { switch (mgmt->u.action.u.measurement.action_code) {
case WLAN_ACTION_SPCT_MSR_REQ: case WLAN_ACTION_SPCT_MSR_REQ:
if (status->band != IEEE80211_BAND_5GHZ)
break;
if (len < (IEEE80211_MIN_ACTION_SIZE + if (len < (IEEE80211_MIN_ACTION_SIZE +
sizeof(mgmt->u.action.u.measurement))) sizeof(mgmt->u.action.u.measurement)))
break; break;
ieee80211_process_measurement_req(sdata, mgmt, len);
goto handled;
case WLAN_ACTION_SPCT_CHL_SWITCH:
if (sdata->vif.type != NL80211_IFTYPE_STATION) if (sdata->vif.type != NL80211_IFTYPE_STATION)
break; 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; break;
goto queue; goto queue;
} }
}
break; break;
case WLAN_CATEGORY_SA_QUERY: case WLAN_CATEGORY_SA_QUERY:
if (len < (IEEE80211_MIN_ACTION_SIZE + if (len < (IEEE80211_MIN_ACTION_SIZE +

View File

@ -391,8 +391,7 @@ static bool ieee80211_can_scan(struct ieee80211_local *local,
return false; return false;
if (sdata->vif.type == NL80211_IFTYPE_STATION && if (sdata->vif.type == NL80211_IFTYPE_STATION &&
sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL | sdata->u.mgd.flags & IEEE80211_STA_CONNECTION_POLL)
IEEE80211_STA_CONNECTION_POLL))
return false; return false;
return true; return true;

View File

@ -21,6 +21,168 @@
#include "sta_info.h" #include "sta_info.h"
#include "wme.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, static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
struct ieee80211_msrment_ie *request_ie, struct ieee80211_msrment_ie *request_ie,
const u8 *da, const u8 *bssid, const u8 *da, const u8 *bssid,

View File

@ -1475,6 +1475,41 @@ DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change,
); );
#endif #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. * Tracing for API calls that drivers call.
*/ */

View File

@ -1981,7 +1981,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
* EAPOL frames from the local station. * EAPOL frames from the local station.
*/ */
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) && 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 || (cpu_to_be16(ethertype) != sdata->control_port_protocol ||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) { !ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG #ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@ -2357,15 +2357,31 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
struct probe_resp *resp; struct probe_resp *resp;
int counter_offset_beacon = sdata->csa_counter_offset_beacon; int counter_offset_beacon = sdata->csa_counter_offset_beacon;
int counter_offset_presp = sdata->csa_counter_offset_presp; 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 */ switch (sdata->vif.type) {
if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0)) 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; 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 && beacon_data[counter_offset_beacon]--;
counter_offset_presp) {
if (sdata->vif.type == NL80211_IFTYPE_AP && counter_offset_presp) {
rcu_read_lock(); rcu_read_lock();
resp = rcu_dereference(sdata->u.ap.probe_resp); resp = rcu_dereference(sdata->u.ap.probe_resp);
@ -2400,6 +2416,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
goto out; goto out;
beacon_data = beacon->tail; beacon_data = beacon->tail;
beacon_data_len = beacon->tail_len; 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 { } else {
WARN_ON(1); WARN_ON(1);
goto out; goto out;
@ -2484,6 +2509,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
if (!presp) if (!presp)
goto out; goto out;
if (sdata->vif.csa_active)
ieee80211_update_csa(sdata, presp);
skb = dev_alloc_skb(local->tx_headroom + presp->head_len); skb = dev_alloc_skb(local->tx_headroom + presp->head_len);
if (!skb) if (!skb)
goto out; goto out;

View File

@ -567,58 +567,14 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
IEEE80211_QUEUE_STOP_REASON_FLUSH); IEEE80211_QUEUE_STOP_REASON_FLUSH);
} }
void ieee80211_iterate_active_interfaces( static void __iterate_active_interfaces(struct ieee80211_local *local,
struct ieee80211_hw *hw, u32 iter_flags, u32 iter_flags,
void (*iterator)(void *data, u8 *mac, void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif), struct ieee80211_vif *vif),
void *data) void *data)
{ {
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata; 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) { list_for_each_entry_rcu(sdata, &local->interfaces, list) {
switch (sdata->vif.type) { switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
@ -638,16 +594,57 @@ void ieee80211_iterate_active_interfaces_atomic(
&sdata->vif); &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 && if (sdata &&
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
sdata->flags & IEEE80211_SDATA_IN_DRIVER)) sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif); 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(); rcu_read_unlock();
} }
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); 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 * Nothing should have been stuffed into the workqueue during
* the suspend->resume cycle. If this WARN is seen then there * the suspend->resume cycle. If this WARN is seen then there
@ -1007,7 +1004,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
*/ */
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION); 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 */ /* Set defaults according to 802.11-2007 Table 7-37 */
aCWmax = 1023; aCWmax = 1023;
if (use_11b) if (use_11b)
@ -1015,6 +1011,14 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
else else
aCWmin = 15; 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) { if (enable_qos) {
switch (ac) { switch (ac) {
case IEEE80211_AC_BK: case IEEE80211_AC_BK:
@ -1050,12 +1054,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
qparam.aifs = 2; qparam.aifs = 2;
break; 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; qparam.uapsd = false;
@ -1084,8 +1082,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt; struct ieee80211_mgmt *mgmt;
int err; int err;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + /* 24 + 6 = header + auth_algo + auth_transaction + status_code */
sizeof(*mgmt) + 6 + extra_len); skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len);
if (!skb) if (!skb)
return; return;
@ -2292,3 +2290,63 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw)
ieee80211_queue_work(hw, &local->radar_detected_work); ieee80211_queue_work(hw, &local->radar_detected_work);
} }
EXPORT_SYMBOL(ieee80211_radar_detected); 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;
}

View File

@ -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) { if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) {
vht_cap->cap |= cap_info & vht_cap->cap |= cap_info &
(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
IEEE80211_VHT_CAP_BEAMFORMER_ANTENNAS_MAX |
IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX); IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX);
} }
if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE) if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
vht_cap->cap |= cap_info & 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) if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
vht_cap->cap |= cap_info & vht_cap->cap |= cap_info &

View File

@ -328,6 +328,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2, return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
width); width);
} }
EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
u32 center_freq, u32 bandwidth, u32 center_freq, u32 bandwidth,

View File

@ -382,15 +382,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
enum cfg80211_chan_mode chanmode, enum cfg80211_chan_mode chanmode,
u8 radar_detect); 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, void cfg80211_set_dfs_state(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef, const struct cfg80211_chan_def *chandef,
enum nl80211_dfs_state dfs_state); enum nl80211_dfs_state dfs_state);

View File

@ -47,17 +47,19 @@ static int ht_print_chan(struct ieee80211_channel *chan,
return 0; return 0;
if (chan->flags & IEEE80211_CHAN_DISABLED) if (chan->flags & IEEE80211_CHAN_DISABLED)
return snprintf(buf + offset, return scnprintf(buf + offset,
buf_size - offset, buf_size - offset,
"%d Disabled\n", "%d Disabled\n",
chan->center_freq); chan->center_freq);
return snprintf(buf + offset, return scnprintf(buf + offset,
buf_size - offset, buf_size - offset,
"%d HT40 %c%c\n", "%d HT40 %c%c\n",
chan->center_freq, chan->center_freq,
(chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ? ' ' : '-', (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) ?
(chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ? ' ' : '+'); ' ' : '-',
(chan->flags & IEEE80211_CHAN_NO_HT40PLUS) ?
' ' : '+');
} }
static ssize_t ht40allow_map_read(struct file *file, static ssize_t ht40allow_map_read(struct file *file,

View File

@ -46,6 +46,12 @@ BEGIN {
sub(/:/, "", country) sub(/:/, "", country)
printf "static const struct ieee80211_regdomain regdom_%s = {\n", country printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
printf "\t.alpha2 = \"%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" printf "\t.reg_rules = {\n"
active = 1 active = 1
regdb = regdb "\t&regdom_" country ",\n" regdb = regdb "\t&regdom_" country ",\n"

View File

@ -5591,6 +5591,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (err) if (err)
return err; return err;
if (netif_carrier_ok(dev))
return -EBUSY;
if (wdev->cac_started) if (wdev->cac_started)
return -EBUSY; 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]; static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
u8 radar_detect_width = 0; u8 radar_detect_width = 0;
int err; int err;
bool need_new_beacon = false;
if (!rdev->ops->channel_switch || if (!rdev->ops->channel_switch ||
!(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
return -EOPNOTSUPP; return -EOPNOTSUPP;
/* may add IBSS support later */ switch (dev->ieee80211_ptr->iftype) {
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && case NL80211_IFTYPE_AP:
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) 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; return -EOPNOTSUPP;
}
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
@ -5651,15 +5665,16 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
return -EINVAL; return -EINVAL;
/* only important for AP, IBSS and mesh create IEs internally */ /* only important for AP, IBSS and mesh create IEs internally */
if (!info->attrs[NL80211_ATTR_CSA_IES]) if (need_new_beacon &&
return -EINVAL; (!info->attrs[NL80211_ATTR_CSA_IES] ||
!info->attrs[NL80211_ATTR_CSA_C_OFF_BEACON]))
/* useless if AP is not running */
if (!wdev->beacon_interval)
return -EINVAL; return -EINVAL;
params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); 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, &params.beacon_after); err = nl80211_parse_beacon(info->attrs, &params.beacon_after);
if (err) if (err)
return err; return err;
@ -5699,6 +5714,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
return -EINVAL; return -EINVAL;
} }
skip_beacons:
err = nl80211_parse_chandef(rdev, info, &params.chandef); err = nl80211_parse_chandef(rdev, info, &params.chandef);
if (err) if (err)
return err; return err;
@ -5706,13 +5722,18 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef)) if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
return -EINVAL; return -EINVAL;
err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef); /* 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,
&params.chandef);
if (err < 0) { if (err < 0) {
return err; return err;
} else if (err) { } else if (err) {
radar_detect_width = BIT(params.chandef.width); radar_detect_width = BIT(params.chandef.width);
params.radar_required = true; params.radar_required = true;
} }
}
err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
params.chandef.chan, params.chandef.chan,
@ -10740,7 +10761,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
wdev_lock(wdev); wdev_lock(wdev);
if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && 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; goto out;
wdev->channel = chandef->chan; wdev->channel = chandef->chan;

View File

@ -172,11 +172,21 @@ static const struct ieee80211_regdomain world_regdom = {
NL80211_RRF_NO_IBSS | NL80211_RRF_NO_IBSS |
NL80211_RRF_NO_OFDM), NL80211_RRF_NO_OFDM),
/* IEEE 802.11a, channel 36..48 */ /* 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_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS), 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 */ /* IEEE 802.11a, channel 149..165 */
REG_RULE(5745-10, 5825+10, 80, 6, 20, REG_RULE(5745-10, 5825+10, 80, 6, 20,

View File

@ -10,6 +10,7 @@
#include <net/cfg80211.h> #include <net/cfg80211.h>
#include <net/ip.h> #include <net/ip.h>
#include <net/dsfield.h> #include <net/dsfield.h>
#include <linux/if_vlan.h>
#include "core.h" #include "core.h"
#include "rdev-ops.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 cfg80211_classify8021d(struct sk_buff *skb)
{ {
unsigned int dscp; unsigned int dscp;
unsigned char vlan_priority;
/* skb->priority values from 256->263 are magic values to /* skb->priority values from 256->263 are magic values to
* directly indicate a specific 802.1d priority. This is used * 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) if (skb->priority >= 256 && skb->priority <= 263)
return skb->priority - 256; 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) { switch (skb->protocol) {
case htons(ETH_P_IP): case htons(ETH_P_IP):
dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc; dscp = ipv4_get_dsfield(ip_hdr(skb)) & 0xfc;