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
F: arch/hexagon/
QUALCOMM WCN36XX WIRELESS DRIVER
M: Eugene Krasnikov <k.eugene.e@gmail.com>
L: wcn36xx@lists.infradead.org
W: http://wireless.kernel.org/en/users/Drivers/wcn36xx
T: git git://github.com/KrasnikovEugene/wcn36xx.git
S: Supported
F: drivers/net/wireless/ath/wcn36xx/
QUICKCAM PARALLEL PORT WEBCAMS
M: Hans Verkuil <hverkuil@xs4all.nl>
L: linux-media@vger.kernel.org

View file

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

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/wil6210/Kconfig"
source "drivers/net/wireless/ath/ath10k/Kconfig"
source "drivers/net/wireless/ath/wcn36xx/Kconfig"
endif

View file

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

View file

@ -338,10 +338,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
aniState->cckNoiseImmunityLevel !=
ATH9K_ANI_CCK_DEF_LEVEL) {
ath_dbg(common, ANI,
"Restore defaults: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n",
"Restore defaults: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",
ah->opmode,
chan->channel,
chan->channelFlags,
is_scanning,
aniState->ofdmNoiseImmunityLevel,
aniState->cckNoiseImmunityLevel);
@ -354,10 +353,9 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
* restore historical levels for this channel
*/
ath_dbg(common, ANI,
"Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d ofdm:%d cck:%d\n",
"Restore history: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",
ah->opmode,
chan->channel,
chan->channelFlags,
is_scanning,
aniState->ofdmNoiseImmunityLevel,
aniState->cckNoiseImmunityLevel);

View file

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

View file

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

View file

@ -419,28 +419,10 @@ void ar9002_hw_load_ani_reg(struct ath_hw *ah, struct ath9k_channel *chan)
u32 modesIndex;
int i;
switch (chan->chanmode) {
case CHANNEL_A:
case CHANNEL_A_HT20:
modesIndex = 1;
break;
case CHANNEL_A_HT40PLUS:
case CHANNEL_A_HT40MINUS:
modesIndex = 2;
break;
case CHANNEL_G:
case CHANNEL_G_HT20:
case CHANNEL_B:
modesIndex = 4;
break;
case CHANNEL_G_HT40PLUS:
case CHANNEL_G_HT40MINUS:
modesIndex = 3;
break;
default:
return;
}
if (IS_CHAN_5GHZ(chan))
modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
else
modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
ENABLE_REGWRITE_BUFFER(ah);

View file

@ -551,8 +551,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah,
if (IS_CHAN_HT40(chan)) {
phymode |= AR_PHY_GC_DYN2040_EN;
/* Configure control (primary) channel at +-10MHz */
if ((chan->chanmode == CHANNEL_A_HT40PLUS) ||
(chan->chanmode == CHANNEL_G_HT40PLUS))
if (IS_CHAN_HT40PLUS(chan))
phymode |= AR_PHY_GC_DYN2040_PRI_CH;
}
@ -565,7 +564,7 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah,
REG_WRITE(ah, AR_PHY_GEN_CTRL, phymode);
/* Configure MAC for 20/40 operation */
ath9k_hw_set11nmac2040(ah);
ath9k_hw_set11nmac2040(ah, chan);
/* global transmit timeout (25 TUs default)*/
REG_WRITE(ah, AR_GTXTO, 25 << AR_GTXTO_TIMEOUT_LIMIT_S);
@ -682,42 +681,23 @@ static int ar9550_hw_get_modes_txgain_index(struct ath_hw *ah,
{
int ret;
switch (chan->chanmode) {
case CHANNEL_A:
case CHANNEL_A_HT20:
if (chan->channel <= 5350)
ret = 1;
else if ((chan->channel > 5350) && (chan->channel <= 5600))
ret = 3;
if (IS_CHAN_2GHZ(chan)) {
if (IS_CHAN_HT40(chan))
return 7;
else
ret = 5;
break;
case CHANNEL_A_HT40PLUS:
case CHANNEL_A_HT40MINUS:
if (chan->channel <= 5350)
ret = 2;
else if ((chan->channel > 5350) && (chan->channel <= 5600))
ret = 4;
else
ret = 6;
break;
case CHANNEL_G:
case CHANNEL_G_HT20:
case CHANNEL_B:
ret = 8;
break;
case CHANNEL_G_HT40PLUS:
case CHANNEL_G_HT40MINUS:
ret = 7;
break;
default:
ret = -EINVAL;
return 8;
}
if (chan->channel <= 5350)
ret = 1;
else if ((chan->channel > 5350) && (chan->channel <= 5600))
ret = 3;
else
ret = 5;
if (IS_CHAN_HT40(chan))
ret++;
return ret;
}
@ -727,28 +707,10 @@ static int ar9003_hw_process_ini(struct ath_hw *ah,
unsigned int regWrites = 0, i;
u32 modesIndex;
switch (chan->chanmode) {
case CHANNEL_A:
case CHANNEL_A_HT20:
modesIndex = 1;
break;
case CHANNEL_A_HT40PLUS:
case CHANNEL_A_HT40MINUS:
modesIndex = 2;
break;
case CHANNEL_G:
case CHANNEL_G_HT20:
case CHANNEL_B:
modesIndex = 4;
break;
case CHANNEL_G_HT40PLUS:
case CHANNEL_G_HT40MINUS:
modesIndex = 3;
break;
default:
return -EINVAL;
}
if (IS_CHAN_5GHZ(chan))
modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
else
modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
/*
* SOC, MAC, BB, RADIO initvals.
@ -846,8 +808,10 @@ static void ar9003_hw_set_rfmode(struct ath_hw *ah,
if (chan == NULL)
return;
rfMode |= (IS_CHAN_B(chan) || IS_CHAN_G(chan))
? AR_PHY_MODE_DYNAMIC : AR_PHY_MODE_OFDM;
if (IS_CHAN_2GHZ(chan))
rfMode |= AR_PHY_MODE_DYNAMIC;
else
rfMode |= AR_PHY_MODE_OFDM;
if (IS_CHAN_A_FAST_CLOCK(ah, chan))
rfMode |= (AR_PHY_MODE_DYNAMIC | AR_PHY_MODE_DYN_CCK_DISABLE);
@ -1273,12 +1237,11 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
aniState = &ah->ani;
iniDef = &aniState->iniDef;
ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz/0x%x\n",
ath_dbg(common, ANI, "ver %d.%d opmode %u chan %d Mhz\n",
ah->hw_version.macVersion,
ah->hw_version.macRev,
ah->opmode,
chan->channel,
chan->channelFlags);
chan->channel);
val = REG_READ(ah, AR_PHY_SFCORR);
iniDef->m1Thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);
@ -1536,28 +1499,10 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
unsigned int regWrites = 0;
u32 modesIndex;
switch (chan->chanmode) {
case CHANNEL_A:
case CHANNEL_A_HT20:
modesIndex = 1;
break;
case CHANNEL_A_HT40PLUS:
case CHANNEL_A_HT40MINUS:
modesIndex = 2;
break;
case CHANNEL_G:
case CHANNEL_G_HT20:
case CHANNEL_B:
modesIndex = 4;
break;
case CHANNEL_G_HT40PLUS:
case CHANNEL_G_HT40MINUS:
modesIndex = 3;
break;
default:
return -EINVAL;
}
if (IS_CHAN_5GHZ(chan))
modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
else
modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
if (modesIndex == ah->modes_index) {
*ini_reloaded = false;

View file

@ -64,7 +64,6 @@ struct ath_node;
struct ath_config {
u16 txpowlimit;
u8 cabqReadytime;
};
/*************************/
@ -207,6 +206,14 @@ struct ath_frame_info {
u8 baw_tracked : 1;
};
struct ath_rxbuf {
struct list_head list;
struct sk_buff *bf_mpdu;
void *bf_desc;
dma_addr_t bf_daddr;
dma_addr_t bf_buf_addr;
};
struct ath_buf_state {
u8 bf_type;
u8 bfs_paprd;
@ -307,7 +314,7 @@ struct ath_rx {
struct ath_descdma rxdma;
struct ath_rx_edma rx_edma[ATH9K_RX_QUEUE_MAX];
struct ath_buf *buf_hold;
struct ath_rxbuf *buf_hold;
struct sk_buff *frag;
u32 ampdu_ref;
@ -926,7 +933,6 @@ void ath9k_deinit_device(struct ath_softc *sc);
void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
void ath9k_reload_chainmask_settings(struct ath_softc *sc);
bool ath9k_uses_beacons(int type);
void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
enum spectral_mode spectral_mode);

View file

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

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

View file

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

View file

@ -24,30 +24,10 @@
static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
struct ath9k_channel *ichan)
{
enum htc_phymode mode;
if (IS_CHAN_5GHZ(ichan))
return HTC_MODE_11NA;
mode = -EINVAL;
switch (ichan->chanmode) {
case CHANNEL_G:
case CHANNEL_G_HT20:
case CHANNEL_G_HT40PLUS:
case CHANNEL_G_HT40MINUS:
mode = HTC_MODE_11NG;
break;
case CHANNEL_A:
case CHANNEL_A_HT20:
case CHANNEL_A_HT40PLUS:
case CHANNEL_A_HT40MINUS:
mode = HTC_MODE_11NA;
break;
default:
break;
}
WARN_ON(mode < 0);
return mode;
return HTC_MODE_11NG;
}
bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
@ -926,7 +906,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
WMI_CMD(WMI_FLUSH_RECV_CMDID);
/* setup initial channel */
init_channel = ath9k_cmn_get_curchannel(hw, ah);
init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
if (ret) {
@ -1208,9 +1188,7 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
ath_dbg(common, CONFIG, "Set channel: %d MHz\n",
curchan->center_freq);
ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
&hw->conf.chandef);
ath9k_cmn_get_channel(hw, priv->ah, &hw->conf.chandef);
if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
ath_err(common, "Unable to set channel\n");
ret = -EINVAL;

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

View file

@ -369,36 +369,6 @@ enum ath9k_int {
ATH9K_INT_NOCARD = 0xffffffff
};
#define CHANNEL_CCK 0x00020
#define CHANNEL_OFDM 0x00040
#define CHANNEL_2GHZ 0x00080
#define CHANNEL_5GHZ 0x00100
#define CHANNEL_PASSIVE 0x00200
#define CHANNEL_DYN 0x00400
#define CHANNEL_HALF 0x04000
#define CHANNEL_QUARTER 0x08000
#define CHANNEL_HT20 0x10000
#define CHANNEL_HT40PLUS 0x20000
#define CHANNEL_HT40MINUS 0x40000
#define CHANNEL_A (CHANNEL_5GHZ|CHANNEL_OFDM)
#define CHANNEL_B (CHANNEL_2GHZ|CHANNEL_CCK)
#define CHANNEL_G (CHANNEL_2GHZ|CHANNEL_OFDM)
#define CHANNEL_G_HT20 (CHANNEL_2GHZ|CHANNEL_HT20)
#define CHANNEL_A_HT20 (CHANNEL_5GHZ|CHANNEL_HT20)
#define CHANNEL_G_HT40PLUS (CHANNEL_2GHZ|CHANNEL_HT40PLUS)
#define CHANNEL_G_HT40MINUS (CHANNEL_2GHZ|CHANNEL_HT40MINUS)
#define CHANNEL_A_HT40PLUS (CHANNEL_5GHZ|CHANNEL_HT40PLUS)
#define CHANNEL_A_HT40MINUS (CHANNEL_5GHZ|CHANNEL_HT40MINUS)
#define CHANNEL_ALL \
(CHANNEL_OFDM| \
CHANNEL_CCK| \
CHANNEL_2GHZ | \
CHANNEL_5GHZ | \
CHANNEL_HT20 | \
CHANNEL_HT40PLUS | \
CHANNEL_HT40MINUS)
#define MAX_RTT_TABLE_ENTRY 6
#define MAX_IQCAL_MEASUREMENT 8
#define MAX_CL_TAB_ENTRY 16
@ -417,8 +387,7 @@ enum ath9k_cal_flags {
struct ath9k_hw_cal_data {
u16 channel;
u32 channelFlags;
u32 chanmode;
u16 channelFlags;
unsigned long cal_flags;
int32_t CalValid;
int8_t iCoff;
@ -436,33 +405,34 @@ struct ath9k_hw_cal_data {
struct ath9k_channel {
struct ieee80211_channel *chan;
u16 channel;
u32 channelFlags;
u32 chanmode;
u16 channelFlags;
s16 noisefloor;
};
#define IS_CHAN_G(_c) ((((_c)->channelFlags & (CHANNEL_G)) == CHANNEL_G) || \
(((_c)->channelFlags & CHANNEL_G_HT20) == CHANNEL_G_HT20) || \
(((_c)->channelFlags & CHANNEL_G_HT40PLUS) == CHANNEL_G_HT40PLUS) || \
(((_c)->channelFlags & CHANNEL_G_HT40MINUS) == CHANNEL_G_HT40MINUS))
#define IS_CHAN_OFDM(_c) (((_c)->channelFlags & CHANNEL_OFDM) != 0)
#define IS_CHAN_5GHZ(_c) (((_c)->channelFlags & CHANNEL_5GHZ) != 0)
#define IS_CHAN_2GHZ(_c) (((_c)->channelFlags & CHANNEL_2GHZ) != 0)
#define IS_CHAN_HALF_RATE(_c) (((_c)->channelFlags & CHANNEL_HALF) != 0)
#define IS_CHAN_QUARTER_RATE(_c) (((_c)->channelFlags & CHANNEL_QUARTER) != 0)
#define IS_CHAN_A_FAST_CLOCK(_ah, _c) \
((((_c)->channelFlags & CHANNEL_5GHZ) != 0) && \
((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK))
#define CHANNEL_5GHZ BIT(0)
#define CHANNEL_HALF BIT(1)
#define CHANNEL_QUARTER BIT(2)
#define CHANNEL_HT BIT(3)
#define CHANNEL_HT40PLUS BIT(4)
#define CHANNEL_HT40MINUS BIT(5)
/* These macros check chanmode and not channelFlags */
#define IS_CHAN_B(_c) ((_c)->chanmode == CHANNEL_B)
#define IS_CHAN_HT20(_c) (((_c)->chanmode == CHANNEL_A_HT20) || \
((_c)->chanmode == CHANNEL_G_HT20))
#define IS_CHAN_HT40(_c) (((_c)->chanmode == CHANNEL_A_HT40PLUS) || \
((_c)->chanmode == CHANNEL_A_HT40MINUS) || \
((_c)->chanmode == CHANNEL_G_HT40PLUS) || \
((_c)->chanmode == CHANNEL_G_HT40MINUS))
#define IS_CHAN_HT(_c) (IS_CHAN_HT20((_c)) || IS_CHAN_HT40((_c)))
#define IS_CHAN_5GHZ(_c) (!!((_c)->channelFlags & CHANNEL_5GHZ))
#define IS_CHAN_2GHZ(_c) (!IS_CHAN_5GHZ(_c))
#define IS_CHAN_HALF_RATE(_c) (!!((_c)->channelFlags & CHANNEL_HALF))
#define IS_CHAN_QUARTER_RATE(_c) (!!((_c)->channelFlags & CHANNEL_QUARTER))
#define IS_CHAN_A_FAST_CLOCK(_ah, _c) \
(IS_CHAN_5GHZ(_c) && ((_ah)->caps.hw_caps & ATH9K_HW_CAP_FASTCLOCK))
#define IS_CHAN_HT(_c) ((_c)->channelFlags & CHANNEL_HT)
#define IS_CHAN_HT20(_c) (IS_CHAN_HT(_c) && !IS_CHAN_HT40(_c))
#define IS_CHAN_HT40(_c) \
(!!((_c)->channelFlags & (CHANNEL_HT40PLUS | CHANNEL_HT40MINUS)))
#define IS_CHAN_HT40PLUS(_c) ((_c)->channelFlags & CHANNEL_HT40PLUS)
#define IS_CHAN_HT40MINUS(_c) ((_c)->channelFlags & CHANNEL_HT40MINUS)
enum ath9k_power_mode {
ATH9K_PM_AWAKE = 0,
@ -1033,7 +1003,7 @@ void ath9k_hw_reset_tsf(struct ath_hw *ah);
void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set);
void ath9k_hw_init_global_settings(struct ath_hw *ah);
u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah);
void ath9k_hw_set11nmac2040(struct ath_hw *ah);
void ath9k_hw_set11nmac2040(struct ath_hw *ah, struct ath9k_channel *chan);
void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period);
void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah,
const struct ath9k_beacon_state *bs);

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);
u8 *ds;
struct ath_buf *bf;
int i, bsize, desc_len;
ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n",
@ -399,33 +398,68 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len);
/* allocate buffers */
bsize = sizeof(struct ath_buf) * nbuf;
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
if (!bf)
return -ENOMEM;
if (is_tx) {
struct ath_buf *bf;
for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
bf->bf_desc = ds;
bf->bf_daddr = DS2PHYS(dd, ds);
bsize = sizeof(struct ath_buf) * nbuf;
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
if (!bf)
return -ENOMEM;
if (!(sc->sc_ah->caps.hw_caps &
ATH9K_HW_CAP_4KB_SPLITTRANS)) {
/*
* Skip descriptor addresses which can cause 4KB
* boundary crossing (addr + length) with a 32 dword
* descriptor fetch.
*/
while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
BUG_ON((caddr_t) bf->bf_desc >=
((caddr_t) dd->dd_desc +
dd->dd_desc_len));
for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
bf->bf_desc = ds;
bf->bf_daddr = DS2PHYS(dd, ds);
ds += (desc_len * ndesc);
bf->bf_desc = ds;
bf->bf_daddr = DS2PHYS(dd, ds);
if (!(sc->sc_ah->caps.hw_caps &
ATH9K_HW_CAP_4KB_SPLITTRANS)) {
/*
* Skip descriptor addresses which can cause 4KB
* boundary crossing (addr + length) with a 32 dword
* descriptor fetch.
*/
while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
BUG_ON((caddr_t) bf->bf_desc >=
((caddr_t) dd->dd_desc +
dd->dd_desc_len));
ds += (desc_len * ndesc);
bf->bf_desc = ds;
bf->bf_daddr = DS2PHYS(dd, ds);
}
}
list_add_tail(&bf->list, head);
}
} else {
struct ath_rxbuf *bf;
bsize = sizeof(struct ath_rxbuf) * nbuf;
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
if (!bf)
return -ENOMEM;
for (i = 0; i < nbuf; i++, bf++, ds += (desc_len * ndesc)) {
bf->bf_desc = ds;
bf->bf_daddr = DS2PHYS(dd, ds);
if (!(sc->sc_ah->caps.hw_caps &
ATH9K_HW_CAP_4KB_SPLITTRANS)) {
/*
* Skip descriptor addresses which can cause 4KB
* boundary crossing (addr + length) with a 32 dword
* descriptor fetch.
*/
while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) {
BUG_ON((caddr_t) bf->bf_desc >=
((caddr_t) dd->dd_desc +
dd->dd_desc_len));
ds += (desc_len * ndesc);
bf->bf_desc = ds;
bf->bf_daddr = DS2PHYS(dd, ds);
}
}
list_add_tail(&bf->list, head);
}
list_add_tail(&bf->list, head);
}
return 0;
}
@ -437,7 +471,6 @@ static int ath9k_init_queues(struct ath_softc *sc)
sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah);
sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0);
sc->config.cabqReadytime = ATH_CABQ_READY_TIME;
ath_cabq_update(sc);
sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0);
@ -768,7 +801,7 @@ static void ath9k_init_band_txpower(struct ath_softc *sc, int band)
chan = &sband->channels[i];
ah->curchan = &ah->channels[chan->hw_value];
cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_HT20);
ath9k_cmn_update_ichannel(ah->curchan, &chandef);
ath9k_cmn_get_channel(sc->hw, ah, &chandef);
ath9k_hw_set_txpowerlimit(ah, MAX_RATE_POWER, true);
}
}

View file

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

View file

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

View file

@ -302,17 +302,91 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
* by reseting the chip. To accomplish this we must first cleanup any pending
* DMA, then restart stuff.
*/
static int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw,
struct ath9k_channel *hchan)
static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_hw *hw = sc->hw;
struct ath9k_channel *hchan;
struct ieee80211_channel *chan = chandef->chan;
unsigned long flags;
bool offchannel;
int pos = chan->hw_value;
int old_pos = -1;
int r;
if (test_bit(SC_OP_INVALID, &sc->sc_flags))
return -EIO;
r = ath_reset_internal(sc, hchan);
offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
return r;
if (ah->curchan)
old_pos = ah->curchan - &ah->channels[0];
ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
chan->center_freq, chandef->width);
/* update survey stats for the old channel before switching */
spin_lock_irqsave(&common->cc_lock, flags);
ath_update_survey_stats(sc);
spin_unlock_irqrestore(&common->cc_lock, flags);
ath9k_cmn_get_channel(hw, ah, chandef);
/*
* If the operating channel changes, change the survey in-use flags
* along with it.
* Reset the survey data for the new channel, unless we're switching
* back to the operating channel from an off-channel operation.
*/
if (!offchannel && sc->cur_survey != &sc->survey[pos]) {
if (sc->cur_survey)
sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
sc->cur_survey = &sc->survey[pos];
memset(sc->cur_survey, 0, sizeof(struct survey_info));
sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
} else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
memset(&sc->survey[pos], 0, sizeof(struct survey_info));
}
hchan = &sc->sc_ah->channels[pos];
r = ath_reset_internal(sc, hchan);
if (r)
return r;
/*
* The most recent snapshot of channel->noisefloor for the old
* channel is only available after the hardware reset. Copy it to
* the survey stats now.
*/
if (old_pos >= 0)
ath_update_survey_nf(sc, old_pos);
/*
* Enable radar pulse detection if on a DFS channel. Spectral
* scanning and radar detection can not be used concurrently.
*/
if (hw->conf.radar_enabled) {
u32 rxfilter;
/* set HW specific DFS configuration */
ath9k_hw_set_radar_params(ah);
rxfilter = ath9k_hw_getrxfilter(ah);
rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
ATH9K_RX_FILTER_PHYERR;
ath9k_hw_setrxfilter(ah, rxfilter);
ath_dbg(common, DFS, "DFS enabled at freq %d\n",
chan->center_freq);
} else {
/* perform spectral scan if requested. */
if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
sc->spectral_mode == SPECTRAL_CHANSCAN)
ath9k_spectral_scan_trigger(hw);
}
return 0;
}
static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
@ -601,7 +675,7 @@ static int ath9k_start(struct ieee80211_hw *hw)
ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
init_channel = ath9k_cmn_get_curchannel(hw, ah);
init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
/* Reset SERDES registers */
ath9k_hw_configpcipowersave(ah, false);
@ -804,7 +878,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
}
if (!ah->curchan)
ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
ath9k_hw_phy_disable(ah);
@ -823,7 +897,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
ath_dbg(common, CONFIG, "Driver halt\n");
}
bool ath9k_uses_beacons(int type)
static bool ath9k_uses_beacons(int type)
{
switch (type) {
case NL80211_IFTYPE_AP:
@ -1208,81 +1282,12 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
}
if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
struct ieee80211_channel *curchan = hw->conf.chandef.chan;
int pos = curchan->hw_value;
int old_pos = -1;
unsigned long flags;
if (ah->curchan)
old_pos = ah->curchan - &ah->channels[0];
ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
curchan->center_freq, hw->conf.chandef.width);
/* update survey stats for the old channel before switching */
spin_lock_irqsave(&common->cc_lock, flags);
ath_update_survey_stats(sc);
spin_unlock_irqrestore(&common->cc_lock, flags);
ath9k_cmn_update_ichannel(&sc->sc_ah->channels[pos],
&conf->chandef);
/*
* If the operating channel changes, change the survey in-use flags
* along with it.
* Reset the survey data for the new channel, unless we're switching
* back to the operating channel from an off-channel operation.
*/
if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) &&
sc->cur_survey != &sc->survey[pos]) {
if (sc->cur_survey)
sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
sc->cur_survey = &sc->survey[pos];
memset(sc->cur_survey, 0, sizeof(struct survey_info));
sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
} else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
memset(&sc->survey[pos], 0, sizeof(struct survey_info));
}
if (ath_set_channel(sc, hw, &sc->sc_ah->channels[pos]) < 0) {
if (ath_set_channel(sc, &hw->conf.chandef) < 0) {
ath_err(common, "Unable to set channel\n");
mutex_unlock(&sc->mutex);
ath9k_ps_restore(sc);
return -EINVAL;
}
/*
* The most recent snapshot of channel->noisefloor for the old
* channel is only available after the hardware reset. Copy it to
* the survey stats now.
*/
if (old_pos >= 0)
ath_update_survey_nf(sc, old_pos);
/*
* Enable radar pulse detection if on a DFS channel. Spectral
* scanning and radar detection can not be used concurrently.
*/
if (hw->conf.radar_enabled) {
u32 rxfilter;
/* set HW specific DFS configuration */
ath9k_hw_set_radar_params(ah);
rxfilter = ath9k_hw_getrxfilter(ah);
rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
ATH9K_RX_FILTER_PHYERR;
ath9k_hw_setrxfilter(ah, rxfilter);
ath_dbg(common, DFS, "DFS enabled at freq %d\n",
curchan->center_freq);
} else {
/* perform spectral scan if requested. */
if (test_bit(SC_OP_SCANNING, &sc->sc_flags) &&
sc->spectral_mode == SPECTRAL_CHANSCAN)
ath9k_spectral_scan_trigger(hw);
}
}
if (changed & IEEE80211_CONF_CHANGE_POWER) {

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

View file

@ -19,7 +19,7 @@
#include "ath9k.h"
#include "ar9003_mac.h"
#define SKB_CB_ATHBUF(__skb) (*((struct ath_buf **)__skb->cb))
#define SKB_CB_ATHBUF(__skb) (*((struct ath_rxbuf **)__skb->cb))
static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
{
@ -35,7 +35,7 @@ static inline bool ath9k_check_auto_sleep(struct ath_softc *sc)
* buffer (or rx fifo). This can incorrectly acknowledge packets
* to a sender if last desc is self-linked.
*/
static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
static void ath_rx_buf_link(struct ath_softc *sc, struct ath_rxbuf *bf)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
@ -68,7 +68,7 @@ static void ath_rx_buf_link(struct ath_softc *sc, struct ath_buf *bf)
sc->rx.rxlink = &ds->ds_link;
}
static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_buf *bf)
static void ath_rx_buf_relink(struct ath_softc *sc, struct ath_rxbuf *bf)
{
if (sc->rx.buf_hold)
ath_rx_buf_link(sc, sc->rx.buf_hold);
@ -112,13 +112,13 @@ static bool ath_rx_edma_buf_link(struct ath_softc *sc,
struct ath_hw *ah = sc->sc_ah;
struct ath_rx_edma *rx_edma;
struct sk_buff *skb;
struct ath_buf *bf;
struct ath_rxbuf *bf;
rx_edma = &sc->rx.rx_edma[qtype];
if (skb_queue_len(&rx_edma->rx_fifo) >= rx_edma->rx_fifo_hwsize)
return false;
bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
list_del_init(&bf->list);
skb = bf->bf_mpdu;
@ -138,7 +138,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc,
enum ath9k_rx_qtype qtype)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_buf *bf, *tbf;
struct ath_rxbuf *bf, *tbf;
if (list_empty(&sc->rx.rxbuf)) {
ath_dbg(common, QUEUE, "No free rx buf available\n");
@ -154,7 +154,7 @@ static void ath_rx_addbuffer_edma(struct ath_softc *sc,
static void ath_rx_remove_buffer(struct ath_softc *sc,
enum ath9k_rx_qtype qtype)
{
struct ath_buf *bf;
struct ath_rxbuf *bf;
struct ath_rx_edma *rx_edma;
struct sk_buff *skb;
@ -171,7 +171,7 @@ static void ath_rx_edma_cleanup(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath_buf *bf;
struct ath_rxbuf *bf;
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP);
ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP);
@ -199,7 +199,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_hw *ah = sc->sc_ah;
struct sk_buff *skb;
struct ath_buf *bf;
struct ath_rxbuf *bf;
int error = 0, i;
u32 size;
@ -211,7 +211,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP],
ah->caps.rx_hp_qdepth);
size = sizeof(struct ath_buf) * nbufs;
size = sizeof(struct ath_rxbuf) * nbufs;
bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
if (!bf)
return -ENOMEM;
@ -271,7 +271,7 @@ int ath_rx_init(struct ath_softc *sc, int nbufs)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct sk_buff *skb;
struct ath_buf *bf;
struct ath_rxbuf *bf;
int error = 0;
spin_lock_init(&sc->sc_pcu_lock);
@ -332,7 +332,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct sk_buff *skb;
struct ath_buf *bf;
struct ath_rxbuf *bf;
if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
ath_rx_edma_cleanup(sc);
@ -427,7 +427,7 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
int ath_startrecv(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_buf *bf, *tbf;
struct ath_rxbuf *bf, *tbf;
if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) {
ath_edma_start_recv(sc);
@ -447,7 +447,7 @@ int ath_startrecv(struct ath_softc *sc)
if (list_empty(&sc->rx.rxbuf))
goto start_recv;
bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
ath9k_hw_putrxbuf(ah, bf->bf_daddr);
ath9k_hw_rxena(ah);
@ -603,13 +603,13 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb, bool mybeacon)
static bool ath_edma_get_buffers(struct ath_softc *sc,
enum ath9k_rx_qtype qtype,
struct ath_rx_status *rs,
struct ath_buf **dest)
struct ath_rxbuf **dest)
{
struct ath_rx_edma *rx_edma = &sc->rx.rx_edma[qtype];
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct sk_buff *skb;
struct ath_buf *bf;
struct ath_rxbuf *bf;
int ret;
skb = skb_peek(&rx_edma->rx_fifo);
@ -653,11 +653,11 @@ static bool ath_edma_get_buffers(struct ath_softc *sc,
return true;
}
static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
static struct ath_rxbuf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
struct ath_rx_status *rs,
enum ath9k_rx_qtype qtype)
{
struct ath_buf *bf = NULL;
struct ath_rxbuf *bf = NULL;
while (ath_edma_get_buffers(sc, qtype, rs, &bf)) {
if (!bf)
@ -668,13 +668,13 @@ static struct ath_buf *ath_edma_get_next_rx_buf(struct ath_softc *sc,
return NULL;
}
static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
static struct ath_rxbuf *ath_get_next_rx_buf(struct ath_softc *sc,
struct ath_rx_status *rs)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath_desc *ds;
struct ath_buf *bf;
struct ath_rxbuf *bf;
int ret;
if (list_empty(&sc->rx.rxbuf)) {
@ -682,7 +682,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
return NULL;
}
bf = list_first_entry(&sc->rx.rxbuf, struct ath_buf, list);
bf = list_first_entry(&sc->rx.rxbuf, struct ath_rxbuf, list);
if (bf == sc->rx.buf_hold)
return NULL;
@ -702,7 +702,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
ret = ath9k_hw_rxprocdesc(ah, ds, rs);
if (ret == -EINPROGRESS) {
struct ath_rx_status trs;
struct ath_buf *tbf;
struct ath_rxbuf *tbf;
struct ath_desc *tds;
memset(&trs, 0, sizeof(trs));
@ -711,7 +711,7 @@ static struct ath_buf *ath_get_next_rx_buf(struct ath_softc *sc,
return NULL;
}
tbf = list_entry(bf->list.next, struct ath_buf, list);
tbf = list_entry(bf->list.next, struct ath_rxbuf, list);
/*
* On some hardware the descriptor status words could
@ -1308,7 +1308,7 @@ static void ath9k_apply_ampdu_details(struct ath_softc *sc,
int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
{
struct ath_buf *bf;
struct ath_rxbuf *bf;
struct sk_buff *skb = NULL, *requeue_skb, *hdr_skb;
struct ieee80211_rx_status *rxs;
struct ath_hw *ah = sc->sc_ah;

View file

@ -1704,16 +1704,9 @@ int ath_cabq_update(struct ath_softc *sc)
int qnum = sc->beacon.cabq->axq_qnum;
ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
/*
* Ensure the readytime % is within the bounds.
*/
if (sc->config.cabqReadytime < ATH9K_READY_TIME_LO_BOUND)
sc->config.cabqReadytime = ATH9K_READY_TIME_LO_BOUND;
else if (sc->config.cabqReadytime > ATH9K_READY_TIME_HI_BOUND)
sc->config.cabqReadytime = ATH9K_READY_TIME_HI_BOUND;
qi.tqi_readyTime = (cur_conf->beacon_interval *
sc->config.cabqReadytime) / 100;
ATH_CABQ_READY_TIME) / 100;
ath_txq_update(sc, qnum, &qi);
return 0;
@ -2037,8 +2030,7 @@ u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate)
struct ath_hw *ah = sc->sc_ah;
struct ath9k_channel *curchan = ah->curchan;
if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) &&
(curchan->channelFlags & CHANNEL_5GHZ) &&
if ((ah->caps.hw_caps & ATH9K_HW_CAP_APM) && IS_CHAN_5GHZ(curchan) &&
(chainmask == 0x7) && (rate < 0x90))
return 0x3;
else if (AR_SREV_9462(ah) && ath9k_hw_btcoex_is_enabled(ah) &&

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;
if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID))
return true;
if (device == BCM4313_D11N2G_ID)
if (device == BCM4313_D11N2G_ID || device == BCM4313_CHIP_ID)
return true;
if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID))
return true;

View file

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

View file

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

View file

@ -882,7 +882,9 @@ mwifiex_add_card(void *card, struct semaphore *sem,
adapter->cmd_wait_q.status = 0;
adapter->scan_wait_q_woken = false;
adapter->workqueue = create_workqueue("MWIFIEX_WORK_QUEUE");
adapter->workqueue =
alloc_workqueue("MWIFIEX_WORK_QUEUE",
WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
if (!adapter->workqueue)
goto err_kmalloc;

View file

@ -327,7 +327,7 @@ mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv,
{
struct mwifiex_adapter *adapter = priv->adapter;
struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg;
u16 hs_activate = false;
bool hs_activate = false;
if (!hscfg_param)
/* New Activate command */

View file

@ -708,7 +708,7 @@ int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv,
{
u8 *curr = (u8 *) &resp->params.get_wmm_status;
uint16_t resp_len = le16_to_cpu(resp->size), tlv_len;
int valid = true;
bool valid = true;
struct mwifiex_ie_types_data *tlv_hdr;
struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus;

View file

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

View file

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

View file

@ -124,7 +124,7 @@
/*
* MAC_CSR0_3290: MAC_CSR0 for RT3290 to identity MAC version number.
*/
#define MAC_CSR0_3290 0x0000
#define MAC_CSR0_3290 0x0000
/*
* E2PROM_CSR: PCI EEPROM control register.
@ -213,17 +213,17 @@
/*
* COEX_CFG_0
*/
#define COEX_CFG0 0x0040
#define COEX_CFG0 0x0040
#define COEX_CFG_ANT FIELD32(0xff000000)
/*
* COEX_CFG_1
*/
#define COEX_CFG1 0x0044
#define COEX_CFG1 0x0044
/*
* COEX_CFG_2
*/
#define COEX_CFG2 0x0048
#define COEX_CFG2 0x0048
#define BT_COEX_CFG1 FIELD32(0xff000000)
#define BT_COEX_CFG0 FIELD32(0x00ff0000)
#define WL_COEX_CFG1 FIELD32(0x0000ff00)
@ -237,8 +237,8 @@
#define PLL_RESERVED_INPUT2 FIELD32(0x0000ff00)
#define PLL_CONTROL FIELD32(0x00070000)
#define PLL_LPF_R1 FIELD32(0x00080000)
#define PLL_LPF_C1_CTRL FIELD32(0x00300000)
#define PLL_LPF_C2_CTRL FIELD32(0x00c00000)
#define PLL_LPF_C1_CTRL FIELD32(0x00300000)
#define PLL_LPF_C2_CTRL FIELD32(0x00c00000)
#define PLL_CP_CURRENT_CTRL FIELD32(0x03000000)
#define PLL_PFD_DELAY_CTRL FIELD32(0x0c000000)
#define PLL_LOCK_CTRL FIELD32(0x70000000)
@ -2166,7 +2166,7 @@ struct mac_iveiv_entry {
*/
#define RFCSR6_R1 FIELD8(0x03)
#define RFCSR6_R2 FIELD8(0x40)
#define RFCSR6_TXDIV FIELD8(0x0c)
#define RFCSR6_TXDIV FIELD8(0x0c)
/* bits for RF3053 */
#define RFCSR6_VCO_IC FIELD8(0xc0)
@ -2204,13 +2204,13 @@ struct mac_iveiv_entry {
* RFCSR 12:
*/
#define RFCSR12_TX_POWER FIELD8(0x1f)
#define RFCSR12_DR0 FIELD8(0xe0)
#define RFCSR12_DR0 FIELD8(0xe0)
/*
* RFCSR 13:
*/
#define RFCSR13_TX_POWER FIELD8(0x1f)
#define RFCSR13_DR0 FIELD8(0xe0)
#define RFCSR13_DR0 FIELD8(0xe0)
/*
* RFCSR 15:
@ -2228,7 +2228,7 @@ struct mac_iveiv_entry {
#define RFCSR17_TXMIXER_GAIN FIELD8(0x07)
#define RFCSR17_TX_LO1_EN FIELD8(0x08)
#define RFCSR17_R FIELD8(0x20)
#define RFCSR17_CODE FIELD8(0x7f)
#define RFCSR17_CODE FIELD8(0x7f)
/* RFCSR 18 */
#define RFCSR18_XO_TUNE_BYPASS FIELD8(0x40)
@ -2451,7 +2451,7 @@ enum rt2800_eeprom_word {
*/
#define EEPROM_NIC_CONF0_RXPATH FIELD16(0x000f)
#define EEPROM_NIC_CONF0_TXPATH FIELD16(0x00f0)
#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00)
#define EEPROM_NIC_CONF0_RF_TYPE FIELD16(0x0f00)
/*
* EEPROM NIC Configuration 1
@ -2473,18 +2473,18 @@ enum rt2800_eeprom_word {
* DAC_TEST: 0: disable, 1: enable
*/
#define EEPROM_NIC_CONF1_HW_RADIO FIELD16(0x0001)
#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002)
#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004)
#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008)
#define EEPROM_NIC_CONF1_EXTERNAL_TX_ALC FIELD16(0x0002)
#define EEPROM_NIC_CONF1_EXTERNAL_LNA_2G FIELD16(0x0004)
#define EEPROM_NIC_CONF1_EXTERNAL_LNA_5G FIELD16(0x0008)
#define EEPROM_NIC_CONF1_CARDBUS_ACCEL FIELD16(0x0010)
#define EEPROM_NIC_CONF1_BW40M_SB_2G FIELD16(0x0020)
#define EEPROM_NIC_CONF1_BW40M_SB_5G FIELD16(0x0040)
#define EEPROM_NIC_CONF1_WPS_PBC FIELD16(0x0080)
#define EEPROM_NIC_CONF1_BW40M_2G FIELD16(0x0100)
#define EEPROM_NIC_CONF1_BW40M_5G FIELD16(0x0200)
#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400)
#define EEPROM_NIC_CONF1_BROADBAND_EXT_LNA FIELD16(0x400)
#define EEPROM_NIC_CONF1_ANT_DIVERSITY FIELD16(0x1800)
#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000)
#define EEPROM_NIC_CONF1_INTERNAL_TX_ALC FIELD16(0x2000)
#define EEPROM_NIC_CONF1_BT_COEXIST FIELD16(0x4000)
#define EEPROM_NIC_CONF1_DAC_TEST FIELD16(0x8000)
@ -2523,9 +2523,9 @@ enum rt2800_eeprom_word {
* TX_STREAM: 0: Reserved, 1: 1 Stream, 2: 2 Stream
* CRYSTAL: 00: Reserved, 01: One crystal, 10: Two crystal, 11: Reserved
*/
#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f)
#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0)
#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600)
#define EEPROM_NIC_CONF2_RX_STREAM FIELD16(0x000f)
#define EEPROM_NIC_CONF2_TX_STREAM FIELD16(0x00f0)
#define EEPROM_NIC_CONF2_CRYSTAL FIELD16(0x0600)
/*
* EEPROM LNA
@ -2792,7 +2792,7 @@ enum rt2800_eeprom_word {
#define MCU_CURRENT 0x36
#define MCU_LED 0x50
#define MCU_LED_STRENGTH 0x51
#define MCU_LED_AG_CONF 0x52
#define MCU_LED_AG_CONF 0x52
#define MCU_LED_ACT_CONF 0x53
#define MCU_LED_LED_POLARITY 0x54
#define MCU_RADAR 0x60
@ -2801,7 +2801,7 @@ enum rt2800_eeprom_word {
#define MCU_FREQ_OFFSET 0x74
#define MCU_BBP_SIGNAL 0x80
#define MCU_POWER_SAVE 0x83
#define MCU_BAND_SELECT 0x91
#define MCU_BAND_SELECT 0x91
/*
* MCU mailbox tokens

View file

@ -278,12 +278,9 @@ static const unsigned int rt2800_eeprom_map_ext[EEPROM_WORD_COUNT] = {
[EEPROM_LNA] = 0x0026,
[EEPROM_EXT_LNA2] = 0x0027,
[EEPROM_RSSI_BG] = 0x0028,
[EEPROM_TXPOWER_DELTA] = 0x0028, /* Overlaps with RSSI_BG */
[EEPROM_RSSI_BG2] = 0x0029,
[EEPROM_TXMIXER_GAIN_BG] = 0x0029, /* Overlaps with RSSI_BG2 */
[EEPROM_RSSI_A] = 0x002a,
[EEPROM_RSSI_A2] = 0x002b,
[EEPROM_TXMIXER_GAIN_A] = 0x002b, /* Overlaps with RSSI_A2 */
[EEPROM_TXPOWER_BG1] = 0x0030,
[EEPROM_TXPOWER_BG2] = 0x0037,
[EEPROM_EXT_TXPOWER_BG3] = 0x003e,
@ -1783,7 +1780,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
rt2800_bbp_read(rt2x00dev, 3, &r3);
if (rt2x00_rt(rt2x00dev, RT3572) &&
test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
rt2x00_has_cap_bt_coexist(rt2x00dev))
rt2800_config_3572bt_ant(rt2x00dev);
/*
@ -1795,7 +1792,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
break;
case 2:
if (rt2x00_rt(rt2x00dev, RT3572) &&
test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
rt2x00_has_cap_bt_coexist(rt2x00dev))
rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 1);
else
rt2x00_set_field8(&r1, BBP1_TX_ANTENNA, 2);
@ -1825,7 +1822,7 @@ void rt2800_config_ant(struct rt2x00_dev *rt2x00dev, struct antenna_setup *ant)
break;
case 2:
if (rt2x00_rt(rt2x00dev, RT3572) &&
test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
rt2x00_has_cap_bt_coexist(rt2x00dev)) {
rt2x00_set_field8(&r3, BBP3_RX_ADC, 1);
rt2x00_set_field8(&r3, BBP3_RX_ANTENNA,
rt2x00dev->curr_band == IEEE80211_BAND_5GHZ);
@ -2029,13 +2026,6 @@ static void rt2800_config_channel_rf3xxx(struct rt2x00_dev *rt2x00dev,
rt2x00dev->default_ant.tx_chain_num <= 2);
rt2800_rfcsr_write(rt2x00dev, 1, rfcsr);
rt2800_rfcsr_read(rt2x00dev, 30, &rfcsr);
rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 1);
rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
msleep(1);
rt2x00_set_field8(&rfcsr, RFCSR30_RF_CALIBRATION, 0);
rt2800_rfcsr_write(rt2x00dev, 30, rfcsr);
rt2800_rfcsr_read(rt2x00dev, 23, &rfcsr);
rt2x00_set_field8(&rfcsr, RFCSR23_FREQ_OFFSET, rt2x00dev->freq_offset);
rt2800_rfcsr_write(rt2x00dev, 23, rfcsr);
@ -2141,7 +2131,7 @@ static void rt2800_config_channel_rf3052(struct rt2x00_dev *rt2x00dev,
rt2x00_set_field8(&rfcsr, RFCSR1_TX1_PD, 0);
rt2x00_set_field8(&rfcsr, RFCSR1_RX2_PD, 0);
rt2x00_set_field8(&rfcsr, RFCSR1_TX2_PD, 0);
if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
if (rf->channel <= 14) {
rt2x00_set_field8(&rfcsr, RFCSR1_RX0_PD, 1);
rt2x00_set_field8(&rfcsr, RFCSR1_TX0_PD, 1);
@ -2674,7 +2664,7 @@ static void rt2800_config_channel_rf53xx(struct rt2x00_dev *rt2x00dev,
if (rf->channel <= 14) {
int idx = rf->channel-1;
if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F)) {
/* r55/r59 value array of channel 1~14 */
static const char r55_bt_rev[] = {0x83, 0x83,
@ -3220,8 +3210,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
if (rf->channel <= 14) {
if (!rt2x00_rt(rt2x00dev, RT5390) &&
!rt2x00_rt(rt2x00dev, RT5392)) {
if (test_bit(CAPABILITY_EXTERNAL_LNA_BG,
&rt2x00dev->cap_flags)) {
if (rt2x00_has_cap_external_lna_bg(rt2x00dev)) {
rt2800_bbp_write(rt2x00dev, 82, 0x62);
rt2800_bbp_write(rt2x00dev, 75, 0x46);
} else {
@ -3246,7 +3235,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
if (rt2x00_rt(rt2x00dev, RT3593))
rt2800_bbp_write(rt2x00dev, 83, 0x9a);
if (test_bit(CAPABILITY_EXTERNAL_LNA_A, &rt2x00dev->cap_flags))
if (rt2x00_has_cap_external_lna_a(rt2x00dev))
rt2800_bbp_write(rt2x00dev, 75, 0x46);
else
rt2800_bbp_write(rt2x00dev, 75, 0x50);
@ -3282,7 +3271,7 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
/* Turn on primary PAs */
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_A0_EN,
rf->channel > 14);
if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags))
if (rt2x00_has_cap_bt_coexist(rt2x00dev))
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN, 1);
else
rt2x00_set_field32(&tx_pin, TX_PIN_CFG_PA_PE_G0_EN,
@ -3313,9 +3302,18 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
rt2800_register_write(rt2x00dev, TX_PIN_CFG, tx_pin);
if (rt2x00_rt(rt2x00dev, RT3572))
if (rt2x00_rt(rt2x00dev, RT3572)) {
rt2800_rfcsr_write(rt2x00dev, 8, 0x80);
/* AGC init */
if (rf->channel <= 14)
reg = 0x1c + (2 * rt2x00dev->lna_gain);
else
reg = 0x22 + ((rt2x00dev->lna_gain * 5) / 3);
rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, reg);
}
if (rt2x00_rt(rt2x00dev, RT3593)) {
rt2800_register_read(rt2x00dev, GPIO_CTRL, &reg);
@ -3575,7 +3573,7 @@ static int rt2800_get_txpower_reg_delta(struct rt2x00_dev *rt2x00dev,
{
int delta;
if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags))
if (rt2x00_has_cap_power_limit(rt2x00dev))
return 0;
/*
@ -3604,7 +3602,7 @@ static u8 rt2800_compensate_txpower(struct rt2x00_dev *rt2x00dev, int is_rate_b,
if (rt2x00_rt(rt2x00dev, RT3593))
return min_t(u8, txpower, 0xc);
if (test_bit(CAPABILITY_POWER_LIMIT, &rt2x00dev->cap_flags)) {
if (rt2x00_has_cap_power_limit(rt2x00dev)) {
/*
* Check if eirp txpower exceed txpower_limit.
* We use OFDM 6M as criterion and its eirp txpower
@ -4416,6 +4414,7 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
rt2x00_rt(rt2x00dev, RT3290) ||
rt2x00_rt(rt2x00dev, RT3390) ||
rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT3593) ||
rt2x00_rt(rt2x00dev, RT5390) ||
rt2x00_rt(rt2x00dev, RT5392) ||
rt2x00_rt(rt2x00dev, RT5592))
@ -4423,8 +4422,8 @@ static u8 rt2800_get_default_vgc(struct rt2x00_dev *rt2x00dev)
else
vgc = 0x2e + rt2x00dev->lna_gain;
} else { /* 5GHZ band */
if (rt2x00_rt(rt2x00dev, RT3572))
vgc = 0x22 + (rt2x00dev->lna_gain * 5) / 3;
if (rt2x00_rt(rt2x00dev, RT3593))
vgc = 0x20 + (rt2x00dev->lna_gain * 5) / 3;
else if (rt2x00_rt(rt2x00dev, RT5592))
vgc = 0x24 + (2 * rt2x00dev->lna_gain);
else {
@ -4442,11 +4441,17 @@ static inline void rt2800_set_vgc(struct rt2x00_dev *rt2x00dev,
struct link_qual *qual, u8 vgc_level)
{
if (qual->vgc_level != vgc_level) {
if (rt2x00_rt(rt2x00dev, RT5592)) {
if (rt2x00_rt(rt2x00dev, RT3572) ||
rt2x00_rt(rt2x00dev, RT3593)) {
rt2800_bbp_write_with_rx_chain(rt2x00dev, 66,
vgc_level);
} else if (rt2x00_rt(rt2x00dev, RT5592)) {
rt2800_bbp_write(rt2x00dev, 83, qual->rssi > -65 ? 0x4a : 0x7a);
rt2800_bbp_write_with_rx_chain(rt2x00dev, 66, vgc_level);
} else
} else {
rt2800_bbp_write(rt2x00dev, 66, vgc_level);
}
qual->vgc_level = vgc_level;
qual->vgc_level_reg = vgc_level;
}
@ -4465,17 +4470,35 @@ void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual,
if (rt2x00_rt_rev(rt2x00dev, RT2860, REV_RT2860C))
return;
/*
* When RSSI is better then -80 increase VGC level with 0x10, except
* for rt5592 chip.
/* When RSSI is better than a certain threshold, increase VGC
* with a chip specific value in order to improve the balance
* between sensibility and noise isolation.
*/
vgc = rt2800_get_default_vgc(rt2x00dev);
if (rt2x00_rt(rt2x00dev, RT5592) && qual->rssi > -65)
vgc += 0x20;
else if (qual->rssi > -80)
vgc += 0x10;
switch (rt2x00dev->chip.rt) {
case RT3572:
case RT3593:
if (qual->rssi > -65) {
if (rt2x00dev->curr_band == IEEE80211_BAND_2GHZ)
vgc += 0x20;
else
vgc += 0x10;
}
break;
case RT5592:
if (qual->rssi > -65)
vgc += 0x20;
break;
default:
if (qual->rssi > -80)
vgc += 0x10;
break;
}
rt2800_set_vgc(rt2x00dev, qual, vgc);
}
@ -5500,7 +5523,7 @@ static void rt2800_init_bbp_53xx(struct rt2x00_dev *rt2x00dev)
ant = (div_mode == 3) ? 1 : 0;
/* check if this is a Bluetooth combo card */
if (test_bit(CAPABILITY_BT_COEXIST, &rt2x00dev->cap_flags)) {
if (rt2x00_has_cap_bt_coexist(rt2x00dev)) {
u32 reg;
rt2800_register_read(rt2x00dev, GPIO_CTRL, &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, RT3090, REV_RT3090E) ||
rt2x00_rt_rev_lt(rt2x00dev, RT3390, REV_RT3390E)) {
if (!test_bit(CAPABILITY_EXTERNAL_LNA_BG, &rt2x00dev->cap_flags))
if (!rt2x00_has_cap_external_lna_bg(rt2x00dev))
rt2x00_set_field8(&rfcsr, RFCSR17_R, 1);
}
@ -6452,7 +6475,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
rt2800_rfcsr_write(rt2x00dev, 28, 0x00);
rt2800_rfcsr_write(rt2x00dev, 29, 0x10);
rt2800_rfcsr_write(rt2x00dev, 30, 0x00);
rt2800_rfcsr_write(rt2x00dev, 30, 0x10);
rt2800_rfcsr_write(rt2x00dev, 31, 0x80);
rt2800_rfcsr_write(rt2x00dev, 32, 0x80);
rt2800_rfcsr_write(rt2x00dev, 33, 0x00);
@ -6490,7 +6513,7 @@ static void rt2800_init_rfcsr_5390(struct rt2x00_dev *rt2x00dev)
rt2800_rfcsr_write(rt2x00dev, 56, 0x22);
rt2800_rfcsr_write(rt2x00dev, 57, 0x80);
rt2800_rfcsr_write(rt2x00dev, 58, 0x7f);
rt2800_rfcsr_write(rt2x00dev, 59, 0x63);
rt2800_rfcsr_write(rt2x00dev, 59, 0x8f);
rt2800_rfcsr_write(rt2x00dev, 60, 0x45);
if (rt2x00_rt_rev_gte(rt2x00dev, RT5390, REV_RT5390F))
@ -6510,7 +6533,6 @@ static void rt2800_init_rfcsr_5392(struct rt2x00_dev *rt2x00dev)
rt2800_rf_init_calibration(rt2x00dev, 2);
rt2800_rfcsr_write(rt2x00dev, 1, 0x17);
rt2800_rfcsr_write(rt2x00dev, 2, 0x80);
rt2800_rfcsr_write(rt2x00dev, 3, 0x88);
rt2800_rfcsr_write(rt2x00dev, 5, 0x10);
rt2800_rfcsr_write(rt2x00dev, 6, 0xe0);
@ -7224,7 +7246,7 @@ static const struct rf_channel rf_vals[] = {
/*
* RF value list for rt3xxx
* Supports: 2.4 GHz (all) & 5.2 GHz (RF3052)
* Supports: 2.4 GHz (all) & 5.2 GHz (RF3052 & RF3053)
*/
static const struct rf_channel rf_vals_3x[] = {
{1, 241, 2, 2 },
@ -7420,72 +7442,6 @@ static const struct rf_channel rf_vals_5592_xtal40[] = {
{196, 83, 0, 12, 1},
};
static const struct rf_channel rf_vals_3053[] = {
/* Channel, N, R, K */
{1, 241, 2, 2},
{2, 241, 2, 7},
{3, 242, 2, 2},
{4, 242, 2, 7},
{5, 243, 2, 2},
{6, 243, 2, 7},
{7, 244, 2, 2},
{8, 244, 2, 7},
{9, 245, 2, 2},
{10, 245, 2, 7},
{11, 246, 2, 2},
{12, 246, 2, 7},
{13, 247, 2, 2},
{14, 248, 2, 4},
{36, 0x56, 0, 4},
{38, 0x56, 0, 6},
{40, 0x56, 0, 8},
{44, 0x57, 0, 0},
{46, 0x57, 0, 2},
{48, 0x57, 0, 4},
{52, 0x57, 0, 8},
{54, 0x57, 0, 10},
{56, 0x58, 0, 0},
{60, 0x58, 0, 4},
{62, 0x58, 0, 6},
{64, 0x58, 0, 8},
{100, 0x5B, 0, 8},
{102, 0x5B, 0, 10},
{104, 0x5C, 0, 0},
{108, 0x5C, 0, 4},
{110, 0x5C, 0, 6},
{112, 0x5C, 0, 8},
/* NOTE: Channel 114 has been removed intentionally.
* The EEPROM contains no TX power values for that,
* and it is disabled in the vendor driver as well.
*/
{116, 0x5D, 0, 0},
{118, 0x5D, 0, 2},
{120, 0x5D, 0, 4},
{124, 0x5D, 0, 8},
{126, 0x5D, 0, 10},
{128, 0x5E, 0, 0},
{132, 0x5E, 0, 4},
{134, 0x5E, 0, 6},
{136, 0x5E, 0, 8},
{140, 0x5F, 0, 0},
{149, 0x5F, 0, 9},
{151, 0x5F, 0, 11},
{153, 0x60, 0, 1},
{157, 0x60, 0, 5},
{159, 0x60, 0, 7},
{161, 0x60, 0, 9},
{165, 0x61, 0, 1},
{167, 0x61, 0, 3},
{169, 0x61, 0, 5},
{171, 0x61, 0, 7},
{173, 0x61, 0, 9},
};
static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
{
struct hw_mode_spec *spec = &rt2x00dev->spec;
@ -7575,14 +7531,11 @@ static int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev)
rt2x00_rf(rt2x00dev, RF5392)) {
spec->num_channels = 14;
spec->channels = rf_vals_3x;
} else if (rt2x00_rf(rt2x00dev, RF3052)) {
} else if (rt2x00_rf(rt2x00dev, RF3052) ||
rt2x00_rf(rt2x00dev, RF3053)) {
spec->supported_bands |= SUPPORT_BAND_5GHZ;
spec->num_channels = ARRAY_SIZE(rf_vals_3x);
spec->channels = rf_vals_3x;
} else if (rt2x00_rf(rt2x00dev, RF3053)) {
spec->supported_bands |= SUPPORT_BAND_5GHZ;
spec->num_channels = ARRAY_SIZE(rf_vals_3053);
spec->channels = rf_vals_3053;
} else if (rt2x00_rf(rt2x00dev, RF5592)) {
spec->supported_bands |= SUPPORT_BAND_5GHZ;

View file

@ -39,6 +39,7 @@
#include <linux/input-polldev.h>
#include <linux/kfifo.h>
#include <linux/hrtimer.h>
#include <linux/average.h>
#include <net/mac80211.h>
@ -138,17 +139,6 @@
#define SHORT_EIFS ( SIFS + SHORT_DIFS + \
GET_DURATION(IEEE80211_HEADER + ACK_SIZE, 10) )
/*
* Structure for average calculation
* The avg field contains the actual average value,
* but avg_weight is internally used during calculations
* to prevent rounding errors.
*/
struct avg_val {
int avg;
int avg_weight;
};
enum rt2x00_chip_intf {
RT2X00_CHIP_INTF_PCI,
RT2X00_CHIP_INTF_PCIE,
@ -297,7 +287,7 @@ struct link_ant {
* Similar to the avg_rssi in the link_qual structure
* this value is updated by using the walking average.
*/
struct avg_val rssi_ant;
struct ewma rssi_ant;
};
/*
@ -326,7 +316,7 @@ struct link {
/*
* Currently active average RSSI value
*/
struct avg_val avg_rssi;
struct ewma avg_rssi;
/*
* Work structure for scheduling periodic link tuning.
@ -1179,6 +1169,93 @@ static inline bool rt2x00_is_soc(struct rt2x00_dev *rt2x00dev)
return rt2x00_intf(rt2x00dev, RT2X00_CHIP_INTF_SOC);
}
/* Helpers for capability flags */
static inline bool
rt2x00_has_cap_flag(struct rt2x00_dev *rt2x00dev,
enum rt2x00_capability_flags cap_flag)
{
return test_bit(cap_flag, &rt2x00dev->cap_flags);
}
static inline bool
rt2x00_has_cap_hw_crypto(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_HW_CRYPTO);
}
static inline bool
rt2x00_has_cap_power_limit(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_POWER_LIMIT);
}
static inline bool
rt2x00_has_cap_control_filters(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTERS);
}
static inline bool
rt2x00_has_cap_control_filter_pspoll(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_CONTROL_FILTER_PSPOLL);
}
static inline bool
rt2x00_has_cap_pre_tbtt_interrupt(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_PRE_TBTT_INTERRUPT);
}
static inline bool
rt2x00_has_cap_link_tuning(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_LINK_TUNING);
}
static inline bool
rt2x00_has_cap_frame_type(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_FRAME_TYPE);
}
static inline bool
rt2x00_has_cap_rf_sequence(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_RF_SEQUENCE);
}
static inline bool
rt2x00_has_cap_external_lna_a(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_A);
}
static inline bool
rt2x00_has_cap_external_lna_bg(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_EXTERNAL_LNA_BG);
}
static inline bool
rt2x00_has_cap_double_antenna(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_DOUBLE_ANTENNA);
}
static inline bool
rt2x00_has_cap_bt_coexist(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_BT_COEXIST);
}
static inline bool
rt2x00_has_cap_vco_recalibration(struct rt2x00_dev *rt2x00dev)
{
return rt2x00_has_cap_flag(rt2x00dev, CAPABILITY_VCO_RECALIBRATION);
}
/**
* rt2x00queue_map_txskb - Map a skb into DMA for TX purposes.
* @entry: Pointer to &struct queue_entry

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

View file

@ -750,7 +750,7 @@ void rt2x00debug_register(struct rt2x00_dev *rt2x00dev)
intf, &rt2x00debug_fop_queue_stats);
#ifdef CONFIG_RT2X00_LIB_CRYPTO
if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
if (rt2x00_has_cap_hw_crypto(rt2x00dev))
intf->crypto_stats_entry =
debugfs_create_file("crypto", S_IRUGO, intf->queue_folder,
intf, &rt2x00debug_fop_crypto_stats);

View file

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

View file

@ -35,50 +35,28 @@
*/
#define DEFAULT_RSSI -128
/*
* Helper struct and macro to work with moving/walking averages.
* When adding a value to the average value the following calculation
* is needed:
*
* avg_rssi = ((avg_rssi * 7) + rssi) / 8;
*
* The advantage of this approach is that we only need 1 variable
* to store the average in (No need for a count and a total).
* But more importantly, normal average values will over time
* move less and less towards newly added values this results
* that with link tuning, the device can have a very good RSSI
* for a few minutes but when the device is moved away from the AP
* the average will not decrease fast enough to compensate.
* The walking average compensates this and will move towards
* the new values correctly allowing a effective link tuning,
* the speed of the average moving towards other values depends
* on the value for the number of samples. The higher the number
* of samples, the slower the average will move.
* We use two variables to keep track of the average value to
* compensate for the rounding errors. This can be a significant
* error (>5dBm) if the factor is too low.
*/
#define AVG_SAMPLES 8
#define AVG_FACTOR 1000
#define MOVING_AVERAGE(__avg, __val) \
({ \
struct avg_val __new; \
__new.avg_weight = \
(__avg).avg_weight ? \
((((__avg).avg_weight * ((AVG_SAMPLES) - 1)) + \
((__val) * (AVG_FACTOR))) / \
(AVG_SAMPLES)) : \
((__val) * (AVG_FACTOR)); \
__new.avg = __new.avg_weight / (AVG_FACTOR); \
__new; \
})
/* Constants for EWMA calculations. */
#define RT2X00_EWMA_FACTOR 1024
#define RT2X00_EWMA_WEIGHT 8
static inline int rt2x00link_get_avg_rssi(struct ewma *ewma)
{
unsigned long avg;
avg = ewma_read(ewma);
if (avg)
return -avg;
return DEFAULT_RSSI;
}
static int rt2x00link_antenna_get_link_rssi(struct rt2x00_dev *rt2x00dev)
{
struct link_ant *ant = &rt2x00dev->link.ant;
if (ant->rssi_ant.avg && rt2x00dev->link.qual.rx_success)
return ant->rssi_ant.avg;
if (rt2x00dev->link.qual.rx_success)
return rt2x00link_get_avg_rssi(&ant->rssi_ant);
return DEFAULT_RSSI;
}
@ -100,8 +78,8 @@ static void rt2x00link_antenna_update_rssi_history(struct rt2x00_dev *rt2x00dev,
static void rt2x00link_antenna_reset(struct rt2x00_dev *rt2x00dev)
{
rt2x00dev->link.ant.rssi_ant.avg = 0;
rt2x00dev->link.ant.rssi_ant.avg_weight = 0;
ewma_init(&rt2x00dev->link.ant.rssi_ant, RT2X00_EWMA_FACTOR,
RT2X00_EWMA_WEIGHT);
}
static void rt2x00lib_antenna_diversity_sample(struct rt2x00_dev *rt2x00dev)
@ -249,12 +227,12 @@ void rt2x00link_update_stats(struct rt2x00_dev *rt2x00dev,
/*
* Update global RSSI
*/
link->avg_rssi = MOVING_AVERAGE(link->avg_rssi, rxdesc->rssi);
ewma_add(&link->avg_rssi, -rxdesc->rssi);
/*
* Update antenna RSSI
*/
ant->rssi_ant = MOVING_AVERAGE(ant->rssi_ant, rxdesc->rssi);
ewma_add(&ant->rssi_ant, -rxdesc->rssi);
}
void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev)
@ -309,6 +287,8 @@ void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna)
*/
rt2x00dev->link.count = 0;
memset(qual, 0, sizeof(*qual));
ewma_init(&rt2x00dev->link.avg_rssi, RT2X00_EWMA_FACTOR,
RT2X00_EWMA_WEIGHT);
/*
* Restore the VGC level as stored in the registers,
@ -363,17 +343,17 @@ static void rt2x00link_tuner(struct work_struct *work)
* collect the RSSI data we could use this. Otherwise we
* must fallback to the default RSSI value.
*/
if (!link->avg_rssi.avg || !qual->rx_success)
if (!qual->rx_success)
qual->rssi = DEFAULT_RSSI;
else
qual->rssi = link->avg_rssi.avg;
qual->rssi = rt2x00link_get_avg_rssi(&link->avg_rssi);
/*
* Check if link tuning is supported by the hardware, some hardware
* do not support link tuning at all, while other devices can disable
* the feature from the EEPROM.
*/
if (test_bit(CAPABILITY_LINK_TUNING, &rt2x00dev->cap_flags))
if (rt2x00_has_cap_link_tuning(rt2x00dev))
rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count);
/*
@ -513,7 +493,7 @@ static void rt2x00link_vcocal(struct work_struct *work)
void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
{
INIT_DELAYED_WORK(&rt2x00dev->link.agc_work, rt2x00link_agc);
if (test_bit(CAPABILITY_VCO_RECALIBRATION, &rt2x00dev->cap_flags))
if (rt2x00_has_cap_vco_recalibration(rt2x00dev))
INIT_DELAYED_WORK(&rt2x00dev->link.vco_work, rt2x00link_vcocal);
INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);

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,
* FIF_CONTROL flag implies FIF_PSPOLL.
*/
if (!test_bit(CAPABILITY_CONTROL_FILTERS, &rt2x00dev->cap_flags)) {
if (!rt2x00_has_cap_control_filters(rt2x00dev)) {
if (*total_flags & FIF_CONTROL || *total_flags & FIF_PSPOLL)
*total_flags |= FIF_CONTROL | FIF_PSPOLL;
}
if (!test_bit(CAPABILITY_CONTROL_FILTER_PSPOLL, &rt2x00dev->cap_flags)) {
if (!rt2x00_has_cap_control_filter_pspoll(rt2x00dev)) {
if (*total_flags & FIF_CONTROL)
*total_flags |= FIF_PSPOLL;
}
@ -469,7 +469,7 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
return 0;
if (!test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags))
if (!rt2x00_has_cap_hw_crypto(rt2x00dev))
return -EOPNOTSUPP;
/*

View file

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

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
* and 8 bytes for ICV data as tailroon.
*/
if (test_bit(CAPABILITY_HW_CRYPTO, &rt2x00dev->cap_flags)) {
if (rt2x00_has_cap_hw_crypto(rt2x00dev)) {
head_size += 8;
tail_size += 8;
}
@ -1033,38 +1033,21 @@ EXPORT_SYMBOL_GPL(rt2x00queue_stop_queue);
void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)
{
bool started;
bool tx_queue =
(queue->qid == QID_AC_VO) ||
(queue->qid == QID_AC_VI) ||
(queue->qid == QID_AC_BE) ||
(queue->qid == QID_AC_BK);
mutex_lock(&queue->status_lock);
/*
* If the queue has been started, we must stop it temporarily
* to prevent any new frames to be queued on the device. If
* we are not dropping the pending frames, the queue must
* only be stopped in the software and not the hardware,
* otherwise the queue will never become empty on its own.
* If we are not supposed to drop any pending
* frames, this means we must force a start (=kick)
* to the queue to make sure the hardware will
* start transmitting.
*/
started = test_bit(QUEUE_STARTED, &queue->flags);
if (started) {
/*
* Pause the queue
*/
rt2x00queue_pause_queue(queue);
/*
* If we are not supposed to drop any pending
* frames, this means we must force a start (=kick)
* to the queue to make sure the hardware will
* start transmitting.
*/
if (!drop && tx_queue)
queue->rt2x00dev->ops->lib->kick_queue(queue);
}
if (!drop && tx_queue)
queue->rt2x00dev->ops->lib->kick_queue(queue);
/*
* Check if driver supports flushing, if that is the case we can
@ -1080,14 +1063,6 @@ void rt2x00queue_flush_queue(struct data_queue *queue, bool drop)
if (unlikely(!rt2x00queue_empty(queue)))
rt2x00_warn(queue->rt2x00dev, "Queue %d failed to flush\n",
queue->qid);
/*
* Restore the queue to the previous status
*/
if (started)
rt2x00queue_unpause_queue(queue);
mutex_unlock(&queue->status_lock);
}
EXPORT_SYMBOL_GPL(rt2x00queue_flush_queue);

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

View file

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

View file

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

View file

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

View file

@ -436,6 +436,15 @@ bool cfg80211_chandef_usable(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
u32 prohibited_flags);
/**
* cfg80211_chandef_dfs_required - checks if radar detection is required
* @wiphy: the wiphy to validate against
* @chandef: the channel definition to check
* Return: 1 if radar detection is required, 0 if it is not, < 0 on error
*/
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef);
/**
* ieee80211_chandef_rate_flags - returns rate flags for a channel
*

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_10MHZ: 10 MHz (half channel) was used
* @RX_FLAG_5MHZ: 5 MHz (quarter channel) was used
* @RX_FLAG_AMSDU_MORE: Some drivers may prefer to report separate A-MSDU
* subframes instead of a one huge frame for performance reasons.
* All, but the last MSDU from an A-MSDU should have this flag set. E.g.
* if an A-MSDU has 3 frames, the first 2 must have the flag set, while
* the 3rd (last) one must not have this flag set. The flag is used to
* deal with retransmission/duplication recovery properly since A-MSDU
* subframes share the same sequence number. Reported subframes can be
* either regular MSDU or singly A-MSDUs. Subframes must not be
* interleaved with other frames.
*/
enum mac80211_rx_flags {
RX_FLAG_MMIC_ERROR = BIT(0),
@ -859,6 +868,7 @@ enum mac80211_rx_flags {
RX_FLAG_STBC_MASK = BIT(26) | BIT(27),
RX_FLAG_10MHZ = BIT(28),
RX_FLAG_5MHZ = BIT(29),
RX_FLAG_AMSDU_MORE = BIT(30),
};
#define RX_FLAG_STBC_SHIFT 26
@ -1492,6 +1502,11 @@ struct ieee80211_tx_control {
*
* @IEEE80211_HW_TIMING_BEACON_ONLY: Use sync timing from beacon frames
* only, to allow getting TBTT of a DTIM beacon.
*
* @IEEE80211_HW_CHANCTX_STA_CSA: Support 802.11h based channel-switch (CSA)
* for a single active channel while using channel contexts. When support
* is not enabled the default action is to disconnect when getting the
* CSA frame.
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@ -1522,6 +1537,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_P2P_DEV_ADDR_FOR_INTF = 1<<25,
IEEE80211_HW_TIMING_BEACON_ONLY = 1<<26,
IEEE80211_HW_SUPPORTS_HT_CCK_RATES = 1<<27,
IEEE80211_HW_CHANCTX_STA_CSA = 1<<28,
};
/**
@ -2666,6 +2682,10 @@ enum ieee80211_roc_type {
* zero using ieee80211_csa_is_complete() after the beacon has been
* transmitted and then call ieee80211_csa_finish().
*
* @join_ibss: Join an IBSS (on an IBSS interface); this is called after all
* information in bss_conf is set up and the beacon can be retrieved. A
* channel context is bound before this is called.
* @leave_ibss: Leave the IBSS again.
*/
struct ieee80211_ops {
void (*tx)(struct ieee80211_hw *hw,
@ -2857,6 +2877,9 @@ struct ieee80211_ops {
void (*channel_switch_beacon)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_chan_def *chandef);
int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
};
/**
@ -3919,6 +3942,25 @@ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw,
struct ieee80211_vif *vif),
void *data);
/**
* ieee80211_iterate_active_interfaces_rtnl - iterate active interfaces
*
* This function iterates over the interfaces associated with a given
* hardware that are currently active and calls the callback for them.
* This version can only be used while holding the RTNL.
*
* @hw: the hardware struct of which the interfaces should be iterated over
* @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags
* @iterator: the iterator function to call, cannot sleep
* @data: first argument of the iterator function
*/
void ieee80211_iterate_active_interfaces_rtnl(struct ieee80211_hw *hw,
u32 iter_flags,
void (*iterator)(void *data,
u8 *mac,
struct ieee80211_vif *vif),
void *data);
/**
* ieee80211_queue_work - add work onto the mac80211 workqueue
*

View file

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

View file

@ -453,11 +453,6 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
drv_change_chanctx(local, ctx, chanctx_changed);
if (!local->use_chanctx) {
local->_oper_chandef = *chandef;
ieee80211_hw_config(local, 0);
}
ieee80211_recalc_chanctx_chantype(local, ctx);
ieee80211_recalc_smps_chanctx(local, ctx);
ieee80211_recalc_radar_chanctx(local, ctx);

View file

@ -103,54 +103,57 @@ static ssize_t hwflags_read(struct file *file, char __user *user_buf,
if (!buf)
return 0;
sf += snprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
sf += scnprintf(buf, mxln - sf, "0x%x\n", local->hw.flags);
if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
sf += snprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
sf += scnprintf(buf + sf, mxln - sf, "HAS_RATE_CONTROL\n");
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
sf += snprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
sf += scnprintf(buf + sf, mxln - sf, "RX_INCLUDES_FCS\n");
if (local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)
sf += snprintf(buf + sf, mxln - sf,
"HOST_BCAST_PS_BUFFERING\n");
sf += scnprintf(buf + sf, mxln - sf,
"HOST_BCAST_PS_BUFFERING\n");
if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)
sf += snprintf(buf + sf, mxln - sf,
"2GHZ_SHORT_SLOT_INCAPABLE\n");
sf += scnprintf(buf + sf, mxln - sf,
"2GHZ_SHORT_SLOT_INCAPABLE\n");
if (local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)
sf += snprintf(buf + sf, mxln - sf,
"2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
sf += scnprintf(buf + sf, mxln - sf,
"2GHZ_SHORT_PREAMBLE_INCAPABLE\n");
if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)
sf += snprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_UNSPEC\n");
if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
sf += snprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
sf += scnprintf(buf + sf, mxln - sf, "SIGNAL_DBM\n");
if (local->hw.flags & IEEE80211_HW_NEED_DTIM_BEFORE_ASSOC)
sf += snprintf(buf + sf, mxln - sf, "NEED_DTIM_BEFORE_ASSOC\n");
sf += scnprintf(buf + sf, mxln - sf,
"NEED_DTIM_BEFORE_ASSOC\n");
if (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT)
sf += snprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
sf += scnprintf(buf + sf, mxln - sf, "SPECTRUM_MGMT\n");
if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
sf += snprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
sf += scnprintf(buf + sf, mxln - sf, "AMPDU_AGGREGATION\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_PS)
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PS\n");
if (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)
sf += snprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
sf += scnprintf(buf + sf, mxln - sf, "PS_NULLFUNC_STACK\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_PS\n");
if (local->hw.flags & IEEE80211_HW_MFP_CAPABLE)
sf += snprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
sf += scnprintf(buf + sf, mxln - sf, "MFP_CAPABLE\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_STATIC_SMPS)
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_STATIC_SMPS\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS)
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_DYNAMIC_SMPS\n");
sf += scnprintf(buf + sf, mxln - sf,
"SUPPORTS_DYNAMIC_SMPS\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_UAPSD\n");
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
sf += snprintf(buf + sf, mxln - sf, "REPORTS_TX_ACK_STATUS\n");
sf += scnprintf(buf + sf, mxln - sf,
"REPORTS_TX_ACK_STATUS\n");
if (local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
sf += snprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
sf += scnprintf(buf + sf, mxln - sf, "CONNECTION_MONITOR\n");
if (local->hw.flags & IEEE80211_HW_SUPPORTS_PER_STA_GTK)
sf += snprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
sf += scnprintf(buf + sf, mxln - sf, "SUPPORTS_PER_STA_GTK\n");
if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
sf += snprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
sf += scnprintf(buf + sf, mxln - sf, "AP_LINK_PS\n");
if (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)
sf += snprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");
sf += scnprintf(buf + sf, mxln - sf, "TX_AMPDU_SETUP_IN_HW\n");
rv = simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
kfree(buf);

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

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 u16 capability, u64 tsf,
struct cfg80211_chan_def *chandef,
bool *have_higher_than_11mbit)
bool *have_higher_than_11mbit,
struct cfg80211_csa_settings *csa_settings)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
@ -59,6 +60,7 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
2 + 8 /* max Supported Rates */ +
3 /* max DS params */ +
4 /* IBSS params */ +
5 /* Channel Switch Announcement */ +
2 + (IEEE80211_MAX_SUPP_RATES - 8) +
2 + sizeof(struct ieee80211_ht_cap) +
2 + sizeof(struct ieee80211_ht_operation) +
@ -135,6 +137,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata,
*pos++ = 0;
*pos++ = 0;
if (csa_settings) {
*pos++ = WLAN_EID_CHANNEL_SWITCH;
*pos++ = 3;
*pos++ = csa_settings->block_tx ? 1 : 0;
*pos++ = ieee80211_frequency_to_channel(
csa_settings->chandef.chan->center_freq);
sdata->csa_counter_offset_beacon = (pos - presp->head);
*pos++ = csa_settings->count;
}
/* put the remaining rates in WLAN_EID_EXT_SUPP_RATES */
if (rates_n > 8) {
*pos++ = WLAN_EID_EXT_SUPP_RATES;
@ -217,6 +229,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
struct beacon_data *presp;
enum nl80211_bss_scan_width scan_width;
bool have_higher_than_11mbit;
int err;
sdata_assert_lock(sdata);
@ -235,6 +248,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
ieee80211_bss_info_change_notify(sdata,
BSS_CHANGED_IBSS |
BSS_CHANGED_BEACON_ENABLED);
drv_leave_ibss(local, sdata);
}
presp = rcu_dereference_protected(ifibss->presp,
@ -276,7 +290,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
presp = ieee80211_ibss_build_presp(sdata, beacon_int, basic_rates,
capability, tsf, &chandef,
&have_higher_than_11mbit);
&have_higher_than_11mbit, NULL);
if (!presp)
return;
@ -317,11 +331,26 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
ieee80211_set_wmm_default(sdata, true);
sdata->vif.bss_conf.ibss_joined = true;
sdata->vif.bss_conf.ibss_creator = creator;
ieee80211_bss_info_change_notify(sdata, bss_change);
ieee80211_set_wmm_default(sdata, true);
err = drv_join_ibss(local, sdata);
if (err) {
sdata->vif.bss_conf.ibss_joined = false;
sdata->vif.bss_conf.ibss_creator = false;
sdata->vif.bss_conf.enable_beacon = false;
sdata->vif.bss_conf.ssid_len = 0;
RCU_INIT_POINTER(ifibss->presp, NULL);
kfree_rcu(presp, rcu_head);
ieee80211_vif_release_channel(sdata);
sdata_info(sdata, "Failed to join IBSS, driver failure: %d\n",
err);
return;
}
ieee80211_bss_info_change_notify(sdata, bss_change);
ifibss->state = IEEE80211_IBSS_MLME_JOINED;
mod_timer(&ifibss->timer,
@ -416,6 +445,169 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
tsf, false);
}
static int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
struct cfg80211_csa_settings *csa_settings)
{
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
int freq;
int hdr_len = offsetof(struct ieee80211_mgmt, u.action.u.chan_switch) +
sizeof(mgmt->u.action.u.chan_switch);
u8 *pos;
skb = dev_alloc_skb(local->tx_headroom + hdr_len +
5 + /* channel switch announcement element */
3); /* secondary channel offset element */
if (!skb)
return -1;
skb_reserve(skb, local->tx_headroom);
mgmt = (struct ieee80211_mgmt *)skb_put(skb, hdr_len);
memset(mgmt, 0, hdr_len);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_ACTION);
eth_broadcast_addr(mgmt->da);
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
memcpy(mgmt->bssid, ifibss->bssid, ETH_ALEN);
mgmt->u.action.category = WLAN_CATEGORY_SPECTRUM_MGMT;
mgmt->u.action.u.chan_switch.action_code = WLAN_ACTION_SPCT_CHL_SWITCH;
pos = skb_put(skb, 5);
*pos++ = WLAN_EID_CHANNEL_SWITCH; /* EID */
*pos++ = 3; /* IE length */
*pos++ = csa_settings->block_tx ? 1 : 0; /* CSA mode */
freq = csa_settings->chandef.chan->center_freq;
*pos++ = ieee80211_frequency_to_channel(freq); /* channel */
*pos++ = csa_settings->count; /* count */
if (csa_settings->chandef.width == NL80211_CHAN_WIDTH_40) {
enum nl80211_channel_type ch_type;
skb_put(skb, 3);
*pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET; /* EID */
*pos++ = 1; /* IE length */
ch_type = cfg80211_get_chandef_type(&csa_settings->chandef);
if (ch_type == NL80211_CHAN_HT40PLUS)
*pos++ = IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
else
*pos++ = IEEE80211_HT_PARAM_CHA_SEC_BELOW;
}
ieee80211_tx_skb(sdata, skb);
return 0;
}
int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
struct cfg80211_csa_settings *csa_settings)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct beacon_data *presp, *old_presp;
struct cfg80211_bss *cbss;
const struct cfg80211_bss_ies *ies;
u16 capability;
u64 tsf;
int ret = 0;
sdata_assert_lock(sdata);
capability = WLAN_CAPABILITY_IBSS;
if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid,
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
WLAN_CAPABILITY_PRIVACY,
capability);
if (WARN_ON(!cbss)) {
ret = -EINVAL;
goto out;
}
rcu_read_lock();
ies = rcu_dereference(cbss->ies);
tsf = ies->tsf;
rcu_read_unlock();
cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
old_presp = rcu_dereference_protected(ifibss->presp,
lockdep_is_held(&sdata->wdev.mtx));
presp = ieee80211_ibss_build_presp(sdata,
sdata->vif.bss_conf.beacon_int,
sdata->vif.bss_conf.basic_rates,
capability, tsf, &ifibss->chandef,
NULL, csa_settings);
if (!presp) {
ret = -ENOMEM;
goto out;
}
rcu_assign_pointer(ifibss->presp, presp);
if (old_presp)
kfree_rcu(old_presp, rcu_head);
/* it might not send the beacon for a while. send an action frame
* immediately to announce the channel switch.
*/
if (csa_settings)
ieee80211_send_action_csa(sdata, csa_settings);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
out:
return ret;
}
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct cfg80211_bss *cbss;
int err;
u16 capability;
sdata_lock(sdata);
/* update cfg80211 bss information with the new channel */
if (!is_zero_ether_addr(ifibss->bssid)) {
capability = WLAN_CAPABILITY_IBSS;
if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
cbss = cfg80211_get_bss(sdata->local->hw.wiphy,
ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid,
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
WLAN_CAPABILITY_PRIVACY,
capability);
/* XXX: should not really modify cfg80211 data */
if (cbss) {
cbss->channel = sdata->local->csa_chandef.chan;
cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
}
}
ifibss->chandef = sdata->local->csa_chandef;
/* generate the beacon */
err = ieee80211_ibss_csa_beacon(sdata, NULL);
sdata_unlock(sdata);
if (err < 0)
return err;
return 0;
}
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
cancel_work_sync(&ifibss->csa_connection_drop_work);
}
static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta)
__acquires(RCU)
{
@ -499,6 +691,295 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, const u8 *bssid,
return ieee80211_ibss_finish_sta(sta);
}
static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
int active = 0;
struct sta_info *sta;
sdata_assert_lock(sdata);
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sta->sdata == sdata &&
time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
jiffies)) {
active++;
break;
}
}
rcu_read_unlock();
return active;
}
static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
struct cfg80211_bss *cbss;
struct beacon_data *presp;
struct sta_info *sta;
int active_ibss;
u16 capability;
active_ibss = ieee80211_sta_active_ibss(sdata);
if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
capability = WLAN_CAPABILITY_IBSS;
if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid,
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
WLAN_CAPABILITY_PRIVACY,
capability);
if (cbss) {
cfg80211_unlink_bss(local->hw.wiphy, cbss);
cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
}
}
ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
sta_info_flush(sdata);
spin_lock_bh(&ifibss->incomplete_lock);
while (!list_empty(&ifibss->incomplete_stations)) {
sta = list_first_entry(&ifibss->incomplete_stations,
struct sta_info, list);
list_del(&sta->list);
spin_unlock_bh(&ifibss->incomplete_lock);
sta_info_free(local, sta);
spin_lock_bh(&ifibss->incomplete_lock);
}
spin_unlock_bh(&ifibss->incomplete_lock);
netif_carrier_off(sdata->dev);
sdata->vif.bss_conf.ibss_joined = false;
sdata->vif.bss_conf.ibss_creator = false;
sdata->vif.bss_conf.enable_beacon = false;
sdata->vif.bss_conf.ssid_len = 0;
/* remove beacon */
presp = rcu_dereference_protected(ifibss->presp,
lockdep_is_held(&sdata->wdev.mtx));
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
if (presp)
kfree_rcu(presp, rcu_head);
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_IBSS);
drv_leave_ibss(local, sdata);
ieee80211_vif_release_channel(sdata);
}
static void ieee80211_csa_connection_drop_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data,
u.ibss.csa_connection_drop_work);
ieee80211_ibss_disconnect(sdata);
synchronize_rcu();
skb_queue_purge(&sdata->skb_queue);
/* trigger a scan to find another IBSS network to join */
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
}
static bool
ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems,
bool beacon)
{
struct cfg80211_csa_settings params;
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_chanctx *chanctx;
enum nl80211_channel_type ch_type;
int err, num_chanctx;
u32 sta_flags;
u8 mode;
if (sdata->vif.csa_active)
return true;
if (!sdata->vif.bss_conf.ibss_joined)
return false;
sta_flags = IEEE80211_STA_DISABLE_VHT;
switch (ifibss->chandef.width) {
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
case NL80211_CHAN_WIDTH_20_NOHT:
sta_flags |= IEEE80211_STA_DISABLE_HT;
/* fall through */
case NL80211_CHAN_WIDTH_20:
sta_flags |= IEEE80211_STA_DISABLE_40MHZ;
break;
default:
break;
}
memset(&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,
struct ieee80211_mgmt *mgmt,
size_t len)
@ -661,10 +1142,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
/* check if we need to merge IBSS */
/* we use a fixed BSSID */
if (sdata->u.ibss.fixed_bssid)
goto put_bss;
/* not an IBSS */
if (!(cbss->capability & WLAN_CAPABILITY_IBSS))
goto put_bss;
@ -680,10 +1157,18 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
sdata->u.ibss.ssid_len))
goto put_bss;
/* process channel switch */
if (ieee80211_ibss_process_chanswitch(sdata, elems, true))
goto put_bss;
/* same BSSID */
if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid))
goto put_bss;
/* we use a fixed BSSID */
if (sdata->u.ibss.fixed_bssid)
goto put_bss;
if (ieee80211_have_rx_timestamp(rx_status)) {
/* time when timestamp field was received */
rx_timestamp =
@ -775,30 +1260,6 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata,
ieee80211_queue_work(&local->hw, &sdata->work);
}
static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
int active = 0;
struct sta_info *sta;
sdata_assert_lock(sdata);
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sta->sdata == sdata &&
time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
jiffies)) {
active++;
break;
}
}
rcu_read_unlock();
return active;
}
static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
@ -1076,6 +1537,8 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct ieee80211_rx_status *rx_status;
struct ieee80211_mgmt *mgmt;
u16 fc;
struct ieee802_11_elems elems;
int ies_len;
rx_status = IEEE80211_SKB_RXCB(skb);
mgmt = (struct ieee80211_mgmt *) skb->data;
@ -1101,6 +1564,27 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
case IEEE80211_STYPE_DEAUTH:
ieee80211_rx_mgmt_deauth_ibss(sdata, mgmt, skb->len);
break;
case IEEE80211_STYPE_ACTION:
switch (mgmt->u.action.category) {
case WLAN_CATEGORY_SPECTRUM_MGMT:
ies_len = skb->len -
offsetof(struct ieee80211_mgmt,
u.action.u.chan_switch.variable);
if (ies_len < 0)
break;
ieee802_11_parse_elems(
mgmt->u.action.u.chan_switch.variable,
ies_len, true, &elems);
if (elems.parse_error)
break;
ieee80211_rx_mgmt_spectrum_mgmt(sdata, mgmt, skb->len,
rx_status, &elems);
break;
}
}
mgmt_out:
@ -1167,6 +1651,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata)
(unsigned long) sdata);
INIT_LIST_HEAD(&ifibss->incomplete_stations);
spin_lock_init(&ifibss->incomplete_lock);
INIT_WORK(&ifibss->csa_connection_drop_work,
ieee80211_csa_connection_drop_work);
}
/* scan finished notification */
@ -1265,73 +1751,19 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
struct ieee80211_local *local = sdata->local;
struct cfg80211_bss *cbss;
u16 capability;
int active_ibss;
struct sta_info *sta;
struct beacon_data *presp;
active_ibss = ieee80211_sta_active_ibss(sdata);
if (!active_ibss && !is_zero_ether_addr(ifibss->bssid)) {
capability = WLAN_CAPABILITY_IBSS;
if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan,
ifibss->bssid, ifibss->ssid,
ifibss->ssid_len, WLAN_CAPABILITY_IBSS |
WLAN_CAPABILITY_PRIVACY,
capability);
if (cbss) {
cfg80211_unlink_bss(local->hw.wiphy, cbss);
cfg80211_put_bss(local->hw.wiphy, cbss);
}
}
ifibss->state = IEEE80211_IBSS_MLME_SEARCH;
memset(ifibss->bssid, 0, ETH_ALEN);
ieee80211_ibss_disconnect(sdata);
ifibss->ssid_len = 0;
sta_info_flush(sdata);
spin_lock_bh(&ifibss->incomplete_lock);
while (!list_empty(&ifibss->incomplete_stations)) {
sta = list_first_entry(&ifibss->incomplete_stations,
struct sta_info, list);
list_del(&sta->list);
spin_unlock_bh(&ifibss->incomplete_lock);
sta_info_free(local, sta);
spin_lock_bh(&ifibss->incomplete_lock);
}
spin_unlock_bh(&ifibss->incomplete_lock);
netif_carrier_off(sdata->dev);
memset(ifibss->bssid, 0, ETH_ALEN);
/* remove beacon */
kfree(sdata->u.ibss.ie);
presp = rcu_dereference_protected(ifibss->presp,
lockdep_is_held(&sdata->wdev.mtx));
RCU_INIT_POINTER(sdata->u.ibss.presp, NULL);
/* on the next join, re-program HT parameters */
memset(&ifibss->ht_capa, 0, sizeof(ifibss->ht_capa));
memset(&ifibss->ht_capa_mask, 0, sizeof(ifibss->ht_capa_mask));
sdata->vif.bss_conf.ibss_joined = false;
sdata->vif.bss_conf.ibss_creator = false;
sdata->vif.bss_conf.enable_beacon = false;
sdata->vif.bss_conf.ssid_len = 0;
clear_bit(SDATA_STATE_OFFCHANNEL_BEACON_STOPPED, &sdata->state);
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED |
BSS_CHANGED_IBSS);
ieee80211_vif_release_channel(sdata);
synchronize_rcu();
kfree(presp);
skb_queue_purge(&sdata->skb_queue);

View file

@ -322,7 +322,6 @@ struct ieee80211_roc_work {
/* flags used in struct ieee80211_if_managed.flags */
enum ieee80211_sta_flags {
IEEE80211_STA_BEACON_POLL = BIT(0),
IEEE80211_STA_CONNECTION_POLL = BIT(1),
IEEE80211_STA_CONTROL_PORT = BIT(2),
IEEE80211_STA_DISABLE_HT = BIT(4),
@ -487,6 +486,7 @@ struct ieee80211_if_managed {
struct ieee80211_if_ibss {
struct timer_list timer;
struct work_struct csa_connection_drop_work;
unsigned long last_scan_completed;
@ -1330,6 +1330,10 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
struct cfg80211_csa_settings *csa_settings);
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
/* mesh code */
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
@ -1481,6 +1485,29 @@ void ieee80211_apply_vhtcap_overrides(struct ieee80211_sub_if_data *sdata,
void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
size_t len);
/**
* ieee80211_parse_ch_switch_ie - parses channel switch IEs
* @sdata: the sdata of the interface which has received the frame
* @elems: parsed 802.11 elements received with the frame
* @beacon: indicates if the frame was a beacon or probe response
* @current_band: indicates the current band
* @sta_flags: contains information about own capabilities and restrictions
* to decide which channel switch announcements can be accepted. Only the
* following subset of &enum ieee80211_sta_flags are evaluated:
* %IEEE80211_STA_DISABLE_HT, %IEEE80211_STA_DISABLE_VHT,
* %IEEE80211_STA_DISABLE_40MHZ, %IEEE80211_STA_DISABLE_80P80MHZ,
* %IEEE80211_STA_DISABLE_160MHZ.
* @count: to be filled with the counter until the switch (on success only)
* @bssid: the currently connected bssid (for reporting)
* @mode: to be filled with CSA mode (on success only)
* @new_chandef: to be filled with destination chandef (on success only)
* Return: 0 on success, <0 on error and >0 if there is nothing to parse.
*/
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems, bool beacon,
enum ieee80211_band current_band,
u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
struct cfg80211_chan_def *new_chandef);
/* Suspend/resume and hw reconfiguration */
int ieee80211_reconfig(struct ieee80211_local *local);
@ -1654,6 +1681,7 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata,
void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan,
const struct ieee80211_ht_operation *ht_oper,
struct cfg80211_chan_def *chandef);
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c);
int __must_check
ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,

View file

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

View file

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

View file

@ -145,66 +145,6 @@ static int ecw2cw(int ecw)
return (1 << ecw) - 1;
}
static u32 chandef_downgrade(struct cfg80211_chan_def *c)
{
u32 ret;
int tmp;
switch (c->width) {
case NL80211_CHAN_WIDTH_20:
c->width = NL80211_CHAN_WIDTH_20_NOHT;
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_40:
c->width = NL80211_CHAN_WIDTH_20;
c->center_freq1 = c->chan->center_freq;
ret = IEEE80211_STA_DISABLE_40MHZ |
IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_80:
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
/* n_P40 */
tmp /= 2;
/* freq_P40 */
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
c->width = NL80211_CHAN_WIDTH_40;
ret = IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_80P80:
c->center_freq2 = 0;
c->width = NL80211_CHAN_WIDTH_80;
ret = IEEE80211_STA_DISABLE_80P80MHZ |
IEEE80211_STA_DISABLE_160MHZ;
break;
case NL80211_CHAN_WIDTH_160:
/* n_P20 */
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
/* n_P80 */
tmp /= 4;
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
c->width = NL80211_CHAN_WIDTH_80;
ret = IEEE80211_STA_DISABLE_80P80MHZ |
IEEE80211_STA_DISABLE_160MHZ;
break;
default:
case NL80211_CHAN_WIDTH_20_NOHT:
WARN_ON_ONCE(1);
c->width = NL80211_CHAN_WIDTH_20_NOHT;
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
WARN_ON_ONCE(1);
/* keep c->width */
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
break;
}
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
return ret;
}
static u32
ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
struct ieee80211_supported_band *sband,
@ -352,7 +292,7 @@ ieee80211_determine_chantype(struct ieee80211_sub_if_data *sdata,
break;
}
ret |= chandef_downgrade(chandef);
ret |= ieee80211_chandef_downgrade(chandef);
}
if (chandef->width != vht_chandef.width && !tracking)
@ -406,13 +346,13 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
*/
if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
chandef.width == NL80211_CHAN_WIDTH_80P80)
flags |= chandef_downgrade(&chandef);
flags |= ieee80211_chandef_downgrade(&chandef);
if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
chandef.width == NL80211_CHAN_WIDTH_160)
flags |= chandef_downgrade(&chandef);
flags |= ieee80211_chandef_downgrade(&chandef);
if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
chandef.width > NL80211_CHAN_WIDTH_20)
flags |= chandef_downgrade(&chandef);
flags |= ieee80211_chandef_downgrade(&chandef);
if (cfg80211_chandef_identical(&chandef, &sdata->vif.bss_conf.chandef))
return 0;
@ -893,8 +833,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
IEEE80211_STA_CONNECTION_POLL))
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_USE_MINRATE;
ieee80211_tx_skb(sdata, skb);
@ -937,6 +876,8 @@ static void ieee80211_chswitch_work(struct work_struct *work)
container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u32 changed = 0;
int ret;
if (!ieee80211_sdata_running(sdata))
return;
@ -945,24 +886,39 @@ static void ieee80211_chswitch_work(struct work_struct *work)
if (!ifmgd->associated)
goto out;
local->_oper_chandef = local->csa_chandef;
ret = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
&changed);
if (ret) {
sdata_info(sdata,
"vif channel switch failed, disconnecting\n");
ieee80211_queue_work(&sdata->local->hw,
&ifmgd->csa_connection_drop_work);
goto out;
}
if (!local->ops->channel_switch) {
/* call "hw_config" only if doing sw channel switch */
ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
} else {
/* update the device channel directly */
local->hw.conf.chandef = local->_oper_chandef;
if (!local->use_chanctx) {
local->_oper_chandef = local->csa_chandef;
/* Call "hw_config" only if doing sw channel switch.
* Otherwise update the channel directly
*/
if (!local->ops->channel_switch)
ieee80211_hw_config(local, 0);
else
local->hw.conf.chandef = local->_oper_chandef;
}
/* XXX: shouldn't really modify cfg80211-owned data! */
ifmgd->associated->channel = local->_oper_chandef.chan;
ifmgd->associated->channel = local->csa_chandef.chan;
/* XXX: wait for a beacon first? */
ieee80211_wake_queues_by_reason(&local->hw,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA);
ieee80211_bss_info_change_notify(sdata, changed);
out:
sdata->vif.csa_active = false;
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
sdata_unlock(sdata);
}
@ -1000,20 +956,12 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct cfg80211_bss *cbss = ifmgd->associated;
struct ieee80211_bss *bss;
struct ieee80211_chanctx *chanctx;
enum ieee80211_band new_band;
int new_freq;
u8 new_chan_no;
enum ieee80211_band current_band;
u8 count;
u8 mode;
struct ieee80211_channel *new_chan;
struct cfg80211_chan_def new_chandef = {};
struct cfg80211_chan_def new_vht_chandef = {};
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
const struct ieee80211_ht_operation *ht_oper;
int secondary_channel_offset = -1;
int res;
sdata_assert_lock(sdata);
@ -1027,162 +975,23 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED)
return;
sec_chan_offs = elems->sec_chan_offs;
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
ht_oper = elems->ht_operation;
if (ifmgd->flags & (IEEE80211_STA_DISABLE_HT |
IEEE80211_STA_DISABLE_40MHZ)) {
sec_chan_offs = NULL;
wide_bw_chansw_ie = NULL;
/* only used for bandwidth here */
ht_oper = NULL;
}
if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
wide_bw_chansw_ie = NULL;
if (elems->ext_chansw_ie) {
if (!ieee80211_operating_class_to_band(
elems->ext_chansw_ie->new_operating_class,
&new_band)) {
sdata_info(sdata,
"cannot understand ECSA IE operating class %d, disconnecting\n",
elems->ext_chansw_ie->new_operating_class);
ieee80211_queue_work(&local->hw,
&ifmgd->csa_connection_drop_work);
}
new_chan_no = elems->ext_chansw_ie->new_ch_num;
count = elems->ext_chansw_ie->count;
mode = elems->ext_chansw_ie->mode;
} else if (elems->ch_switch_ie) {
new_band = cbss->channel->band;
new_chan_no = elems->ch_switch_ie->new_ch_num;
count = elems->ch_switch_ie->count;
mode = elems->ch_switch_ie->mode;
} else {
/* nothing here we understand */
return;
}
bss = (void *)cbss->priv;
new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
sdata_info(sdata,
"AP %pM switches to unsupported channel (%d MHz), disconnecting\n",
ifmgd->associated->bssid, new_freq);
current_band = cbss->channel->band;
res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
ifmgd->flags,
ifmgd->associated->bssid, &count,
&mode, &new_chandef);
if (res < 0)
ieee80211_queue_work(&local->hw,
&ifmgd->csa_connection_drop_work);
if (res)
return;
}
if (!beacon && sec_chan_offs) {
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
} else if (beacon && ht_oper) {
secondary_channel_offset =
ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
} else if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) {
/*
* If it's not a beacon, HT is enabled and the IE not present,
* it's 20 MHz, 802.11-2012 8.5.2.6:
* This element [the Secondary Channel Offset Element] is
* present when switching to a 40 MHz channel. It may be
* present when switching to a 20 MHz channel (in which
* case the secondary channel offset is set to SCN).
*/
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
}
switch (secondary_channel_offset) {
default:
/* secondary_channel_offset was present but is invalid */
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
cfg80211_chandef_create(&new_chandef, new_chan,
NL80211_CHAN_HT20);
break;
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
cfg80211_chandef_create(&new_chandef, new_chan,
NL80211_CHAN_HT40PLUS);
break;
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
cfg80211_chandef_create(&new_chandef, new_chan,
NL80211_CHAN_HT40MINUS);
break;
case -1:
cfg80211_chandef_create(&new_chandef, new_chan,
NL80211_CHAN_NO_HT);
/* keep width for 5/10 MHz channels */
switch (sdata->vif.bss_conf.chandef.width) {
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
new_chandef.width = sdata->vif.bss_conf.chandef.width;
break;
default:
break;
}
break;
}
if (wide_bw_chansw_ie) {
new_vht_chandef.chan = new_chan;
new_vht_chandef.center_freq1 =
ieee80211_channel_to_frequency(
wide_bw_chansw_ie->new_center_freq_seg0,
new_band);
switch (wide_bw_chansw_ie->new_channel_width) {
default:
/* hmmm, ignore VHT and use HT if present */
case IEEE80211_VHT_CHANWIDTH_USE_HT:
new_vht_chandef.chan = NULL;
break;
case IEEE80211_VHT_CHANWIDTH_80MHZ:
new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
break;
case IEEE80211_VHT_CHANWIDTH_160MHZ:
new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
break;
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
/* field is otherwise reserved */
new_vht_chandef.center_freq2 =
ieee80211_channel_to_frequency(
wide_bw_chansw_ie->new_center_freq_seg1,
new_band);
new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
break;
}
if (ifmgd->flags & IEEE80211_STA_DISABLE_80P80MHZ &&
new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
chandef_downgrade(&new_vht_chandef);
if (ifmgd->flags & IEEE80211_STA_DISABLE_160MHZ &&
new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
chandef_downgrade(&new_vht_chandef);
if (ifmgd->flags & IEEE80211_STA_DISABLE_40MHZ &&
new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
chandef_downgrade(&new_vht_chandef);
}
/* if VHT data is there validate & use it */
if (new_vht_chandef.chan) {
if (!cfg80211_chandef_compatible(&new_vht_chandef,
&new_chandef)) {
sdata_info(sdata,
"AP %pM CSA has inconsistent channel data, disconnecting\n",
ifmgd->associated->bssid);
ieee80211_queue_work(&local->hw,
&ifmgd->csa_connection_drop_work);
return;
}
new_chandef = new_vht_chandef;
}
if (!cfg80211_chandef_usable(local->hw.wiphy, &new_chandef,
IEEE80211_CHAN_DISABLED)) {
sdata_info(sdata,
"AP %pM switches to unsupported channel (%d MHz, width:%d, CF1/2: %d/%d MHz), disconnecting\n",
ifmgd->associated->bssid, new_freq,
ifmgd->associated->bssid,
new_chandef.chan->center_freq,
new_chandef.width, new_chandef.center_freq1,
new_chandef.center_freq2);
ieee80211_queue_work(&local->hw,
@ -1191,17 +1000,28 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
}
ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED;
if (local->use_chanctx) {
sdata_info(sdata,
"not handling channel switch with channel contexts\n");
ieee80211_queue_work(&local->hw,
&ifmgd->csa_connection_drop_work);
return;
}
sdata->vif.csa_active = true;
mutex_lock(&local->chanctx_mtx);
if (local->use_chanctx) {
u32 num_chanctx = 0;
list_for_each_entry(chanctx, &local->chanctx_list, list)
num_chanctx++;
if (num_chanctx > 1 ||
!(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
sdata_info(sdata,
"not handling chan-switch with channel contexts\n");
ieee80211_queue_work(&local->hw,
&ifmgd->csa_connection_drop_work);
mutex_unlock(&local->chanctx_mtx);
return;
}
}
if (WARN_ON(!rcu_access_pointer(sdata->vif.chanctx_conf))) {
ieee80211_queue_work(&local->hw,
&ifmgd->csa_connection_drop_work);
mutex_unlock(&local->chanctx_mtx);
return;
}
@ -1374,8 +1194,7 @@ static bool ieee80211_powersave_allowed(struct ieee80211_sub_if_data *sdata)
if (!mgd->associated)
return false;
if (mgd->flags & (IEEE80211_STA_BEACON_POLL |
IEEE80211_STA_CONNECTION_POLL))
if (mgd->flags & IEEE80211_STA_CONNECTION_POLL)
return false;
if (!mgd->have_beacon)
@ -1691,8 +1510,7 @@ static void __ieee80211_stop_poll(struct ieee80211_sub_if_data *sdata)
{
lockdep_assert_held(&sdata->local->mtx);
sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
IEEE80211_STA_BEACON_POLL);
sdata->u.mgd.flags &= ~IEEE80211_STA_CONNECTION_POLL;
ieee80211_run_deferred_scan(sdata->local);
}
@ -1954,11 +1772,8 @@ static void ieee80211_reset_ap_probe(struct ieee80211_sub_if_data *sdata)
struct ieee80211_local *local = sdata->local;
mutex_lock(&local->mtx);
if (!(ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
IEEE80211_STA_CONNECTION_POLL))) {
mutex_unlock(&local->mtx);
return;
}
if (!(ifmgd->flags & IEEE80211_STA_CONNECTION_POLL))
goto out;
__ieee80211_stop_poll(sdata);
@ -2094,15 +1909,9 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
* because otherwise we would reset the timer every time and
* never check whether we received a probe response!
*/
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
IEEE80211_STA_CONNECTION_POLL))
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL)
already = true;
if (beacon)
ifmgd->flags |= IEEE80211_STA_BEACON_POLL;
else
ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
mutex_unlock(&sdata->local->mtx);
if (already)
@ -2174,6 +1983,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
true, frame_buf);
ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
sdata->vif.csa_active = false;
ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA);
@ -3061,17 +2871,10 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL) {
mlme_dbg_ratelimited(sdata,
"cancelling AP probe due to a received beacon\n");
mutex_lock(&local->mtx);
ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
ieee80211_run_deferred_scan(local);
mutex_unlock(&local->mtx);
mutex_lock(&local->iflist_mtx);
ieee80211_recalc_ps(local, -1);
mutex_unlock(&local->iflist_mtx);
ieee80211_reset_ap_probe(sdata);
}
/*
@ -3543,8 +3346,7 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
} else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started)
run_again(sdata, ifmgd->assoc_data->timeout);
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
IEEE80211_STA_CONNECTION_POLL) &&
if (ifmgd->flags & IEEE80211_STA_CONNECTION_POLL &&
ifmgd->associated) {
u8 bssid[ETH_ALEN];
int max_tries;
@ -3876,7 +3678,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
return ret;
while (ret && chandef.width != NL80211_CHAN_WIDTH_20_NOHT) {
ifmgd->flags |= chandef_downgrade(&chandef);
ifmgd->flags |= ieee80211_chandef_downgrade(&chandef);
ret = ieee80211_vif_use_channel(sdata, &chandef,
IEEE80211_CHANCTX_SHARED);
}

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

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;
}
@ -774,6 +782,11 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
info->flags |= mi->tx_flags;
minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
#ifdef CONFIG_MAC80211_DEBUGFS
if (mp->fixed_rate_idx != -1)
return;
#endif
/* Don't use EAPOL frames for sampling on non-mrr hw */
if (mp->hw->max_rates == 1 &&
(info->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO))
@ -781,16 +794,6 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
else
sample_idx = minstrel_get_sample_rate(mp, mi);
#ifdef CONFIG_MAC80211_DEBUGFS
/* use fixed index if set */
if (mp->fixed_rate_idx != -1) {
mi->max_tp_rate = mp->fixed_rate_idx;
mi->max_tp_rate2 = mp->fixed_rate_idx;
mi->max_prob_rate = mp->fixed_rate_idx;
sample_idx = -1;
}
#endif
mi->total_packets++;
/* wraparound */

View file

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

View file

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

View file

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

View file

@ -21,6 +21,168 @@
#include "sta_info.h"
#include "wme.h"
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *elems, bool beacon,
enum ieee80211_band current_band,
u32 sta_flags, u8 *bssid, u8 *count, u8 *mode,
struct cfg80211_chan_def *new_chandef)
{
enum ieee80211_band new_band;
int new_freq;
u8 new_chan_no;
struct ieee80211_channel *new_chan;
struct cfg80211_chan_def new_vht_chandef = {};
const struct ieee80211_sec_chan_offs_ie *sec_chan_offs;
const struct ieee80211_wide_bw_chansw_ie *wide_bw_chansw_ie;
const struct ieee80211_ht_operation *ht_oper;
int secondary_channel_offset = -1;
sec_chan_offs = elems->sec_chan_offs;
wide_bw_chansw_ie = elems->wide_bw_chansw_ie;
ht_oper = elems->ht_operation;
if (sta_flags & (IEEE80211_STA_DISABLE_HT |
IEEE80211_STA_DISABLE_40MHZ)) {
sec_chan_offs = NULL;
wide_bw_chansw_ie = NULL;
/* only used for bandwidth here */
ht_oper = NULL;
}
if (sta_flags & IEEE80211_STA_DISABLE_VHT)
wide_bw_chansw_ie = NULL;
if (elems->ext_chansw_ie) {
if (!ieee80211_operating_class_to_band(
elems->ext_chansw_ie->new_operating_class,
&new_band)) {
sdata_info(sdata,
"cannot understand ECSA IE operating class %d, disconnecting\n",
elems->ext_chansw_ie->new_operating_class);
return -EINVAL;
}
new_chan_no = elems->ext_chansw_ie->new_ch_num;
*count = elems->ext_chansw_ie->count;
*mode = elems->ext_chansw_ie->mode;
} else if (elems->ch_switch_ie) {
new_band = current_band;
new_chan_no = elems->ch_switch_ie->new_ch_num;
*count = elems->ch_switch_ie->count;
*mode = elems->ch_switch_ie->mode;
} else {
/* nothing here we understand */
return 1;
}
new_freq = ieee80211_channel_to_frequency(new_chan_no, new_band);
new_chan = ieee80211_get_channel(sdata->local->hw.wiphy, new_freq);
if (!new_chan || new_chan->flags & IEEE80211_CHAN_DISABLED) {
sdata_info(sdata,
"BSS %pM switches to unsupported channel (%d MHz), disconnecting\n",
bssid, new_freq);
return -EINVAL;
}
if (!beacon && sec_chan_offs) {
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
} else if (beacon && ht_oper) {
secondary_channel_offset =
ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
/* If it's not a beacon, HT is enabled and the IE not present,
* it's 20 MHz, 802.11-2012 8.5.2.6:
* This element [the Secondary Channel Offset Element] is
* present when switching to a 40 MHz channel. It may be
* present when switching to a 20 MHz channel (in which
* case the secondary channel offset is set to SCN).
*/
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
}
switch (secondary_channel_offset) {
default:
/* secondary_channel_offset was present but is invalid */
case IEEE80211_HT_PARAM_CHA_SEC_NONE:
cfg80211_chandef_create(new_chandef, new_chan,
NL80211_CHAN_HT20);
break;
case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
cfg80211_chandef_create(new_chandef, new_chan,
NL80211_CHAN_HT40PLUS);
break;
case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
cfg80211_chandef_create(new_chandef, new_chan,
NL80211_CHAN_HT40MINUS);
break;
case -1:
cfg80211_chandef_create(new_chandef, new_chan,
NL80211_CHAN_NO_HT);
/* keep width for 5/10 MHz channels */
switch (sdata->vif.bss_conf.chandef.width) {
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
new_chandef->width = sdata->vif.bss_conf.chandef.width;
break;
default:
break;
}
break;
}
if (wide_bw_chansw_ie) {
new_vht_chandef.chan = new_chan;
new_vht_chandef.center_freq1 =
ieee80211_channel_to_frequency(
wide_bw_chansw_ie->new_center_freq_seg0,
new_band);
switch (wide_bw_chansw_ie->new_channel_width) {
default:
/* hmmm, ignore VHT and use HT if present */
case IEEE80211_VHT_CHANWIDTH_USE_HT:
new_vht_chandef.chan = NULL;
break;
case IEEE80211_VHT_CHANWIDTH_80MHZ:
new_vht_chandef.width = NL80211_CHAN_WIDTH_80;
break;
case IEEE80211_VHT_CHANWIDTH_160MHZ:
new_vht_chandef.width = NL80211_CHAN_WIDTH_160;
break;
case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
/* field is otherwise reserved */
new_vht_chandef.center_freq2 =
ieee80211_channel_to_frequency(
wide_bw_chansw_ie->new_center_freq_seg1,
new_band);
new_vht_chandef.width = NL80211_CHAN_WIDTH_80P80;
break;
}
if (sta_flags & IEEE80211_STA_DISABLE_80P80MHZ &&
new_vht_chandef.width == NL80211_CHAN_WIDTH_80P80)
ieee80211_chandef_downgrade(&new_vht_chandef);
if (sta_flags & IEEE80211_STA_DISABLE_160MHZ &&
new_vht_chandef.width == NL80211_CHAN_WIDTH_160)
ieee80211_chandef_downgrade(&new_vht_chandef);
if (sta_flags & IEEE80211_STA_DISABLE_40MHZ &&
new_vht_chandef.width > NL80211_CHAN_WIDTH_20)
ieee80211_chandef_downgrade(&new_vht_chandef);
}
/* if VHT data is there validate & use it */
if (new_vht_chandef.chan) {
if (!cfg80211_chandef_compatible(&new_vht_chandef,
new_chandef)) {
sdata_info(sdata,
"BSS %pM: CSA has inconsistent channel data, disconnecting\n",
bssid);
return -EINVAL;
}
*new_chandef = new_vht_chandef;
}
return 0;
}
static void ieee80211_send_refuse_measurement_request(struct ieee80211_sub_if_data *sdata,
struct ieee80211_msrment_ie *request_ie,
const u8 *da, const u8 *bssid,

View file

@ -1475,6 +1475,41 @@ DEFINE_EVENT(local_sdata_evt, drv_ipv6_addr_change,
);
#endif
TRACE_EVENT(drv_join_ibss,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_bss_conf *info),
TP_ARGS(local, sdata, info),
TP_STRUCT__entry(
LOCAL_ENTRY
VIF_ENTRY
__field(u8, dtimper)
__field(u16, bcnint)
__dynamic_array(u8, ssid, info->ssid_len);
),
TP_fast_assign(
LOCAL_ASSIGN;
VIF_ASSIGN;
__entry->dtimper = info->dtim_period;
__entry->bcnint = info->beacon_int;
memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len);
),
TP_printk(
LOCAL_PR_FMT VIF_PR_FMT,
LOCAL_PR_ARG, VIF_PR_ARG
)
);
DEFINE_EVENT(local_sdata_evt, drv_leave_ibss,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata),
TP_ARGS(local, sdata)
);
/*
* Tracing for API calls that drivers call.
*/

View file

@ -1981,7 +1981,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
* EAPOL frames from the local station.
*/
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
!is_multicast_ether_addr(hdr.addr1) && !authorized &&
!multicast && !authorized &&
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
@ -2357,15 +2357,31 @@ static void ieee80211_update_csa(struct ieee80211_sub_if_data *sdata,
struct probe_resp *resp;
int counter_offset_beacon = sdata->csa_counter_offset_beacon;
int counter_offset_presp = sdata->csa_counter_offset_presp;
u8 *beacon_data;
size_t beacon_data_len;
/* warn if the driver did not check for/react to csa completeness */
if (WARN_ON(((u8 *)beacon->tail)[counter_offset_beacon] == 0))
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
beacon_data = beacon->tail;
beacon_data_len = beacon->tail_len;
break;
case NL80211_IFTYPE_ADHOC:
beacon_data = beacon->head;
beacon_data_len = beacon->head_len;
break;
default:
return;
}
if (WARN_ON(counter_offset_beacon >= beacon_data_len))
return;
((u8 *)beacon->tail)[counter_offset_beacon]--;
/* warn if the driver did not check for/react to csa completeness */
if (WARN_ON(beacon_data[counter_offset_beacon] == 0))
return;
if (sdata->vif.type == NL80211_IFTYPE_AP &&
counter_offset_presp) {
beacon_data[counter_offset_beacon]--;
if (sdata->vif.type == NL80211_IFTYPE_AP && counter_offset_presp) {
rcu_read_lock();
resp = rcu_dereference(sdata->u.ap.probe_resp);
@ -2400,6 +2416,15 @@ bool ieee80211_csa_is_complete(struct ieee80211_vif *vif)
goto out;
beacon_data = beacon->tail;
beacon_data_len = beacon->tail_len;
} else if (vif->type == NL80211_IFTYPE_ADHOC) {
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
beacon = rcu_dereference(ifibss->presp);
if (!beacon)
goto out;
beacon_data = beacon->head;
beacon_data_len = beacon->head_len;
} else {
WARN_ON(1);
goto out;
@ -2484,6 +2509,10 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
if (!presp)
goto out;
if (sdata->vif.csa_active)
ieee80211_update_csa(sdata, presp);
skb = dev_alloc_skb(local->tx_headroom + presp->head_len);
if (!skb)
goto out;

View file

@ -567,58 +567,14 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
IEEE80211_QUEUE_STOP_REASON_FLUSH);
}
void ieee80211_iterate_active_interfaces(
struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
static void __iterate_active_interfaces(struct ieee80211_local *local,
u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR:
if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
continue;
break;
case NL80211_IFTYPE_AP_VLAN:
continue;
default:
break;
}
if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) &&
!(sdata->flags & IEEE80211_SDATA_IN_DRIVER))
continue;
if (ieee80211_sdata_running(sdata))
iterator(data, sdata->vif.addr,
&sdata->vif);
}
sdata = rcu_dereference_protected(local->monitor_sdata,
lockdep_is_held(&local->iflist_mtx));
if (sdata &&
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif);
mutex_unlock(&local->iflist_mtx);
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
void ieee80211_iterate_active_interfaces_atomic(
struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
switch (sdata->vif.type) {
case NL80211_IFTYPE_MONITOR:
@ -638,16 +594,57 @@ void ieee80211_iterate_active_interfaces_atomic(
&sdata->vif);
}
sdata = rcu_dereference(local->monitor_sdata);
sdata = rcu_dereference_check(local->monitor_sdata,
lockdep_is_held(&local->iflist_mtx) ||
lockdep_rtnl_is_held());
if (sdata &&
(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL ||
sdata->flags & IEEE80211_SDATA_IN_DRIVER))
iterator(data, sdata->vif.addr, &sdata->vif);
}
void ieee80211_iterate_active_interfaces(
struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
{
struct ieee80211_local *local = hw_to_local(hw);
mutex_lock(&local->iflist_mtx);
__iterate_active_interfaces(local, iter_flags, iterator, data);
mutex_unlock(&local->iflist_mtx);
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces);
void ieee80211_iterate_active_interfaces_atomic(
struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
{
struct ieee80211_local *local = hw_to_local(hw);
rcu_read_lock();
__iterate_active_interfaces(local, iter_flags, iterator, data);
rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
void ieee80211_iterate_active_interfaces_rtnl(
struct ieee80211_hw *hw, u32 iter_flags,
void (*iterator)(void *data, u8 *mac,
struct ieee80211_vif *vif),
void *data)
{
struct ieee80211_local *local = hw_to_local(hw);
ASSERT_RTNL();
__iterate_active_interfaces(local, iter_flags, iterator, data);
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
/*
* Nothing should have been stuffed into the workqueue during
* the suspend->resume cycle. If this WARN is seen then there
@ -1007,14 +1004,21 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
*/
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
/* Set defaults according to 802.11-2007 Table 7-37 */
aCWmax = 1023;
if (use_11b)
aCWmin = 31;
else
aCWmin = 15;
/* Set defaults according to 802.11-2007 Table 7-37 */
aCWmax = 1023;
if (use_11b)
aCWmin = 31;
else
aCWmin = 15;
/* Confiure old 802.11b/g medium access rules. */
qparam.cw_max = aCWmax;
qparam.cw_min = aCWmin;
qparam.txop = 0;
qparam.aifs = 2;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
/* Update if QoS is enabled. */
if (enable_qos) {
switch (ac) {
case IEEE80211_AC_BK:
@ -1050,12 +1054,6 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
qparam.aifs = 2;
break;
}
} else {
/* Confiure old 802.11b/g medium access rules. */
qparam.cw_max = aCWmax;
qparam.cw_min = aCWmin;
qparam.txop = 0;
qparam.aifs = 2;
}
qparam.uapsd = false;
@ -1084,8 +1082,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt;
int err;
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
sizeof(*mgmt) + 6 + extra_len);
/* 24 + 6 = header + auth_algo + auth_transaction + status_code */
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len);
if (!skb)
return;
@ -2292,3 +2290,63 @@ void ieee80211_radar_detected(struct ieee80211_hw *hw)
ieee80211_queue_work(hw, &local->radar_detected_work);
}
EXPORT_SYMBOL(ieee80211_radar_detected);
u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c)
{
u32 ret;
int tmp;
switch (c->width) {
case NL80211_CHAN_WIDTH_20:
c->width = NL80211_CHAN_WIDTH_20_NOHT;
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_40:
c->width = NL80211_CHAN_WIDTH_20;
c->center_freq1 = c->chan->center_freq;
ret = IEEE80211_STA_DISABLE_40MHZ |
IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_80:
tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
/* n_P40 */
tmp /= 2;
/* freq_P40 */
c->center_freq1 = c->center_freq1 - 20 + 40 * tmp;
c->width = NL80211_CHAN_WIDTH_40;
ret = IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_80P80:
c->center_freq2 = 0;
c->width = NL80211_CHAN_WIDTH_80;
ret = IEEE80211_STA_DISABLE_80P80MHZ |
IEEE80211_STA_DISABLE_160MHZ;
break;
case NL80211_CHAN_WIDTH_160:
/* n_P20 */
tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
/* n_P80 */
tmp /= 4;
c->center_freq1 = c->center_freq1 - 40 + 80 * tmp;
c->width = NL80211_CHAN_WIDTH_80;
ret = IEEE80211_STA_DISABLE_80P80MHZ |
IEEE80211_STA_DISABLE_160MHZ;
break;
default:
case NL80211_CHAN_WIDTH_20_NOHT:
WARN_ON_ONCE(1);
c->width = NL80211_CHAN_WIDTH_20_NOHT;
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
break;
case NL80211_CHAN_WIDTH_5:
case NL80211_CHAN_WIDTH_10:
WARN_ON_ONCE(1);
/* keep c->width */
ret = IEEE80211_STA_DISABLE_HT | IEEE80211_STA_DISABLE_VHT;
break;
}
WARN_ON_ONCE(!cfg80211_chandef_valid(c));
return ret;
}

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

View file

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

View file

@ -382,15 +382,6 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
enum cfg80211_chan_mode chanmode,
u8 radar_detect);
/**
* cfg80211_chandef_dfs_required - checks if radar detection is required
* @wiphy: the wiphy to validate against
* @chandef: the channel definition to check
* Return: 1 if radar detection is required, 0 if it is not, < 0 on error
*/
int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
const struct cfg80211_chan_def *c);
void cfg80211_set_dfs_state(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
enum nl80211_dfs_state dfs_state);

View file

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

View file

@ -46,6 +46,12 @@ BEGIN {
sub(/:/, "", country)
printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
printf "\t.alpha2 = \"%s\",\n", country
if ($NF ~ /DFS-ETSI/)
printf "\t.dfs_region = NL80211_DFS_ETSI,\n"
else if ($NF ~ /DFS-FCC/)
printf "\t.dfs_region = NL80211_DFS_FCC,\n"
else if ($NF ~ /DFS-JP/)
printf "\t.dfs_region = NL80211_DFS_JP,\n"
printf "\t.reg_rules = {\n"
active = 1
regdb = regdb "\t&regdom_" country ",\n"

View file

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

View file

@ -172,11 +172,21 @@ static const struct ieee80211_regdomain world_regdom = {
NL80211_RRF_NO_IBSS |
NL80211_RRF_NO_OFDM),
/* IEEE 802.11a, channel 36..48 */
REG_RULE(5180-10, 5240+10, 80, 6, 20,
REG_RULE(5180-10, 5240+10, 160, 6, 20,
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS),
/* NB: 5260 MHz - 5700 MHz requires DFS */
/* IEEE 802.11a, channel 52..64 - DFS required */
REG_RULE(5260-10, 5320+10, 160, 6, 20,
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS |
NL80211_RRF_DFS),
/* IEEE 802.11a, channel 100..144 - DFS required */
REG_RULE(5500-10, 5720+10, 160, 6, 20,
NL80211_RRF_PASSIVE_SCAN |
NL80211_RRF_NO_IBSS |
NL80211_RRF_DFS),
/* IEEE 802.11a, channel 149..165 */
REG_RULE(5745-10, 5825+10, 80, 6, 20,

View file

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