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

Conflicts:
	drivers/net/wireless/wl12xx/wl1271_cmd.h
This commit is contained in:
John W. Linville 2010-07-13 15:57:29 -04:00
commit e300d955de
106 changed files with 9726 additions and 8521 deletions

View File

@ -313,11 +313,9 @@ S: Maintained
F: drivers/hwmon/adm1029.c F: drivers/hwmon/adm1029.c
ADM8211 WIRELESS DRIVER ADM8211 WIRELESS DRIVER
M: Michael Wu <flamingice@sourmilk.net>
L: linux-wireless@vger.kernel.org L: linux-wireless@vger.kernel.org
W: http://linuxwireless.org/ W: http://linuxwireless.org/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mwu/mac80211-drivers.git S: Orphan
S: Maintained
F: drivers/net/wireless/adm8211.* F: drivers/net/wireless/adm8211.*
ADT746X FAN DRIVER ADT746X FAN DRIVER
@ -4251,10 +4249,9 @@ F: include/scsi/osd_*
F: fs/exofs/ F: fs/exofs/
P54 WIRELESS DRIVER P54 WIRELESS DRIVER
M: Michael Wu <flamingice@sourmilk.net> M: Christian Lamparter <chunkeey@googlemail.com>
L: linux-wireless@vger.kernel.org L: linux-wireless@vger.kernel.org
W: http://prism54.org W: http://wireless.kernel.org/en/users/Drivers/p54
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mwu/mac80211-drivers.git
S: Maintained S: Maintained
F: drivers/net/wireless/p54/ F: drivers/net/wireless/p54/

View File

@ -1903,7 +1903,7 @@ static int __devinit adm8211_probe(struct pci_dev *pdev,
if (err) { if (err) {
printk(KERN_ERR "%s (adm8211): Cannot register device\n", printk(KERN_ERR "%s (adm8211): Cannot register device\n",
pci_name(pdev)); pci_name(pdev));
goto err_free_desc; goto err_free_eeprom;
} }
printk(KERN_INFO "%s: hwaddr %pM, Rev 0x%02x\n", printk(KERN_INFO "%s: hwaddr %pM, Rev 0x%02x\n",
@ -1912,6 +1912,9 @@ static int __devinit adm8211_probe(struct pci_dev *pdev,
return 0; return 0;
err_free_eeprom:
kfree(priv->eeprom);
err_free_desc: err_free_desc:
pci_free_consistent(pdev, pci_free_consistent(pdev,
sizeof(struct adm8211_desc) * priv->rx_ring_size + sizeof(struct adm8211_desc) * priv->rx_ring_size +

File diff suppressed because it is too large Load Diff

View File

@ -1495,121 +1495,25 @@ static bool ar5008_hw_ani_control_new(struct ath_hw *ah,
static void ar5008_hw_do_getnf(struct ath_hw *ah, static void ar5008_hw_do_getnf(struct ath_hw *ah,
int16_t nfarray[NUM_NF_READINGS]) int16_t nfarray[NUM_NF_READINGS])
{ {
struct ath_common *common = ath9k_hw_common(ah);
int16_t nf; int16_t nf;
nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_CCA), AR_PHY_MINCCA_PWR);
if (nf & 0x100) nfarray[0] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ctl] [chain 0] is %d\n", nf);
nfarray[0] = nf;
nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR_PHY_CH1_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR_PHY_CH1_MINCCA_PWR);
if (nf & 0x100) nfarray[1] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ctl] [chain 1] is %d\n", nf);
nfarray[1] = nf;
nf = MS(REG_READ(ah, AR_PHY_CH2_CCA), AR_PHY_CH2_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_CH2_CCA), AR_PHY_CH2_MINCCA_PWR);
if (nf & 0x100) nfarray[2] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ctl] [chain 2] is %d\n", nf);
nfarray[2] = nf;
nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR_PHY_EXT_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR_PHY_EXT_MINCCA_PWR);
if (nf & 0x100) nfarray[3] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ext] [chain 0] is %d\n", nf);
nfarray[3] = nf;
nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), AR_PHY_CH1_EXT_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), AR_PHY_CH1_EXT_MINCCA_PWR);
if (nf & 0x100) nfarray[4] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ext] [chain 1] is %d\n", nf);
nfarray[4] = nf;
nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA), AR_PHY_CH2_EXT_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_CH2_EXT_CCA), AR_PHY_CH2_EXT_MINCCA_PWR);
if (nf & 0x100) nfarray[5] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ext] [chain 2] is %d\n", nf);
nfarray[5] = nf;
}
static void ar5008_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
{
struct ath9k_nfcal_hist *h;
int i, j;
int32_t val;
const u32 ar5416_cca_regs[6] = {
AR_PHY_CCA,
AR_PHY_CH1_CCA,
AR_PHY_CH2_CCA,
AR_PHY_EXT_CCA,
AR_PHY_CH1_EXT_CCA,
AR_PHY_CH2_EXT_CCA
};
u8 chainmask, rx_chain_status;
rx_chain_status = REG_READ(ah, AR_PHY_RX_CHAINMASK);
if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
chainmask = 0x9;
else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) {
if ((rx_chain_status & 0x2) || (rx_chain_status & 0x4))
chainmask = 0x1B;
else
chainmask = 0x09;
} else {
if (rx_chain_status & 0x4)
chainmask = 0x3F;
else if (rx_chain_status & 0x2)
chainmask = 0x1B;
else
chainmask = 0x09;
}
h = ah->nfCalHist;
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
val = REG_READ(ah, ar5416_cca_regs[i]);
val &= 0xFFFFFE00;
val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
REG_WRITE(ah, ar5416_cca_regs[i], val);
}
}
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_ENABLE_NF);
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
for (j = 0; j < 5; j++) {
if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
AR_PHY_AGC_CONTROL_NF) == 0)
break;
udelay(50);
}
ENABLE_REGWRITE_BUFFER(ah);
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
val = REG_READ(ah, ar5416_cca_regs[i]);
val &= 0xFFFFFE00;
val |= (((u32) (-50) << 1) & 0x1ff);
REG_WRITE(ah, ar5416_cca_regs[i], val);
}
}
REGWRITE_BUFFER_FLUSH(ah);
DISABLE_REGWRITE_BUFFER(ah);
} }
/* /*
@ -1676,10 +1580,27 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah)
aniState->cycleCount = 0; aniState->cycleCount = 0;
} }
static void ar5008_hw_set_nf_limits(struct ath_hw *ah)
{
ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_5416_2GHZ;
ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_5416_2GHZ;
ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_5416_2GHZ;
ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_5416_5GHZ;
ah->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_5416_5GHZ;
ah->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_5416_5GHZ;
}
void ar5008_hw_attach_phy_ops(struct ath_hw *ah) void ar5008_hw_attach_phy_ops(struct ath_hw *ah)
{ {
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
const u32 ar5416_cca_regs[6] = {
AR_PHY_CCA,
AR_PHY_CH1_CCA,
AR_PHY_CH2_CCA,
AR_PHY_EXT_CCA,
AR_PHY_CH1_EXT_CCA,
AR_PHY_CH2_EXT_CCA
};
priv_ops->rf_set_freq = ar5008_hw_set_channel; priv_ops->rf_set_freq = ar5008_hw_set_channel;
priv_ops->spur_mitigate_freq = ar5008_hw_spur_mitigate; priv_ops->spur_mitigate_freq = ar5008_hw_spur_mitigate;
@ -1699,7 +1620,6 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah)
priv_ops->restore_chainmask = ar5008_restore_chainmask; priv_ops->restore_chainmask = ar5008_restore_chainmask;
priv_ops->set_diversity = ar5008_set_diversity; priv_ops->set_diversity = ar5008_set_diversity;
priv_ops->do_getnf = ar5008_hw_do_getnf; priv_ops->do_getnf = ar5008_hw_do_getnf;
priv_ops->loadnf = ar5008_hw_loadnf;
if (modparam_force_new_ani) { if (modparam_force_new_ani) {
priv_ops->ani_control = ar5008_hw_ani_control_new; priv_ops->ani_control = ar5008_hw_ani_control_new;
@ -1713,4 +1633,7 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah)
priv_ops->compute_pll_control = ar9160_hw_compute_pll_control; priv_ops->compute_pll_control = ar9160_hw_compute_pll_control;
else else
priv_ops->compute_pll_control = ar5008_hw_compute_pll_control; priv_ops->compute_pll_control = ar5008_hw_compute_pll_control;
ar5008_hw_set_nf_limits(ah);
memcpy(ah->nf_regs, ar5416_cca_regs, sizeof(ah->nf_regs));
} }

File diff suppressed because it is too large Load Diff

View File

@ -239,7 +239,7 @@ static void ar9002_hw_iqcalibrate(struct ath_hw *ah, u8 numChains)
if (qCoff > 15) if (qCoff > 15)
qCoff = 15; qCoff = 15;
else if (qCoff <= -16) else if (qCoff <= -16)
qCoff = 16; qCoff = -16;
ath_print(common, ATH_DBG_CALIBRATE, ath_print(common, ATH_DBG_CALIBRATE,
"Chn %d : iCoff = 0x%x qCoff = 0x%x\n", "Chn %d : iCoff = 0x%x qCoff = 0x%x\n",

View File

@ -179,8 +179,8 @@ static void ar9002_hw_init_mode_regs(struct ath_hw *ah)
ARRAY_SIZE(ar5416Bank7_9160), 2); ARRAY_SIZE(ar5416Bank7_9160), 2);
if (AR_SREV_9160_11(ah)) { if (AR_SREV_9160_11(ah)) {
INIT_INI_ARRAY(&ah->iniAddac, INIT_INI_ARRAY(&ah->iniAddac,
ar5416Addac_91601_1, ar5416Addac_9160_1_1,
ARRAY_SIZE(ar5416Addac_91601_1), 2); ARRAY_SIZE(ar5416Addac_9160_1_1), 2);
} else { } else {
INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac_9160, INIT_INI_ARRAY(&ah->iniAddac, ar5416Addac_9160,
ARRAY_SIZE(ar5416Addac_9160), 2); ARRAY_SIZE(ar5416Addac_9160), 2);
@ -239,12 +239,12 @@ void ar9002_hw_cck_chan14_spread(struct ath_hw *ah)
{ {
if (AR_SREV_9287_11_OR_LATER(ah)) { if (AR_SREV_9287_11_OR_LATER(ah)) {
INIT_INI_ARRAY(&ah->iniCckfirNormal, INIT_INI_ARRAY(&ah->iniCckfirNormal,
ar9287Common_normal_cck_fir_coeff_92871_1, ar9287Common_normal_cck_fir_coeff_9287_1_1,
ARRAY_SIZE(ar9287Common_normal_cck_fir_coeff_92871_1), ARRAY_SIZE(ar9287Common_normal_cck_fir_coeff_9287_1_1),
2); 2);
INIT_INI_ARRAY(&ah->iniCckfirJapan2484, INIT_INI_ARRAY(&ah->iniCckfirJapan2484,
ar9287Common_japan_2484_cck_fir_coeff_92871_1, ar9287Common_japan_2484_cck_fir_coeff_9287_1_1,
ARRAY_SIZE(ar9287Common_japan_2484_cck_fir_coeff_92871_1), ARRAY_SIZE(ar9287Common_japan_2484_cck_fir_coeff_9287_1_1),
2); 2);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -287,6 +287,7 @@ static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds,
ts->ts_shortretry = MS(ads->ds_txstatus1, AR_RTSFailCnt); ts->ts_shortretry = MS(ads->ds_txstatus1, AR_RTSFailCnt);
ts->ts_longretry = MS(ads->ds_txstatus1, AR_DataFailCnt); ts->ts_longretry = MS(ads->ds_txstatus1, AR_DataFailCnt);
ts->ts_virtcol = MS(ads->ds_txstatus1, AR_VirtRetryCnt); ts->ts_virtcol = MS(ads->ds_txstatus1, AR_VirtRetryCnt);
ts->tid = MS(ads->ds_txstatus9, AR_TxTid);
ts->ts_antenna = 0; ts->ts_antenna = 0;
return 0; return 0;

View File

@ -471,52 +471,45 @@ static u32 ar9002_hw_compute_pll_control(struct ath_hw *ah,
static void ar9002_hw_do_getnf(struct ath_hw *ah, static void ar9002_hw_do_getnf(struct ath_hw *ah,
int16_t nfarray[NUM_NF_READINGS]) int16_t nfarray[NUM_NF_READINGS])
{ {
struct ath_common *common = ath9k_hw_common(ah);
int16_t nf; int16_t nf;
nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR);
nfarray[0] = sign_extend(nf, 9);
if (nf & 0x100)
nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ctl] [chain 0] is %d\n", nf);
if (AR_SREV_9271(ah) && (nf >= -114))
nf = -116;
nfarray[0] = nf;
if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) {
nf = MS(REG_READ(ah, AR_PHY_CH1_CCA),
AR9280_PHY_CH1_MINCCA_PWR);
if (nf & 0x100)
nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ctl] [chain 1] is %d\n", nf);
nfarray[1] = nf;
}
nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR9280_PHY_EXT_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR9280_PHY_EXT_MINCCA_PWR);
if (nf & 0x100) nfarray[3] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ext] [chain 0] is %d\n", nf);
if (AR_SREV_9271(ah) && (nf >= -114)) if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
nf = -116; return;
nfarray[3] = nf; nf = MS(REG_READ(ah, AR_PHY_CH1_CCA), AR9280_PHY_CH1_MINCCA_PWR);
nfarray[1] = sign_extend(nf, 9);
if (!AR_SREV_9285(ah) && !AR_SREV_9271(ah)) { nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), AR9280_PHY_CH1_EXT_MINCCA_PWR);
nf = MS(REG_READ(ah, AR_PHY_CH1_EXT_CCA), nfarray[4] = sign_extend(nf, 9);
AR9280_PHY_CH1_EXT_MINCCA_PWR); }
if (nf & 0x100) static void ar9002_hw_set_nf_limits(struct ath_hw *ah)
nf = 0 - ((nf ^ 0x1ff) + 1); {
ath_print(common, ATH_DBG_CALIBRATE, if (AR_SREV_9285(ah)) {
"NF calibrated [ext] [chain 1] is %d\n", nf); ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9285_2GHZ;
nfarray[4] = nf; ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9285_2GHZ;
ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9285_2GHZ;
} else if (AR_SREV_9287(ah)) {
ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9287_2GHZ;
ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9287_2GHZ;
ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9287_2GHZ;
} else if (AR_SREV_9271(ah)) {
ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9271_2GHZ;
ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9271_2GHZ;
ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9271_2GHZ;
} else {
ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9280_2GHZ;
ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9280_2GHZ;
ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9280_2GHZ;
ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9280_5GHZ;
ah->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9280_5GHZ;
ah->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9280_5GHZ;
} }
} }
@ -532,4 +525,6 @@ void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
priv_ops->olc_init = ar9002_olc_init; priv_ops->olc_init = ar9002_olc_init;
priv_ops->compute_pll_control = ar9002_hw_compute_pll_control; priv_ops->compute_pll_control = ar9002_hw_compute_pll_control;
priv_ops->do_getnf = ar9002_hw_do_getnf; priv_ops->do_getnf = ar9002_hw_do_getnf;
ar9002_hw_set_nf_limits(ah);
} }

View File

@ -576,4 +576,30 @@
#define AR_PHY_CH2_EXT_MINCCA_PWR 0xFF800000 #define AR_PHY_CH2_EXT_MINCCA_PWR 0xFF800000
#define AR_PHY_CH2_EXT_MINCCA_PWR_S 23 #define AR_PHY_CH2_EXT_MINCCA_PWR_S 23
#define AR_PHY_CCA_NOM_VAL_5416_2GHZ -90
#define AR_PHY_CCA_NOM_VAL_5416_5GHZ -100
#define AR_PHY_CCA_MIN_GOOD_VAL_5416_2GHZ -100
#define AR_PHY_CCA_MIN_GOOD_VAL_5416_5GHZ -110
#define AR_PHY_CCA_MAX_GOOD_VAL_5416_2GHZ -80
#define AR_PHY_CCA_MAX_GOOD_VAL_5416_5GHZ -90
#define AR_PHY_CCA_NOM_VAL_9280_2GHZ -112
#define AR_PHY_CCA_NOM_VAL_9280_5GHZ -112
#define AR_PHY_CCA_MIN_GOOD_VAL_9280_2GHZ -127
#define AR_PHY_CCA_MIN_GOOD_VAL_9280_5GHZ -122
#define AR_PHY_CCA_MAX_GOOD_VAL_9280_2GHZ -97
#define AR_PHY_CCA_MAX_GOOD_VAL_9280_5GHZ -102
#define AR_PHY_CCA_NOM_VAL_9285_2GHZ -118
#define AR_PHY_CCA_MIN_GOOD_VAL_9285_2GHZ -127
#define AR_PHY_CCA_MAX_GOOD_VAL_9285_2GHZ -108
#define AR_PHY_CCA_NOM_VAL_9271_2GHZ -118
#define AR_PHY_CCA_MIN_GOOD_VAL_9271_2GHZ -127
#define AR_PHY_CCA_MAX_GOOD_VAL_9271_2GHZ -116
#define AR_PHY_CCA_NOM_VAL_9287_2GHZ -120
#define AR_PHY_CCA_MIN_GOOD_VAL_9287_2GHZ -127
#define AR_PHY_CCA_MAX_GOOD_VAL_9287_2GHZ -110
#endif #endif

View File

@ -951,7 +951,7 @@ static u8 ath9k_hw_ar9300_get_num_ant_config(struct ath_hw *ah,
return 1; return 1;
} }
static u16 ath9k_hw_ar9300_get_eeprom_antenna_cfg(struct ath_hw *ah, static u32 ath9k_hw_ar9300_get_eeprom_antenna_cfg(struct ath_hw *ah,
struct ath9k_channel *chan) struct ath9k_channel *chan)
{ {
return -EINVAL; return -EINVAL;

View File

@ -33,9 +33,6 @@
#define AR_TxDescId_S 16 #define AR_TxDescId_S 16
#define AR_TxPtrChkSum 0x0000ffff #define AR_TxPtrChkSum 0x0000ffff
#define AR_TxTid 0xf0000000
#define AR_TxTid_S 28
#define AR_LowRxChain 0x00004000 #define AR_LowRxChain 0x00004000
#define AR_Not_Sounding 0x20000000 #define AR_Not_Sounding 0x20000000

View File

@ -1015,213 +1015,38 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
return true; return true;
} }
static void ar9003_hw_nf_sanitize_2g(struct ath_hw *ah, s16 *nf)
{
struct ath_common *common = ath9k_hw_common(ah);
if (*nf > ah->nf_2g_max) {
ath_print(common, ATH_DBG_CALIBRATE,
"2 GHz NF (%d) > MAX (%d), "
"correcting to MAX",
*nf, ah->nf_2g_max);
*nf = ah->nf_2g_max;
} else if (*nf < ah->nf_2g_min) {
ath_print(common, ATH_DBG_CALIBRATE,
"2 GHz NF (%d) < MIN (%d), "
"correcting to MIN",
*nf, ah->nf_2g_min);
*nf = ah->nf_2g_min;
}
}
static void ar9003_hw_nf_sanitize_5g(struct ath_hw *ah, s16 *nf)
{
struct ath_common *common = ath9k_hw_common(ah);
if (*nf > ah->nf_5g_max) {
ath_print(common, ATH_DBG_CALIBRATE,
"5 GHz NF (%d) > MAX (%d), "
"correcting to MAX",
*nf, ah->nf_5g_max);
*nf = ah->nf_5g_max;
} else if (*nf < ah->nf_5g_min) {
ath_print(common, ATH_DBG_CALIBRATE,
"5 GHz NF (%d) < MIN (%d), "
"correcting to MIN",
*nf, ah->nf_5g_min);
*nf = ah->nf_5g_min;
}
}
static void ar9003_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
{
if (IS_CHAN_2GHZ(ah->curchan))
ar9003_hw_nf_sanitize_2g(ah, nf);
else
ar9003_hw_nf_sanitize_5g(ah, nf);
}
static void ar9003_hw_do_getnf(struct ath_hw *ah, static void ar9003_hw_do_getnf(struct ath_hw *ah,
int16_t nfarray[NUM_NF_READINGS]) int16_t nfarray[NUM_NF_READINGS])
{ {
struct ath_common *common = ath9k_hw_common(ah);
int16_t nf; int16_t nf;
nf = MS(REG_READ(ah, AR_PHY_CCA_0), AR_PHY_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_CCA_0), AR_PHY_MINCCA_PWR);
if (nf & 0x100) nfarray[0] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ar9003_hw_nf_sanitize(ah, &nf);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ctl] [chain 0] is %d\n", nf);
nfarray[0] = nf;
nf = MS(REG_READ(ah, AR_PHY_CCA_1), AR_PHY_CH1_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_CCA_1), AR_PHY_CH1_MINCCA_PWR);
if (nf & 0x100) nfarray[1] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ar9003_hw_nf_sanitize(ah, &nf);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ctl] [chain 1] is %d\n", nf);
nfarray[1] = nf;
nf = MS(REG_READ(ah, AR_PHY_CCA_2), AR_PHY_CH2_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_CCA_2), AR_PHY_CH2_MINCCA_PWR);
if (nf & 0x100) nfarray[2] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ar9003_hw_nf_sanitize(ah, &nf);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ctl] [chain 2] is %d\n", nf);
nfarray[2] = nf;
nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR_PHY_EXT_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_EXT_CCA), AR_PHY_EXT_MINCCA_PWR);
if (nf & 0x100) nfarray[3] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ar9003_hw_nf_sanitize(ah, &nf);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ext] [chain 0] is %d\n", nf);
nfarray[3] = nf;
nf = MS(REG_READ(ah, AR_PHY_EXT_CCA_1), AR_PHY_CH1_EXT_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_EXT_CCA_1), AR_PHY_CH1_EXT_MINCCA_PWR);
if (nf & 0x100) nfarray[4] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ar9003_hw_nf_sanitize(ah, &nf);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ext] [chain 1] is %d\n", nf);
nfarray[4] = nf;
nf = MS(REG_READ(ah, AR_PHY_EXT_CCA_2), AR_PHY_CH2_EXT_MINCCA_PWR); nf = MS(REG_READ(ah, AR_PHY_EXT_CCA_2), AR_PHY_CH2_EXT_MINCCA_PWR);
if (nf & 0x100) nfarray[5] = sign_extend(nf, 9);
nf = 0 - ((nf ^ 0x1ff) + 1);
ar9003_hw_nf_sanitize(ah, &nf);
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [ext] [chain 2] is %d\n", nf);
nfarray[5] = nf;
} }
void ar9003_hw_set_nf_limits(struct ath_hw *ah) static void ar9003_hw_set_nf_limits(struct ath_hw *ah)
{ {
ah->nf_2g_max = AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ; ah->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9300_2GHZ;
ah->nf_2g_min = AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ; ah->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9300_2GHZ;
ah->nf_5g_max = AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ; ah->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9300_2GHZ;
ah->nf_5g_min = AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ; ah->nf_5g.max = AR_PHY_CCA_MAX_GOOD_VAL_9300_5GHZ;
} ah->nf_5g.min = AR_PHY_CCA_MIN_GOOD_VAL_9300_5GHZ;
ah->nf_5g.nominal = AR_PHY_CCA_NOM_VAL_9300_5GHZ;
/*
* Find out which of the RX chains are enabled
*/
static u32 ar9003_hw_get_rx_chainmask(struct ath_hw *ah)
{
u32 chain = REG_READ(ah, AR_PHY_RX_CHAINMASK);
/*
* The bits [2:0] indicate the rx chain mask and are to be
* interpreted as follows:
* 00x => Only chain 0 is enabled
* 01x => Chain 1 and 0 enabled
* 1xx => Chain 2,1 and 0 enabled
*/
return chain & 0x7;
}
static void ar9003_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
{
struct ath9k_nfcal_hist *h;
unsigned i, j;
int32_t val;
const u32 ar9300_cca_regs[6] = {
AR_PHY_CCA_0,
AR_PHY_CCA_1,
AR_PHY_CCA_2,
AR_PHY_EXT_CCA,
AR_PHY_EXT_CCA_1,
AR_PHY_EXT_CCA_2,
};
u8 chainmask, rx_chain_status;
struct ath_common *common = ath9k_hw_common(ah);
rx_chain_status = ar9003_hw_get_rx_chainmask(ah);
chainmask = 0x3F;
h = ah->nfCalHist;
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
val = REG_READ(ah, ar9300_cca_regs[i]);
val &= 0xFFFFFE00;
val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
REG_WRITE(ah, ar9300_cca_regs[i], val);
}
}
/*
* Load software filtered NF value into baseband internal minCCApwr
* variable.
*/
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_ENABLE_NF);
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
/*
* Wait for load to complete, should be fast, a few 10s of us.
* The max delay was changed from an original 250us to 10000us
* since 250us often results in NF load timeout and causes deaf
* condition during stress testing 12/12/2009
*/
for (j = 0; j < 1000; j++) {
if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
AR_PHY_AGC_CONTROL_NF) == 0)
break;
udelay(10);
}
/*
* We timed out waiting for the noisefloor to load, probably due to an
* in-progress rx. Simply return here and allow the load plenty of time
* to complete before the next calibration interval. We need to avoid
* trying to load -50 (which happens below) while the previous load is
* still in progress as this can cause rx deafness. Instead by returning
* here, the baseband nf cal will just be capped by our present
* noisefloor until the next calibration timer.
*/
if (j == 1000) {
ath_print(common, ATH_DBG_ANY, "Timeout while waiting for nf "
"to load: AR_PHY_AGC_CONTROL=0x%x\n",
REG_READ(ah, AR_PHY_AGC_CONTROL));
return;
}
/*
* Restore maxCCAPower register parameter again so that we're not capped
* by the median we just loaded. This will be initial (and max) value
* of next noise floor calibration the baseband does.
*/
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
val = REG_READ(ah, ar9300_cca_regs[i]);
val &= 0xFFFFFE00;
val |= (((u32) (-50) << 1) & 0x1ff);
REG_WRITE(ah, ar9300_cca_regs[i], val);
}
}
} }
/* /*
@ -1291,6 +1116,14 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah)
void ar9003_hw_attach_phy_ops(struct ath_hw *ah) void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
{ {
struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
const u32 ar9300_cca_regs[6] = {
AR_PHY_CCA_0,
AR_PHY_CCA_1,
AR_PHY_CCA_2,
AR_PHY_EXT_CCA,
AR_PHY_EXT_CCA_1,
AR_PHY_EXT_CCA_2,
};
priv_ops->rf_set_freq = ar9003_hw_set_channel; priv_ops->rf_set_freq = ar9003_hw_set_channel;
priv_ops->spur_mitigate_freq = ar9003_hw_spur_mitigate; priv_ops->spur_mitigate_freq = ar9003_hw_spur_mitigate;
@ -1307,8 +1140,10 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah)
priv_ops->set_diversity = ar9003_hw_set_diversity; priv_ops->set_diversity = ar9003_hw_set_diversity;
priv_ops->ani_control = ar9003_hw_ani_control; priv_ops->ani_control = ar9003_hw_ani_control;
priv_ops->do_getnf = ar9003_hw_do_getnf; priv_ops->do_getnf = ar9003_hw_do_getnf;
priv_ops->loadnf = ar9003_hw_loadnf;
priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs; priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs;
ar9003_hw_set_nf_limits(ah);
memcpy(ah->nf_regs, ar9300_cca_regs, sizeof(ah->nf_regs));
} }
void ar9003_hw_bb_watchdog_config(struct ath_hw *ah) void ar9003_hw_bb_watchdog_config(struct ath_hw *ah)

View File

@ -428,6 +428,7 @@ int ath_beaconq_config(struct ath_softc *sc);
#define ATH_PAPRD_TIMEOUT 100 /* msecs */ #define ATH_PAPRD_TIMEOUT 100 /* msecs */
void ath_hw_check(struct work_struct *work);
void ath_paprd_calibrate(struct work_struct *work); void ath_paprd_calibrate(struct work_struct *work);
void ath_ani_calibrate(unsigned long data); void ath_ani_calibrate(unsigned long data);
@ -562,6 +563,7 @@ struct ath_softc {
spinlock_t sc_pm_lock; spinlock_t sc_pm_lock;
struct mutex mutex; struct mutex mutex;
struct work_struct paprd_work; struct work_struct paprd_work;
struct work_struct hw_check_work;
struct completion paprd_complete; struct completion paprd_complete;
u32 intrstatus; u32 intrstatus;

View File

@ -74,13 +74,8 @@ static void ath9k_hw_update_nfcal_hist_buffer(struct ath9k_nfcal_hist *h,
h[i].currIndex = 0; h[i].currIndex = 0;
if (h[i].invalidNFcount > 0) { if (h[i].invalidNFcount > 0) {
if (nfarray[i] < AR_PHY_CCA_MIN_BAD_VALUE ||
nfarray[i] > AR_PHY_CCA_MAX_HIGH_VALUE) {
h[i].invalidNFcount = ATH9K_NF_CAL_HIST_MAX;
} else {
h[i].invalidNFcount--; h[i].invalidNFcount--;
h[i].privNF = nfarray[i]; h[i].privNF = nfarray[i];
}
} else { } else {
h[i].privNF = h[i].privNF =
ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer); ath9k_hw_get_nf_hist_mid(h[i].nfCalBuffer);
@ -172,6 +167,133 @@ void ath9k_hw_start_nfcal(struct ath_hw *ah)
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF); REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
} }
void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan)
{
struct ath9k_nfcal_hist *h;
unsigned i, j;
int32_t val;
u8 chainmask;
struct ath_common *common = ath9k_hw_common(ah);
if (AR_SREV_9300_20_OR_LATER(ah))
chainmask = 0x3F;
else if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
chainmask = 0x9;
else if (AR_SREV_9280(ah) || AR_SREV_9287(ah)) {
if ((ah->rxchainmask & 0x2) || (ah->rxchainmask & 0x4))
chainmask = 0x1B;
else
chainmask = 0x09;
} else {
if (ah->rxchainmask & 0x4)
chainmask = 0x3F;
else if (ah->rxchainmask & 0x2)
chainmask = 0x1B;
else
chainmask = 0x09;
}
h = ah->nfCalHist;
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
val = REG_READ(ah, ah->nf_regs[i]);
val &= 0xFFFFFE00;
val |= (((u32) (h[i].privNF) << 1) & 0x1ff);
REG_WRITE(ah, ah->nf_regs[i], val);
}
}
/*
* Load software filtered NF value into baseband internal minCCApwr
* variable.
*/
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_ENABLE_NF);
REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
/*
* Wait for load to complete, should be fast, a few 10s of us.
* The max delay was changed from an original 250us to 10000us
* since 250us often results in NF load timeout and causes deaf
* condition during stress testing 12/12/2009
*/
for (j = 0; j < 1000; j++) {
if ((REG_READ(ah, AR_PHY_AGC_CONTROL) &
AR_PHY_AGC_CONTROL_NF) == 0)
break;
udelay(10);
}
/*
* We timed out waiting for the noisefloor to load, probably due to an
* in-progress rx. Simply return here and allow the load plenty of time
* to complete before the next calibration interval. We need to avoid
* trying to load -50 (which happens below) while the previous load is
* still in progress as this can cause rx deafness. Instead by returning
* here, the baseband nf cal will just be capped by our present
* noisefloor until the next calibration timer.
*/
if (j == 1000) {
ath_print(common, ATH_DBG_ANY, "Timeout while waiting for nf "
"to load: AR_PHY_AGC_CONTROL=0x%x\n",
REG_READ(ah, AR_PHY_AGC_CONTROL));
return;
}
/*
* Restore maxCCAPower register parameter again so that we're not capped
* by the median we just loaded. This will be initial (and max) value
* of next noise floor calibration the baseband does.
*/
ENABLE_REGWRITE_BUFFER(ah);
for (i = 0; i < NUM_NF_READINGS; i++) {
if (chainmask & (1 << i)) {
val = REG_READ(ah, ah->nf_regs[i]);
val &= 0xFFFFFE00;
val |= (((u32) (-50) << 1) & 0x1ff);
REG_WRITE(ah, ah->nf_regs[i], val);
}
}
REGWRITE_BUFFER_FLUSH(ah);
DISABLE_REGWRITE_BUFFER(ah);
}
static void ath9k_hw_nf_sanitize(struct ath_hw *ah, s16 *nf)
{
struct ath_common *common = ath9k_hw_common(ah);
struct ath_nf_limits *limit;
int i;
if (IS_CHAN_2GHZ(ah->curchan))
limit = &ah->nf_2g;
else
limit = &ah->nf_5g;
for (i = 0; i < NUM_NF_READINGS; i++) {
if (!nf[i])
continue;
ath_print(common, ATH_DBG_CALIBRATE,
"NF calibrated [%s] [chain %d] is %d\n",
(i > 3 ? "ext" : "ctl"), i % 3, nf[i]);
if (nf[i] > limit->max) {
ath_print(common, ATH_DBG_CALIBRATE,
"NF[%d] (%d) > MAX (%d), correcting to MAX",
i, nf[i], limit->max);
nf[i] = limit->max;
} else if (nf[i] < limit->min) {
ath_print(common, ATH_DBG_CALIBRATE,
"NF[%d] (%d) < MIN (%d), correcting to NOM",
i, nf[i], limit->min);
nf[i] = limit->nominal;
}
}
}
int16_t ath9k_hw_getnf(struct ath_hw *ah, int16_t ath9k_hw_getnf(struct ath_hw *ah,
struct ath9k_channel *chan) struct ath9k_channel *chan)
{ {
@ -190,6 +312,7 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
return chan->rawNoiseFloor; return chan->rawNoiseFloor;
} else { } else {
ath9k_hw_do_getnf(ah, nfarray); ath9k_hw_do_getnf(ah, nfarray);
ath9k_hw_nf_sanitize(ah, nfarray);
nf = nfarray[0]; nf = nfarray[0];
if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh) if (ath9k_hw_get_nf_thresh(ah, c->band, &nfThresh)
&& nf > nfThresh) { && nf > nfThresh) {
@ -211,25 +334,21 @@ int16_t ath9k_hw_getnf(struct ath_hw *ah,
void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah) void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah)
{ {
struct ath_nf_limits *limit;
int i, j; int i, j;
s16 noise_floor;
if (AR_SREV_9280(ah)) if (!ah->curchan || IS_CHAN_2GHZ(ah->curchan))
noise_floor = AR_PHY_CCA_MAX_AR9280_GOOD_VALUE; limit = &ah->nf_2g;
else if (AR_SREV_9285(ah) || AR_SREV_9271(ah))
noise_floor = AR_PHY_CCA_MAX_AR9285_GOOD_VALUE;
else if (AR_SREV_9287(ah))
noise_floor = AR_PHY_CCA_MAX_AR9287_GOOD_VALUE;
else else
noise_floor = AR_PHY_CCA_MAX_AR5416_GOOD_VALUE; limit = &ah->nf_5g;
for (i = 0; i < NUM_NF_READINGS; i++) { for (i = 0; i < NUM_NF_READINGS; i++) {
ah->nfCalHist[i].currIndex = 0; ah->nfCalHist[i].currIndex = 0;
ah->nfCalHist[i].privNF = noise_floor; ah->nfCalHist[i].privNF = limit->nominal;
ah->nfCalHist[i].invalidNFcount = ah->nfCalHist[i].invalidNFcount =
AR_PHY_CCA_FILTERWINDOW_LENGTH; AR_PHY_CCA_FILTERWINDOW_LENGTH;
for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) { for (j = 0; j < ATH9K_NF_CAL_HIST_MAX; j++) {
ah->nfCalHist[i].nfCalBuffer[j] = noise_floor; ah->nfCalHist[i].nfCalBuffer[j] = limit->nominal;
} }
} }
} }

View File

@ -19,12 +19,6 @@
#include "hw.h" #include "hw.h"
#define AR_PHY_CCA_MAX_AR5416_GOOD_VALUE -85
#define AR_PHY_CCA_MAX_AR9280_GOOD_VALUE -112
#define AR_PHY_CCA_MAX_AR9285_GOOD_VALUE -118
#define AR_PHY_CCA_MAX_AR9287_GOOD_VALUE -118
#define AR_PHY_CCA_MAX_HIGH_VALUE -62
#define AR_PHY_CCA_MIN_BAD_VALUE -140
#define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3 #define AR_PHY_CCA_FILTERWINDOW_LENGTH_INIT 3
#define AR_PHY_CCA_FILTERWINDOW_LENGTH 5 #define AR_PHY_CCA_FILTERWINDOW_LENGTH 5
@ -115,6 +109,7 @@ struct ath9k_pacal_info{
bool ath9k_hw_reset_calvalid(struct ath_hw *ah); bool ath9k_hw_reset_calvalid(struct ath_hw *ah);
void ath9k_hw_start_nfcal(struct ath_hw *ah); void ath9k_hw_start_nfcal(struct ath_hw *ah);
void ath9k_hw_loadnf(struct ath_hw *ah, struct ath9k_channel *chan);
int16_t ath9k_hw_getnf(struct ath_hw *ah, int16_t ath9k_hw_getnf(struct ath_hw *ah,
struct ath9k_channel *chan); struct ath9k_channel *chan);
void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah); void ath9k_init_nfcal_hist_buffer(struct ath_hw *ah);

View File

@ -319,6 +319,10 @@ int ath9k_cmn_key_config(struct ath_common *common,
idx = ath_reserve_key_cache_slot(common, key->alg); idx = ath_reserve_key_cache_slot(common, key->alg);
break; break;
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
if (!sta) {
idx = key->keyidx;
break;
}
memcpy(gmac, sta->addr, ETH_ALEN); memcpy(gmac, sta->addr, ETH_ALEN);
gmac[0] |= 0x01; gmac[0] |= 0x01;
mac = gmac; mac = gmac;

View File

@ -670,7 +670,7 @@ struct eeprom_ops {
int (*get_eeprom_ver)(struct ath_hw *hw); int (*get_eeprom_ver)(struct ath_hw *hw);
int (*get_eeprom_rev)(struct ath_hw *hw); int (*get_eeprom_rev)(struct ath_hw *hw);
u8 (*get_num_ant_config)(struct ath_hw *hw, enum ieee80211_band band); u8 (*get_num_ant_config)(struct ath_hw *hw, enum ieee80211_band band);
u16 (*get_eeprom_antenna_cfg)(struct ath_hw *hw, u32 (*get_eeprom_antenna_cfg)(struct ath_hw *hw,
struct ath9k_channel *chan); struct ath9k_channel *chan);
void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan); void (*set_board_values)(struct ath_hw *hw, struct ath9k_channel *chan);
void (*set_addac)(struct ath_hw *hw, struct ath9k_channel *chan); void (*set_addac)(struct ath_hw *hw, struct ath9k_channel *chan);

View File

@ -1150,13 +1150,13 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah,
} }
} }
static u16 ath9k_hw_4k_get_eeprom_antenna_cfg(struct ath_hw *ah, static u32 ath9k_hw_4k_get_eeprom_antenna_cfg(struct ath_hw *ah,
struct ath9k_channel *chan) struct ath9k_channel *chan)
{ {
struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
struct modal_eep_4k_header *pModal = &eep->modalHeader; struct modal_eep_4k_header *pModal = &eep->modalHeader;
return pModal->antCtrlCommon & 0xFFFF; return pModal->antCtrlCommon;
} }
static u8 ath9k_hw_4k_get_num_ant_config(struct ath_hw *ah, static u8 ath9k_hw_4k_get_num_ant_config(struct ath_hw *ah,

View File

@ -1130,13 +1130,13 @@ static u8 ath9k_hw_ar9287_get_num_ant_config(struct ath_hw *ah,
return 1; return 1;
} }
static u16 ath9k_hw_ar9287_get_eeprom_antenna_cfg(struct ath_hw *ah, static u32 ath9k_hw_ar9287_get_eeprom_antenna_cfg(struct ath_hw *ah,
struct ath9k_channel *chan) struct ath9k_channel *chan)
{ {
struct ar9287_eeprom *eep = &ah->eeprom.map9287; struct ar9287_eeprom *eep = &ah->eeprom.map9287;
struct modal_eep_ar9287_header *pModal = &eep->modalHeader; struct modal_eep_ar9287_header *pModal = &eep->modalHeader;
return pModal->antCtrlCommon & 0xFFFF; return pModal->antCtrlCommon;
} }
static u16 ath9k_hw_ar9287_get_spur_channel(struct ath_hw *ah, static u16 ath9k_hw_ar9287_get_spur_channel(struct ath_hw *ah,

View File

@ -730,7 +730,7 @@ static void ath9k_hw_get_def_gain_boundaries_pdadcs(struct ath_hw *ah,
vpdTableI[i][sizeCurrVpdTable - 2]); vpdTableI[i][sizeCurrVpdTable - 2]);
vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep);
if (tgtIndex > maxIndex) { if (tgtIndex >= maxIndex) {
while ((ss <= tgtIndex) && while ((ss <= tgtIndex) &&
(k < (AR5416_NUM_PDADC_VALUES - 1))) { (k < (AR5416_NUM_PDADC_VALUES - 1))) {
tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] + tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] +
@ -1438,14 +1438,14 @@ static u8 ath9k_hw_def_get_num_ant_config(struct ath_hw *ah,
return num_ant_config; return num_ant_config;
} }
static u16 ath9k_hw_def_get_eeprom_antenna_cfg(struct ath_hw *ah, static u32 ath9k_hw_def_get_eeprom_antenna_cfg(struct ath_hw *ah,
struct ath9k_channel *chan) struct ath9k_channel *chan)
{ {
struct ar5416_eeprom_def *eep = &ah->eeprom.def; struct ar5416_eeprom_def *eep = &ah->eeprom.def;
struct modal_eep_header *pModal = struct modal_eep_header *pModal =
&(eep->modalHeader[IS_CHAN_2GHZ(chan)]); &(eep->modalHeader[IS_CHAN_2GHZ(chan)]);
return pModal->antCtrlCommon & 0xFFFF; return pModal->antCtrlCommon;
} }
static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz)

View File

@ -745,13 +745,17 @@ static int ath9k_hif_usb_alloc_urbs(struct hif_device_usb *hif_dev)
/* RX */ /* RX */
if (ath9k_hif_usb_alloc_rx_urbs(hif_dev) < 0) if (ath9k_hif_usb_alloc_rx_urbs(hif_dev) < 0)
goto err; goto err_rx;
/* Register Read */ /* Register Read */
if (ath9k_hif_usb_alloc_reg_in_urb(hif_dev) < 0) if (ath9k_hif_usb_alloc_reg_in_urb(hif_dev) < 0)
goto err; goto err_reg;
return 0; return 0;
err_reg:
ath9k_hif_usb_dealloc_rx_urbs(hif_dev);
err_rx:
ath9k_hif_usb_dealloc_tx_urbs(hif_dev);
err: err:
return -ENOMEM; return -ENOMEM;
} }

View File

@ -264,12 +264,6 @@ static inline void ath9k_hw_do_getnf(struct ath_hw *ah,
ath9k_hw_private_ops(ah)->do_getnf(ah, nfarray); ath9k_hw_private_ops(ah)->do_getnf(ah, nfarray);
} }
static inline void ath9k_hw_loadnf(struct ath_hw *ah,
struct ath9k_channel *chan)
{
ath9k_hw_private_ops(ah)->loadnf(ah, chan);
}
static inline bool ath9k_hw_init_cal(struct ath_hw *ah, static inline bool ath9k_hw_init_cal(struct ath_hw *ah,
struct ath9k_channel *chan) struct ath9k_channel *chan)
{ {

View File

@ -609,9 +609,6 @@ static int __ath9k_hw_init(struct ath_hw *ah)
else else
ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S); ah->tx_trig_level = (AR_FTRIG_512B >> AR_FTRIG_S);
if (AR_SREV_9300_20_OR_LATER(ah))
ar9003_hw_set_nf_limits(ah);
ath9k_init_nfcal_hist_buffer(ah); ath9k_init_nfcal_hist_buffer(ah);
ah->bb_watchdog_timeout_ms = 25; ah->bb_watchdog_timeout_ms = 25;
@ -1235,9 +1232,11 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
if (!ah->chip_fullsleep) { if (!ah->chip_fullsleep) {
ath9k_hw_abortpcurecv(ah); ath9k_hw_abortpcurecv(ah);
if (!ath9k_hw_stopdmarecv(ah)) if (!ath9k_hw_stopdmarecv(ah)) {
ath_print(common, ATH_DBG_XMIT, ath_print(common, ATH_DBG_XMIT,
"Failed to stop receive dma\n"); "Failed to stop receive dma\n");
bChannelChange = false;
}
} }
if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))

View File

@ -510,7 +510,6 @@ struct ath_gen_timer_table {
* AR_RTC_PLL_CONTROL for a given channel * AR_RTC_PLL_CONTROL for a given channel
* @setup_calibration: set up calibration * @setup_calibration: set up calibration
* @iscal_supported: used to query if a type of calibration is supported * @iscal_supported: used to query if a type of calibration is supported
* @loadnf: load noise floor read from each chain on the CCA registers
* *
* @ani_reset: reset ANI parameters to default values * @ani_reset: reset ANI parameters to default values
* @ani_lower_immunity: lower the noise immunity level. The level controls * @ani_lower_immunity: lower the noise immunity level. The level controls
@ -564,7 +563,6 @@ struct ath_hw_private_ops {
bool (*ani_control)(struct ath_hw *ah, enum ath9k_ani_cmd cmd, bool (*ani_control)(struct ath_hw *ah, enum ath9k_ani_cmd cmd,
int param); int param);
void (*do_getnf)(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]); void (*do_getnf)(struct ath_hw *ah, int16_t nfarray[NUM_NF_READINGS]);
void (*loadnf)(struct ath_hw *ah, struct ath9k_channel *chan);
/* ANI */ /* ANI */
void (*ani_reset)(struct ath_hw *ah, bool is_scanning); void (*ani_reset)(struct ath_hw *ah, bool is_scanning);
@ -630,6 +628,12 @@ struct ath_hw_ops {
void (*ani_monitor)(struct ath_hw *ah, struct ath9k_channel *chan); void (*ani_monitor)(struct ath_hw *ah, struct ath9k_channel *chan);
}; };
struct ath_nf_limits {
s16 max;
s16 min;
s16 nominal;
};
struct ath_hw { struct ath_hw {
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
struct ath_common common; struct ath_common common;
@ -651,10 +655,10 @@ struct ath_hw {
bool is_pciexpress; bool is_pciexpress;
bool need_an_top2_fixup; bool need_an_top2_fixup;
u16 tx_trig_level; u16 tx_trig_level;
s16 nf_2g_max;
s16 nf_2g_min; u32 nf_regs[6];
s16 nf_5g_max; struct ath_nf_limits nf_2g;
s16 nf_5g_min; struct ath_nf_limits nf_5g;
u16 rfsilent; u16 rfsilent;
u32 rfkill_gpio; u32 rfkill_gpio;
u32 rfkill_polarity; u32 rfkill_polarity;
@ -848,6 +852,12 @@ static inline struct ath_hw_ops *ath9k_hw_ops(struct ath_hw *ah)
return &ah->ops; return &ah->ops;
} }
static inline int sign_extend(int val, const int nbits)
{
int order = BIT(nbits-1);
return (val ^ order) - order;
}
/* Initialization, Detach, Reset */ /* Initialization, Detach, Reset */
const char *ath9k_hw_probe(u16 vendorid, u16 devid); const char *ath9k_hw_probe(u16 vendorid, u16 devid);
void ath9k_hw_deinit(struct ath_hw *ah); void ath9k_hw_deinit(struct ath_hw *ah);
@ -943,7 +953,6 @@ void ar9002_hw_enable_wep_aggregation(struct ath_hw *ah);
* Code specific to AR9003, we stuff these here to avoid callbacks * Code specific to AR9003, we stuff these here to avoid callbacks
* for older families * for older families
*/ */
void ar9003_hw_set_nf_limits(struct ath_hw *ah);
void ar9003_hw_bb_watchdog_config(struct ath_hw *ah); void ar9003_hw_bb_watchdog_config(struct ath_hw *ah);
void ar9003_hw_bb_watchdog_read(struct ath_hw *ah); void ar9003_hw_bb_watchdog_read(struct ath_hw *ah);
void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah); void ar9003_hw_bb_watchdog_dbg_info(struct ath_hw *ah);

View File

@ -718,6 +718,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid,
goto error_world; goto error_world;
} }
INIT_WORK(&sc->hw_check_work, ath_hw_check);
INIT_WORK(&sc->paprd_work, ath_paprd_calibrate); INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work);
INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work);

View File

@ -485,6 +485,9 @@ struct ar5416_desc {
#define AR_TxRSSICombined 0xff000000 #define AR_TxRSSICombined 0xff000000
#define AR_TxRSSICombined_S 24 #define AR_TxRSSICombined_S 24
#define AR_TxTid 0xf0000000
#define AR_TxTid_S 28
#define AR_TxEVM0 ds_txstatus5 #define AR_TxEVM0 ds_txstatus5
#define AR_TxEVM1 ds_txstatus6 #define AR_TxEVM1 ds_txstatus6
#define AR_TxEVM2 ds_txstatus7 #define AR_TxEVM2 ds_txstatus7

View File

@ -515,6 +515,25 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta)
ath_tx_node_cleanup(sc, an); ath_tx_node_cleanup(sc, an);
} }
void ath_hw_check(struct work_struct *work)
{
struct ath_softc *sc = container_of(work, struct ath_softc, hw_check_work);
int i;
ath9k_ps_wakeup(sc);
for (i = 0; i < 3; i++) {
if (ath9k_hw_check_alive(sc->sc_ah))
goto out;
msleep(1);
}
ath_reset(sc, false);
out:
ath9k_ps_restore(sc);
}
void ath9k_tasklet(unsigned long data) void ath9k_tasklet(unsigned long data)
{ {
struct ath_softc *sc = (struct ath_softc *)data; struct ath_softc *sc = (struct ath_softc *)data;
@ -526,13 +545,15 @@ void ath9k_tasklet(unsigned long data)
ath9k_ps_wakeup(sc); ath9k_ps_wakeup(sc);
if ((status & ATH9K_INT_FATAL) || if (status & ATH9K_INT_FATAL) {
!ath9k_hw_check_alive(ah)) {
ath_reset(sc, false); ath_reset(sc, false);
ath9k_ps_restore(sc); ath9k_ps_restore(sc);
return; return;
} }
if (!ath9k_hw_check_alive(ah))
ieee80211_queue_work(sc->hw, &sc->hw_check_work);
if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL | rxmask = (ATH9K_INT_RXHP | ATH9K_INT_RXLP | ATH9K_INT_RXEOL |
ATH9K_INT_RXORN); ATH9K_INT_RXORN);
@ -1253,6 +1274,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->tx_complete_work);
cancel_work_sync(&sc->paprd_work); cancel_work_sync(&sc->paprd_work);
cancel_work_sync(&sc->hw_check_work);
if (!sc->num_sec_wiphy) { if (!sc->num_sec_wiphy) {
cancel_delayed_work_sync(&sc->wiphy_work); cancel_delayed_work_sync(&sc->wiphy_work);
@ -1976,6 +1998,7 @@ static void ath9k_sw_scan_start(struct ieee80211_hw *hw)
sc->sc_flags |= SC_OP_SCANNING; sc->sc_flags |= SC_OP_SCANNING;
del_timer_sync(&common->ani.timer); del_timer_sync(&common->ani.timer);
cancel_work_sync(&sc->paprd_work); cancel_work_sync(&sc->paprd_work);
cancel_work_sync(&sc->hw_check_work);
cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->tx_complete_work);
mutex_unlock(&sc->mutex); mutex_unlock(&sc->mutex);
} }

View File

@ -329,6 +329,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0; int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0;
bool rc_update = true; bool rc_update = true;
struct ieee80211_tx_rate rates[4]; struct ieee80211_tx_rate rates[4];
unsigned long flags;
skb = bf->bf_mpdu; skb = bf->bf_mpdu;
hdr = (struct ieee80211_hdr *)skb->data; hdr = (struct ieee80211_hdr *)skb->data;
@ -344,12 +345,24 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
sta = ieee80211_find_sta_by_hw(hw, hdr->addr1); sta = ieee80211_find_sta_by_hw(hw, hdr->addr1);
if (!sta) { if (!sta) {
rcu_read_unlock(); rcu_read_unlock();
spin_lock_irqsave(&sc->tx.txbuflock, flags);
list_splice_tail_init(bf_q, &sc->tx.txbuf);
spin_unlock_irqrestore(&sc->tx.txbuflock, flags);
return; return;
} }
an = (struct ath_node *)sta->drv_priv; an = (struct ath_node *)sta->drv_priv;
tid = ATH_AN_2_TID(an, bf->bf_tidno); tid = ATH_AN_2_TID(an, bf->bf_tidno);
/*
* The hardware occasionally sends a tx status for the wrong TID.
* In this case, the BA status cannot be considered valid and all
* subframes need to be retransmitted
*/
if (bf->bf_tidno != ts->tid)
txok = false;
isaggr = bf_isaggr(bf); isaggr = bf_isaggr(bf);
memset(ba, 0, WME_BA_BMP_SIZE >> 3); memset(ba, 0, WME_BA_BMP_SIZE >> 3);
@ -2430,37 +2443,37 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an)
void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an) void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an)
{ {
int i; struct ath_atx_ac *ac;
struct ath_atx_ac *ac, *ac_tmp; struct ath_atx_tid *tid;
struct ath_atx_tid *tid, *tid_tmp;
struct ath_txq *txq; struct ath_txq *txq;
int i, tidno;
for (tidno = 0, tid = &an->tid[tidno];
tidno < WME_NUM_TID; tidno++, tid++) {
i = tid->ac->qnum;
if (!ATH_TXQ_SETUP(sc, i))
continue;
for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
if (ATH_TXQ_SETUP(sc, i)) {
txq = &sc->tx.txq[i]; txq = &sc->tx.txq[i];
ac = tid->ac;
spin_lock_bh(&txq->axq_lock); spin_lock_bh(&txq->axq_lock);
list_for_each_entry_safe(ac, if (tid->sched) {
ac_tmp, &txq->axq_acq, list) {
tid = list_first_entry(&ac->tid_q,
struct ath_atx_tid, list);
if (tid && tid->an != an)
continue;
list_del(&ac->list);
ac->sched = false;
list_for_each_entry_safe(tid,
tid_tmp, &ac->tid_q, list) {
list_del(&tid->list); list_del(&tid->list);
tid->sched = false; tid->sched = false;
}
if (ac->sched) {
list_del(&ac->list);
tid->ac->sched = false;
}
ath_tid_drain(sc, txq, tid); ath_tid_drain(sc, txq, tid);
tid->state &= ~AGGR_ADDBA_COMPLETE; tid->state &= ~AGGR_ADDBA_COMPLETE;
tid->state &= ~AGGR_CLEANUP; tid->state &= ~AGGR_CLEANUP;
}
}
spin_unlock_bh(&txq->axq_lock); spin_unlock_bh(&txq->axq_lock);
} }
}
} }

View File

@ -186,7 +186,7 @@ int prism2_wds_add(local_info_t *local, u8 *remote_addr,
return -ENOBUFS; return -ENOBUFS;
/* verify that there is room for wds# postfix in the interface name */ /* verify that there is room for wds# postfix in the interface name */
if (strlen(local->dev->name) > IFNAMSIZ - 5) { if (strlen(local->dev->name) >= IFNAMSIZ - 5) {
printk(KERN_DEBUG "'%s' too long base device name\n", printk(KERN_DEBUG "'%s' too long base device name\n",
local->dev->name); local->dev->name);
return -EINVAL; return -EINVAL;

View File

@ -87,10 +87,15 @@ config IWL4965
This option enables support for Intel Wireless WiFi Link 4965AGN This option enables support for Intel Wireless WiFi Link 4965AGN
config IWL5000 config IWL5000
bool "Intel Wireless WiFi 5000AGN; Intel WiFi Link 1000, 6000, and 6050 Series" bool "Intel Wireless-N/Advanced-N/Ultimate-N WiFi Link"
depends on IWLAGN depends on IWLAGN
---help--- ---help---
This option enables support for Intel Wireless WiFi Link 5000AGN Family This option enables support for use with the following hardware:
Intel Wireless WiFi Link 6250AGN Adapter
Intel 6000 Series Wi-Fi Adapters (6200AGN and 6300AGN)
Intel WiFi Link 1000BGN
Intel Wireless WiFi 5150AGN
Intel Wireless WiFi 5100AGN, 5300AGN, and 5350AGN
config IWL3945 config IWL3945
tristate "Intel PRO/Wireless 3945ABG/BG Network Connection (iwl3945)" tristate "Intel PRO/Wireless 3945ABG/BG Network Connection (iwl3945)"

View File

@ -129,8 +129,8 @@ static int iwl1000_hw_set_hw_params(struct iwl_priv *priv)
priv->cfg->num_of_queues * priv->cfg->num_of_queues *
sizeof(struct iwlagn_scd_bc_tbl); sizeof(struct iwlagn_scd_bc_tbl);
priv->hw_params.tfd_size = sizeof(struct iwl_tfd); priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
priv->hw_params.max_stations = IWL5000_STATION_COUNT; priv->hw_params.max_stations = IWLAGN_STATION_COUNT;
priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID; priv->hw_params.bcast_sta_id = IWLAGN_BROADCAST_ID;
priv->hw_params.max_data_size = IWLAGN_RTC_DATA_SIZE; priv->hw_params.max_data_size = IWLAGN_RTC_DATA_SIZE;
priv->hw_params.max_inst_size = IWLAGN_RTC_INST_SIZE; priv->hw_params.max_inst_size = IWLAGN_RTC_INST_SIZE;
@ -226,6 +226,8 @@ static struct iwl_lib_ops iwl1000_lib = {
.recover_from_tx_stall = iwl_bg_monitor_recover, .recover_from_tx_stall = iwl_bg_monitor_recover,
.check_plcp_health = iwl_good_plcp_health, .check_plcp_health = iwl_good_plcp_health,
.check_ack_health = iwl_good_ack_health, .check_ack_health = iwl_good_ack_health,
.txfifo_flush = iwlagn_txfifo_flush,
.dev_txfifo_flush = iwlagn_dev_txfifo_flush,
}; };
static const struct iwl_ops iwl1000_ops = { static const struct iwl_ops iwl1000_ops = {

View File

@ -179,8 +179,8 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv)
priv->cfg->num_of_queues * priv->cfg->num_of_queues *
sizeof(struct iwlagn_scd_bc_tbl); sizeof(struct iwlagn_scd_bc_tbl);
priv->hw_params.tfd_size = sizeof(struct iwl_tfd); priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
priv->hw_params.max_stations = IWL5000_STATION_COUNT; priv->hw_params.max_stations = IWLAGN_STATION_COUNT;
priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID; priv->hw_params.bcast_sta_id = IWLAGN_BROADCAST_ID;
priv->hw_params.max_data_size = IWLAGN_RTC_DATA_SIZE; priv->hw_params.max_data_size = IWLAGN_RTC_DATA_SIZE;
priv->hw_params.max_inst_size = IWLAGN_RTC_INST_SIZE; priv->hw_params.max_inst_size = IWLAGN_RTC_INST_SIZE;
@ -226,8 +226,8 @@ static int iwl5150_hw_set_hw_params(struct iwl_priv *priv)
priv->cfg->num_of_queues * priv->cfg->num_of_queues *
sizeof(struct iwlagn_scd_bc_tbl); sizeof(struct iwlagn_scd_bc_tbl);
priv->hw_params.tfd_size = sizeof(struct iwl_tfd); priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
priv->hw_params.max_stations = IWL5000_STATION_COUNT; priv->hw_params.max_stations = IWLAGN_STATION_COUNT;
priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID; priv->hw_params.bcast_sta_id = IWLAGN_BROADCAST_ID;
priv->hw_params.max_data_size = IWLAGN_RTC_DATA_SIZE; priv->hw_params.max_data_size = IWLAGN_RTC_DATA_SIZE;
priv->hw_params.max_inst_size = IWLAGN_RTC_INST_SIZE; priv->hw_params.max_inst_size = IWLAGN_RTC_INST_SIZE;
@ -402,6 +402,8 @@ static struct iwl_lib_ops iwl5000_lib = {
.recover_from_tx_stall = iwl_bg_monitor_recover, .recover_from_tx_stall = iwl_bg_monitor_recover,
.check_plcp_health = iwl_good_plcp_health, .check_plcp_health = iwl_good_plcp_health,
.check_ack_health = iwl_good_ack_health, .check_ack_health = iwl_good_ack_health,
.txfifo_flush = iwlagn_txfifo_flush,
.dev_txfifo_flush = iwlagn_dev_txfifo_flush,
}; };
static struct iwl_lib_ops iwl5150_lib = { static struct iwl_lib_ops iwl5150_lib = {
@ -465,6 +467,8 @@ static struct iwl_lib_ops iwl5150_lib = {
.recover_from_tx_stall = iwl_bg_monitor_recover, .recover_from_tx_stall = iwl_bg_monitor_recover,
.check_plcp_health = iwl_good_plcp_health, .check_plcp_health = iwl_good_plcp_health,
.check_ack_health = iwl_good_ack_health, .check_ack_health = iwl_good_ack_health,
.txfifo_flush = iwlagn_txfifo_flush,
.dev_txfifo_flush = iwlagn_dev_txfifo_flush,
}; };
static const struct iwl_ops iwl5000_ops = { static const struct iwl_ops iwl5000_ops = {

View File

@ -160,8 +160,8 @@ static int iwl6000_hw_set_hw_params(struct iwl_priv *priv)
priv->cfg->num_of_queues * priv->cfg->num_of_queues *
sizeof(struct iwlagn_scd_bc_tbl); sizeof(struct iwlagn_scd_bc_tbl);
priv->hw_params.tfd_size = sizeof(struct iwl_tfd); priv->hw_params.tfd_size = sizeof(struct iwl_tfd);
priv->hw_params.max_stations = IWL5000_STATION_COUNT; priv->hw_params.max_stations = IWLAGN_STATION_COUNT;
priv->hw_params.bcast_sta_id = IWL5000_BROADCAST_ID; priv->hw_params.bcast_sta_id = IWLAGN_BROADCAST_ID;
priv->hw_params.max_data_size = IWL60_RTC_DATA_SIZE; priv->hw_params.max_data_size = IWL60_RTC_DATA_SIZE;
priv->hw_params.max_inst_size = IWL60_RTC_INST_SIZE; priv->hw_params.max_inst_size = IWL60_RTC_INST_SIZE;
@ -327,6 +327,8 @@ static struct iwl_lib_ops iwl6000_lib = {
.recover_from_tx_stall = iwl_bg_monitor_recover, .recover_from_tx_stall = iwl_bg_monitor_recover,
.check_plcp_health = iwl_good_plcp_health, .check_plcp_health = iwl_good_plcp_health,
.check_ack_health = iwl_good_ack_health, .check_ack_health = iwl_good_ack_health,
.txfifo_flush = iwlagn_txfifo_flush,
.dev_txfifo_flush = iwlagn_dev_txfifo_flush,
}; };
static const struct iwl_ops iwl6000_ops = { static const struct iwl_ops iwl6000_ops = {
@ -827,6 +829,44 @@ struct iwl_cfg iwl6050_2agn_cfg = {
.need_dc_calib = true, .need_dc_calib = true,
}; };
struct iwl_cfg iwl6050g2_bgn_cfg = {
.name = "6050 Series 1x2 BGN Gen2",
.fw_name_pre = IWL6050_FW_PRE,
.ucode_api_max = IWL6050_UCODE_API_MAX,
.ucode_api_min = IWL6050_UCODE_API_MIN,
.sku = IWL_SKU_G|IWL_SKU_N,
.ops = &iwl6000_ops,
.eeprom_size = OTP_LOW_IMAGE_SIZE,
.eeprom_ver = EEPROM_6050G2_EEPROM_VERSION,
.eeprom_calib_ver = EEPROM_6050G2_TX_POWER_VERSION,
.num_of_queues = IWLAGN_NUM_QUEUES,
.num_of_ampdu_queues = IWLAGN_NUM_AMPDU_QUEUES,
.mod_params = &iwlagn_mod_params,
.valid_tx_ant = ANT_A,
.valid_rx_ant = ANT_AB,
.pll_cfg_val = 0,
.set_l0s = true,
.use_bsm = false,
.pa_type = IWL_PA_SYSTEM,
.max_ll_items = OTP_MAX_LL_ITEMS_6x50,
.shadow_ram_support = true,
.ht_greenfield_support = true,
.led_compensation = 51,
.use_rts_for_ht = true, /* use rts/cts protection */
.chain_noise_num_beacons = IWL_CAL_NUM_BEACONS,
.supports_idle = true,
.adv_thermal_throttle = true,
.support_ct_kill_exit = true,
.plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
.chain_noise_scale = 1500,
.monitor_recover_period = IWL_MONITORING_PERIOD,
.max_event_log_size = 1024,
.ucode_tracing = true,
.sensitivity_calib_by_driver = true,
.chain_noise_calib_by_driver = true,
.need_dc_calib = true,
};
struct iwl_cfg iwl6050_2abg_cfg = { struct iwl_cfg iwl6050_2abg_cfg = {
.name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG", .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG",
.fw_name_pre = IWL6050_FW_PRE, .fw_name_pre = IWL6050_FW_PRE,

View File

@ -409,10 +409,50 @@ static int iwl_sens_auto_corr_ofdm(struct iwl_priv *priv,
return 0; return 0;
} }
static void iwl_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv,
struct iwl_sensitivity_data *data,
__le16 *tbl)
{
tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] =
cpu_to_le16((u16)data->auto_corr_ofdm);
tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
cpu_to_le16((u16)data->auto_corr_ofdm_x1);
tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
cpu_to_le16((u16)data->auto_corr_cck);
tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
cpu_to_le16((u16)data->auto_corr_cck_mrc);
tbl[HD_MIN_ENERGY_CCK_DET_INDEX] =
cpu_to_le16((u16)data->nrg_th_cck);
tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] =
cpu_to_le16((u16)data->nrg_th_ofdm);
tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
cpu_to_le16(data->barker_corr_th_min);
tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
cpu_to_le16(data->barker_corr_th_min_mrc);
tbl[HD_OFDM_ENERGY_TH_IN_INDEX] =
cpu_to_le16(data->nrg_th_cca);
IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
data->nrg_th_ofdm);
IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n",
data->auto_corr_cck, data->auto_corr_cck_mrc,
data->nrg_th_cck);
}
/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ /* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
static int iwl_sensitivity_write(struct iwl_priv *priv) static int iwl_sensitivity_write(struct iwl_priv *priv)
{ {
struct iwl_sensitivity_cmd cmd ; struct iwl_sensitivity_cmd cmd;
struct iwl_sensitivity_data *data = NULL; struct iwl_sensitivity_data *data = NULL;
struct iwl_host_cmd cmd_out = { struct iwl_host_cmd cmd_out = {
.id = SENSITIVITY_CMD, .id = SENSITIVITY_CMD,
@ -425,40 +465,7 @@ static int iwl_sensitivity_write(struct iwl_priv *priv)
memset(&cmd, 0, sizeof(cmd)); memset(&cmd, 0, sizeof(cmd));
cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] = iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]);
cpu_to_le16((u16)data->auto_corr_ofdm);
cmd.table[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] =
cpu_to_le16((u16)data->auto_corr_ofdm_mrc);
cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] =
cpu_to_le16((u16)data->auto_corr_ofdm_x1);
cmd.table[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] =
cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1);
cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] =
cpu_to_le16((u16)data->auto_corr_cck);
cmd.table[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] =
cpu_to_le16((u16)data->auto_corr_cck_mrc);
cmd.table[HD_MIN_ENERGY_CCK_DET_INDEX] =
cpu_to_le16((u16)data->nrg_th_cck);
cmd.table[HD_MIN_ENERGY_OFDM_DET_INDEX] =
cpu_to_le16((u16)data->nrg_th_ofdm);
cmd.table[HD_BARKER_CORR_TH_ADD_MIN_INDEX] =
cpu_to_le16(data->barker_corr_th_min);
cmd.table[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] =
cpu_to_le16(data->barker_corr_th_min_mrc);
cmd.table[HD_OFDM_ENERGY_TH_IN_INDEX] =
cpu_to_le16(data->nrg_th_cca);
IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n",
data->auto_corr_ofdm, data->auto_corr_ofdm_mrc,
data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1,
data->nrg_th_ofdm);
IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n",
data->auto_corr_cck, data->auto_corr_cck_mrc,
data->nrg_th_cck);
/* Update uCode's "work" table, and copy it to DSP */ /* Update uCode's "work" table, and copy it to DSP */
cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
@ -477,6 +484,70 @@ static int iwl_sensitivity_write(struct iwl_priv *priv)
return iwl_send_cmd(priv, &cmd_out); return iwl_send_cmd(priv, &cmd_out);
} }
/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */
static int iwl_enhance_sensitivity_write(struct iwl_priv *priv)
{
struct iwl_enhance_sensitivity_cmd cmd;
struct iwl_sensitivity_data *data = NULL;
struct iwl_host_cmd cmd_out = {
.id = SENSITIVITY_CMD,
.len = sizeof(struct iwl_enhance_sensitivity_cmd),
.flags = CMD_ASYNC,
.data = &cmd,
};
data = &(priv->sensitivity_data);
memset(&cmd, 0, sizeof(cmd));
iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]);
cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] =
HD_INA_NON_SQUARE_DET_OFDM_DATA;
cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] =
HD_INA_NON_SQUARE_DET_CCK_DATA;
cmd.enhance_table[HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX] =
HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA;
cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA;
cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA;
cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX] =
HD_OFDM_NON_SQUARE_DET_SLOPE_DATA;
cmd.enhance_table[HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX] =
HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA;
cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX] =
HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA;
cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX] =
HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA;
cmd.enhance_table[HD_CCK_NON_SQUARE_DET_SLOPE_INDEX] =
HD_CCK_NON_SQUARE_DET_SLOPE_DATA;
cmd.enhance_table[HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX] =
HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA;
/* Update uCode's "work" table, and copy it to DSP */
cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE;
/* Don't send command to uCode if nothing has changed */
if (!memcmp(&cmd.enhance_table[0], &(priv->sensitivity_tbl[0]),
sizeof(u16)*HD_TABLE_SIZE) &&
!memcmp(&cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX],
&(priv->enhance_sensitivity_tbl[0]),
sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES)) {
IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n");
return 0;
}
/* Copy table for comparison next time */
memcpy(&(priv->sensitivity_tbl[0]), &(cmd.enhance_table[0]),
sizeof(u16)*HD_TABLE_SIZE);
memcpy(&(priv->enhance_sensitivity_tbl[0]),
&(cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX]),
sizeof(u16)*ENHANCE_HD_TABLE_ENTRIES);
return iwl_send_cmd(priv, &cmd_out);
}
void iwl_init_sensitivity(struct iwl_priv *priv) void iwl_init_sensitivity(struct iwl_priv *priv)
{ {
int ret = 0; int ret = 0;
@ -527,6 +598,9 @@ void iwl_init_sensitivity(struct iwl_priv *priv)
data->last_bad_plcp_cnt_cck = 0; data->last_bad_plcp_cnt_cck = 0;
data->last_fa_cnt_cck = 0; data->last_fa_cnt_cck = 0;
if (priv->enhance_sensitivity_table)
ret |= iwl_enhance_sensitivity_write(priv);
else
ret |= iwl_sensitivity_write(priv); ret |= iwl_sensitivity_write(priv);
IWL_DEBUG_CALIB(priv, "<<return 0x%X\n", ret); IWL_DEBUG_CALIB(priv, "<<return 0x%X\n", ret);
} }
@ -633,6 +707,9 @@ void iwl_sensitivity_calibration(struct iwl_priv *priv,
iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time); iwl_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time);
iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis); iwl_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis);
if (priv->enhance_sensitivity_table)
iwl_enhance_sensitivity_write(priv);
else
iwl_sensitivity_write(priv); iwl_sensitivity_write(priv);
} }

View File

@ -205,7 +205,9 @@ void iwl_check_abort_status(struct iwl_priv *priv,
u8 frame_count, u32 status) u8 frame_count, u32 status)
{ {
if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) {
IWL_ERR(priv, "TODO: Implement Tx flush command!!!\n"); IWL_ERR(priv, "Tx flush command to flush out all frames\n");
if (!test_bit(STATUS_EXIT_PENDING, &priv->status))
queue_work(priv->workqueue, &priv->tx_flush);
} }
} }
@ -1435,3 +1437,81 @@ void iwl_free_tfds_in_queue(struct iwl_priv *priv,
priv->stations[sta_id].tid[tid].tfds_in_queue = 0; priv->stations[sta_id].tid[tid].tfds_in_queue = 0;
} }
} }
#define IWL_FLUSH_WAIT_MS 2000
int iwlagn_wait_tx_queue_empty(struct iwl_priv *priv)
{
struct iwl_tx_queue *txq;
struct iwl_queue *q;
int cnt;
unsigned long now = jiffies;
int ret = 0;
/* waiting for all the tx frames complete might take a while */
for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
if (cnt == IWL_CMD_QUEUE_NUM)
continue;
txq = &priv->txq[cnt];
q = &txq->q;
while (q->read_ptr != q->write_ptr && !time_after(jiffies,
now + msecs_to_jiffies(IWL_FLUSH_WAIT_MS)))
msleep(1);
if (q->read_ptr != q->write_ptr) {
IWL_ERR(priv, "fail to flush all tx fifo queues\n");
ret = -ETIMEDOUT;
break;
}
}
return ret;
}
#define IWL_TX_QUEUE_MSK 0xfffff
/**
* iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode
*
* pre-requirements:
* 1. acquire mutex before calling
* 2. make sure rf is on and not in exit state
*/
int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control)
{
struct iwl_txfifo_flush_cmd flush_cmd;
struct iwl_host_cmd cmd = {
.id = REPLY_TXFIFO_FLUSH,
.len = sizeof(struct iwl_txfifo_flush_cmd),
.flags = CMD_SYNC,
.data = &flush_cmd,
};
might_sleep();
memset(&flush_cmd, 0, sizeof(flush_cmd));
flush_cmd.fifo_control = IWL_TX_FIFO_VO_MSK | IWL_TX_FIFO_VI_MSK |
IWL_TX_FIFO_BE_MSK | IWL_TX_FIFO_BK_MSK;
if (priv->cfg->sku & IWL_SKU_N)
flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK;
IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n",
flush_cmd.fifo_control);
flush_cmd.flush_control = cpu_to_le16(flush_control);
return iwl_send_cmd(priv, &cmd);
}
void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control)
{
mutex_lock(&priv->mutex);
ieee80211_stop_queues(priv->hw);
if (priv->cfg->ops->lib->txfifo_flush(priv, IWL_DROP_ALL)) {
IWL_ERR(priv, "flush request fail\n");
goto done;
}
IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n");
iwlagn_wait_tx_queue_empty(priv);
done:
ieee80211_wake_queues(priv->hw);
mutex_unlock(&priv->mutex);
}

View File

@ -950,9 +950,12 @@ void iwlagn_txq_ctx_stop(struct iwl_priv *priv)
/* Stop each Tx DMA channel, and wait for it to be idle */ /* Stop each Tx DMA channel, and wait for it to be idle */
for (ch = 0; ch < priv->hw_params.dma_chnl_num; ch++) { for (ch = 0; ch < priv->hw_params.dma_chnl_num; ch++) {
iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0);
iwl_poll_direct_bit(priv, FH_TSSR_TX_STATUS_REG, if (iwl_poll_direct_bit(priv, FH_TSSR_TX_STATUS_REG,
FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch),
1000); 1000))
IWL_ERR(priv, "Failing on timeout while stopping"
" DMA channel %d [0x%08x]", ch,
iwl_read_direct32(priv, FH_TSSR_TX_STATUS_REG));
} }
spin_unlock_irqrestore(&priv->lock, flags); spin_unlock_irqrestore(&priv->lock, flags);
} }

View File

@ -859,6 +859,24 @@ int iwl_set_pwr_src(struct iwl_priv *priv, enum iwl_pwr_src src)
return 0; return 0;
} }
static void iwl_bg_tx_flush(struct work_struct *work)
{
struct iwl_priv *priv =
container_of(work, struct iwl_priv, tx_flush);
if (test_bit(STATUS_EXIT_PENDING, &priv->status))
return;
/* do nothing if rf-kill is on */
if (!iwl_is_ready_rf(priv))
return;
if (priv->cfg->ops->lib->txfifo_flush) {
IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n");
iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL);
}
}
/** /**
* iwl_setup_rx_handlers - Initialize Rx handler callbacks * iwl_setup_rx_handlers - Initialize Rx handler callbacks
* *
@ -1806,12 +1824,21 @@ static int iwlagn_load_firmware(struct iwl_priv *priv,
const u8 *data; const u8 *data;
int wanted_alternative = iwlagn_wanted_ucode_alternative, tmp; int wanted_alternative = iwlagn_wanted_ucode_alternative, tmp;
u64 alternatives; u64 alternatives;
u32 tlv_len;
enum iwl_ucode_tlv_type tlv_type;
const u8 *tlv_data;
int ret = 0;
if (len < sizeof(*ucode)) if (len < sizeof(*ucode)) {
IWL_ERR(priv, "uCode has invalid length: %zd\n", len);
return -EINVAL; return -EINVAL;
}
if (ucode->magic != cpu_to_le32(IWL_TLV_UCODE_MAGIC)) if (ucode->magic != cpu_to_le32(IWL_TLV_UCODE_MAGIC)) {
IWL_ERR(priv, "invalid uCode magic: 0X%x\n",
le32_to_cpu(ucode->magic));
return -EINVAL; return -EINVAL;
}
/* /*
* Check which alternatives are present, and "downgrade" * Check which alternatives are present, and "downgrade"
@ -1836,11 +1863,9 @@ static int iwlagn_load_firmware(struct iwl_priv *priv,
len -= sizeof(*ucode); len -= sizeof(*ucode);
while (len >= sizeof(*tlv)) { while (len >= sizeof(*tlv) && !ret) {
u32 tlv_len;
enum iwl_ucode_tlv_type tlv_type;
u16 tlv_alt; u16 tlv_alt;
const u8 *tlv_data; u32 fixed_tlv_size = 4;
len -= sizeof(*tlv); len -= sizeof(*tlv);
tlv = (void *)data; tlv = (void *)data;
@ -1850,8 +1875,11 @@ static int iwlagn_load_firmware(struct iwl_priv *priv,
tlv_alt = le16_to_cpu(tlv->alternative); tlv_alt = le16_to_cpu(tlv->alternative);
tlv_data = tlv->data; tlv_data = tlv->data;
if (len < tlv_len) if (len < tlv_len) {
IWL_ERR(priv, "invalid TLV len: %zd/%u\n",
len, tlv_len);
return -EINVAL; return -EINVAL;
}
len -= ALIGN(tlv_len, 4); len -= ALIGN(tlv_len, 4);
data += sizeof(*tlv) + ALIGN(tlv_len, 4); data += sizeof(*tlv) + ALIGN(tlv_len, 4);
@ -1885,56 +1913,77 @@ static int iwlagn_load_firmware(struct iwl_priv *priv,
pieces->boot_size = tlv_len; pieces->boot_size = tlv_len;
break; break;
case IWL_UCODE_TLV_PROBE_MAX_LEN: case IWL_UCODE_TLV_PROBE_MAX_LEN:
if (tlv_len != 4) if (tlv_len != fixed_tlv_size)
return -EINVAL; ret = -EINVAL;
else
capa->max_probe_length = capa->max_probe_length =
le32_to_cpup((__le32 *)tlv_data); le32_to_cpup((__le32 *)tlv_data);
break; break;
case IWL_UCODE_TLV_INIT_EVTLOG_PTR: case IWL_UCODE_TLV_INIT_EVTLOG_PTR:
if (tlv_len != 4) if (tlv_len != fixed_tlv_size)
return -EINVAL; ret = -EINVAL;
else
pieces->init_evtlog_ptr = pieces->init_evtlog_ptr =
le32_to_cpup((__le32 *)tlv_data); le32_to_cpup((__le32 *)tlv_data);
break; break;
case IWL_UCODE_TLV_INIT_EVTLOG_SIZE: case IWL_UCODE_TLV_INIT_EVTLOG_SIZE:
if (tlv_len != 4) if (tlv_len != fixed_tlv_size)
return -EINVAL; ret = -EINVAL;
else
pieces->init_evtlog_size = pieces->init_evtlog_size =
le32_to_cpup((__le32 *)tlv_data); le32_to_cpup((__le32 *)tlv_data);
break; break;
case IWL_UCODE_TLV_INIT_ERRLOG_PTR: case IWL_UCODE_TLV_INIT_ERRLOG_PTR:
if (tlv_len != 4) if (tlv_len != fixed_tlv_size)
return -EINVAL; ret = -EINVAL;
else
pieces->init_errlog_ptr = pieces->init_errlog_ptr =
le32_to_cpup((__le32 *)tlv_data); le32_to_cpup((__le32 *)tlv_data);
break; break;
case IWL_UCODE_TLV_RUNT_EVTLOG_PTR: case IWL_UCODE_TLV_RUNT_EVTLOG_PTR:
if (tlv_len != 4) if (tlv_len != fixed_tlv_size)
return -EINVAL; ret = -EINVAL;
else
pieces->inst_evtlog_ptr = pieces->inst_evtlog_ptr =
le32_to_cpup((__le32 *)tlv_data); le32_to_cpup((__le32 *)tlv_data);
break; break;
case IWL_UCODE_TLV_RUNT_EVTLOG_SIZE: case IWL_UCODE_TLV_RUNT_EVTLOG_SIZE:
if (tlv_len != 4) if (tlv_len != fixed_tlv_size)
return -EINVAL; ret = -EINVAL;
else
pieces->inst_evtlog_size = pieces->inst_evtlog_size =
le32_to_cpup((__le32 *)tlv_data); le32_to_cpup((__le32 *)tlv_data);
break; break;
case IWL_UCODE_TLV_RUNT_ERRLOG_PTR: case IWL_UCODE_TLV_RUNT_ERRLOG_PTR:
if (tlv_len != 4) if (tlv_len != fixed_tlv_size)
return -EINVAL; ret = -EINVAL;
else
pieces->inst_errlog_ptr = pieces->inst_errlog_ptr =
le32_to_cpup((__le32 *)tlv_data); le32_to_cpup((__le32 *)tlv_data);
break; break;
case IWL_UCODE_TLV_ENHANCE_SENS_TBL:
if (tlv_len)
ret = -EINVAL;
else
priv->enhance_sensitivity_table = true;
break;
default: default:
IWL_WARN(priv, "unknown TLV: %d\n", tlv_type);
break; break;
} }
} }
if (len) if (len) {
return -EINVAL; IWL_ERR(priv, "invalid TLV after parsing: %zd\n", len);
iwl_print_hex_dump(priv, IWL_DL_FW, (u8 *)data, len);
ret = -EINVAL;
} else if (ret) {
IWL_ERR(priv, "TLV %d has invalid size: %u\n",
tlv_type, tlv_len);
iwl_print_hex_dump(priv, IWL_DL_FW, (u8 *)tlv_data, tlv_len);
}
return 0; return ret;
} }
/** /**
@ -2247,17 +2296,41 @@ static const char *desc_lookup_text[] = {
"DEBUG_1", "DEBUG_1",
"DEBUG_2", "DEBUG_2",
"DEBUG_3", "DEBUG_3",
"ADVANCED SYSASSERT"
}; };
static const char *desc_lookup(int i) static struct { char *name; u8 num; } advanced_lookup[] = {
{ "NMI_INTERRUPT_WDG", 0x34 },
{ "SYSASSERT", 0x35 },
{ "UCODE_VERSION_MISMATCH", 0x37 },
{ "BAD_COMMAND", 0x38 },
{ "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C },
{ "FATAL_ERROR", 0x3D },
{ "NMI_TRM_HW_ERR", 0x46 },
{ "NMI_INTERRUPT_TRM", 0x4C },
{ "NMI_INTERRUPT_BREAK_POINT", 0x54 },
{ "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C },
{ "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 },
{ "NMI_INTERRUPT_HOST", 0x66 },
{ "NMI_INTERRUPT_ACTION_PT", 0x7C },
{ "NMI_INTERRUPT_UNKNOWN", 0x84 },
{ "NMI_INTERRUPT_INST_ACTION_PT", 0x86 },
{ "ADVANCED_SYSASSERT", 0 },
};
static const char *desc_lookup(u32 num)
{ {
int max = ARRAY_SIZE(desc_lookup_text) - 1; int i;
int max = ARRAY_SIZE(desc_lookup_text);
if (i < 0 || i > max) if (num < max)
i = max; return desc_lookup_text[num];
return desc_lookup_text[i]; max = ARRAY_SIZE(advanced_lookup) - 1;
for (i = 0; i < max; i++) {
if (advanced_lookup[i].num == num)
break;;
}
return advanced_lookup[i].name;
} }
#define ERROR_START_OFFSET (1 * sizeof(u32)) #define ERROR_START_OFFSET (1 * sizeof(u32))
@ -3614,6 +3687,44 @@ out_exit:
IWL_DEBUG_MAC80211(priv, "leave\n"); IWL_DEBUG_MAC80211(priv, "leave\n");
} }
static void iwl_mac_flush(struct ieee80211_hw *hw, bool drop)
{
struct iwl_priv *priv = hw->priv;
mutex_lock(&priv->mutex);
IWL_DEBUG_MAC80211(priv, "enter\n");
/* do not support "flush" */
if (!priv->cfg->ops->lib->txfifo_flush)
goto done;
if (test_bit(STATUS_EXIT_PENDING, &priv->status)) {
IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n");
goto done;
}
if (iwl_is_rfkill(priv)) {
IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n");
goto done;
}
/*
* mac80211 will not push any more frames for transmit
* until the flush is completed
*/
if (drop) {
IWL_DEBUG_MAC80211(priv, "send flush command\n");
if (priv->cfg->ops->lib->txfifo_flush(priv, IWL_DROP_ALL)) {
IWL_ERR(priv, "flush request fail\n");
goto done;
}
}
IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n");
iwlagn_wait_tx_queue_empty(priv);
done:
mutex_unlock(&priv->mutex);
IWL_DEBUG_MAC80211(priv, "leave\n");
}
/***************************************************************************** /*****************************************************************************
* *
* driver setup and teardown * driver setup and teardown
@ -3630,6 +3741,7 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv)
INIT_WORK(&priv->rx_replenish, iwl_bg_rx_replenish); INIT_WORK(&priv->rx_replenish, iwl_bg_rx_replenish);
INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update); INIT_WORK(&priv->beacon_update, iwl_bg_beacon_update);
INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work); INIT_WORK(&priv->run_time_calib_work, iwl_bg_run_time_calib_work);
INIT_WORK(&priv->tx_flush, iwl_bg_tx_flush);
INIT_DELAYED_WORK(&priv->init_alive_start, iwl_bg_init_alive_start); INIT_DELAYED_WORK(&priv->init_alive_start, iwl_bg_init_alive_start);
INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start); INIT_DELAYED_WORK(&priv->alive_start, iwl_bg_alive_start);
@ -3787,6 +3899,7 @@ static struct ieee80211_ops iwl_hw_ops = {
.sta_add = iwlagn_mac_sta_add, .sta_add = iwlagn_mac_sta_add,
.sta_remove = iwl_mac_sta_remove, .sta_remove = iwl_mac_sta_remove,
.channel_switch = iwl_mac_channel_switch, .channel_switch = iwl_mac_channel_switch,
.flush = iwl_mac_flush,
}; };
static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
@ -4234,6 +4347,14 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
{IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)}, {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)},
{IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)}, {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)},
/* 6x50 WiFi/WiMax Series Gen2 */
{IWL_PCI_DEVICE(0x0885, 0x1305, iwl6050g2_bgn_cfg)},
{IWL_PCI_DEVICE(0x0885, 0x1306, iwl6050g2_bgn_cfg)},
{IWL_PCI_DEVICE(0x0885, 0x1325, iwl6050g2_bgn_cfg)},
{IWL_PCI_DEVICE(0x0885, 0x1326, iwl6050g2_bgn_cfg)},
{IWL_PCI_DEVICE(0x0886, 0x1315, iwl6050g2_bgn_cfg)},
{IWL_PCI_DEVICE(0x0886, 0x1316, iwl6050g2_bgn_cfg)},
/* 1000 Series WiFi */ /* 1000 Series WiFi */
{IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)}, {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)},
{IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)}, {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)},

View File

@ -89,6 +89,7 @@ extern struct iwl_cfg iwl6000i_2bg_cfg;
extern struct iwl_cfg iwl6000_3agn_cfg; extern struct iwl_cfg iwl6000_3agn_cfg;
extern struct iwl_cfg iwl6050_2agn_cfg; extern struct iwl_cfg iwl6050_2agn_cfg;
extern struct iwl_cfg iwl6050_2abg_cfg; extern struct iwl_cfg iwl6050_2abg_cfg;
extern struct iwl_cfg iwl6050g2_bgn_cfg;
extern struct iwl_cfg iwl1000_bgn_cfg; extern struct iwl_cfg iwl1000_bgn_cfg;
extern struct iwl_cfg iwl1000_bg_cfg; extern struct iwl_cfg iwl1000_bg_cfg;
@ -147,6 +148,9 @@ const u8 *iwlagn_eeprom_query_addr(const struct iwl_priv *priv,
void iwlagn_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq); void iwlagn_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq); int iwlagn_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq);
int iwlagn_hw_nic_init(struct iwl_priv *priv); int iwlagn_hw_nic_init(struct iwl_priv *priv);
int iwlagn_wait_tx_queue_empty(struct iwl_priv *priv);
int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control);
void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control);
/* rx */ /* rx */
void iwlagn_rx_queue_restock(struct iwl_priv *priv); void iwlagn_rx_queue_restock(struct iwl_priv *priv);

View File

@ -97,6 +97,7 @@ enum {
REPLY_ADD_STA = 0x18, REPLY_ADD_STA = 0x18,
REPLY_REMOVE_STA = 0x19, REPLY_REMOVE_STA = 0x19,
REPLY_REMOVE_ALL_STA = 0x1a, /* not used */ REPLY_REMOVE_ALL_STA = 0x1a, /* not used */
REPLY_TXFIFO_FLUSH = 0x1e,
/* Security */ /* Security */
REPLY_WEPKEY = 0x20, REPLY_WEPKEY = 0x20,
@ -957,8 +958,8 @@ struct iwl_qosparam_cmd {
#define IWL3945_STATION_COUNT 25 #define IWL3945_STATION_COUNT 25
#define IWL4965_BROADCAST_ID 31 #define IWL4965_BROADCAST_ID 31
#define IWL4965_STATION_COUNT 32 #define IWL4965_STATION_COUNT 32
#define IWL5000_BROADCAST_ID 15 #define IWLAGN_BROADCAST_ID 15
#define IWL5000_STATION_COUNT 16 #define IWLAGN_STATION_COUNT 16
#define IWL_STATION_COUNT 32 /* MAX(3945,4965)*/ #define IWL_STATION_COUNT 32 /* MAX(3945,4965)*/
#define IWL_INVALID_STATION 255 #define IWL_INVALID_STATION 255
@ -1209,6 +1210,43 @@ struct iwl_rem_sta_cmd {
u8 reserved2[2]; u8 reserved2[2];
} __packed; } __packed;
#define IWL_TX_FIFO_BK_MSK cpu_to_le32(BIT(0))
#define IWL_TX_FIFO_BE_MSK cpu_to_le32(BIT(1))
#define IWL_TX_FIFO_VI_MSK cpu_to_le32(BIT(2))
#define IWL_TX_FIFO_VO_MSK cpu_to_le32(BIT(3))
#define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00)
#define IWL_DROP_SINGLE 0
#define IWL_DROP_SELECTED 1
#define IWL_DROP_ALL 2
/*
* REPLY_TXFIFO_FLUSH = 0x1e(command and response)
*
* When using full FIFO flush this command checks the scheduler HW block WR/RD
* pointers to check if all the frames were transferred by DMA into the
* relevant TX FIFO queue. Only when the DMA is finished and the queue is
* empty the command can finish.
* This command is used to flush the TXFIFO from transmit commands, it may
* operate on single or multiple queues, the command queue can't be flushed by
* this command. The command response is returned when all the queue flush
* operations are done. Each TX command flushed return response with the FLUSH
* status set in the TX response status. When FIFO flush operation is used,
* the flush operation ends when both the scheduler DMA done and TXFIFO empty
* are set.
*
* @fifo_control: bit mask for which queues to flush
* @flush_control: flush controls
* 0: Dump single MSDU
* 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable.
* 2: Dump all FIFO
*/
struct iwl_txfifo_flush_cmd {
__le32 fifo_control;
__le16 flush_control;
__le16 reserved;
} __attribute__ ((packed));
/* /*
* REPLY_WEP_KEY = 0x20 * REPLY_WEP_KEY = 0x20
*/ */
@ -3452,6 +3490,41 @@ struct iwl_missed_beacon_notif {
#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9) #define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9)
#define HD_OFDM_ENERGY_TH_IN_INDEX (10) #define HD_OFDM_ENERGY_TH_IN_INDEX (10)
/*
* Additional table entries in enhance SENSITIVITY_CMD
*/
#define HD_INA_NON_SQUARE_DET_OFDM_INDEX (11)
#define HD_INA_NON_SQUARE_DET_CCK_INDEX (12)
#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_INDEX (13)
#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_INDEX (14)
#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (15)
#define HD_OFDM_NON_SQUARE_DET_SLOPE_INDEX (16)
#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_INDEX (17)
#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_INDEX (18)
#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_INDEX (19)
#define HD_CCK_NON_SQUARE_DET_SLOPE_INDEX (20)
#define HD_CCK_NON_SQUARE_DET_INTERCEPT_INDEX (21)
#define HD_RESERVED (22)
/* number of entries for enhanced tbl */
#define ENHANCE_HD_TABLE_SIZE (23)
/* number of additional entries for enhanced tbl */
#define ENHANCE_HD_TABLE_ENTRIES (ENHANCE_HD_TABLE_SIZE - HD_TABLE_SIZE)
#define HD_INA_NON_SQUARE_DET_OFDM_DATA cpu_to_le16(0)
#define HD_INA_NON_SQUARE_DET_CCK_DATA cpu_to_le16(0)
#define HD_CORR_11_INSTEAD_OF_CORR_9_EN_DATA cpu_to_le16(0)
#define HD_OFDM_NON_SQUARE_DET_SLOPE_MRC_DATA cpu_to_le16(668)
#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_MRC_DATA cpu_to_le16(4)
#define HD_OFDM_NON_SQUARE_DET_SLOPE_DATA cpu_to_le16(486)
#define HD_OFDM_NON_SQUARE_DET_INTERCEPT_DATA cpu_to_le16(37)
#define HD_CCK_NON_SQUARE_DET_SLOPE_MRC_DATA cpu_to_le16(853)
#define HD_CCK_NON_SQUARE_DET_INTERCEPT_MRC_DATA cpu_to_le16(4)
#define HD_CCK_NON_SQUARE_DET_SLOPE_DATA cpu_to_le16(476)
#define HD_CCK_NON_SQUARE_DET_INTERCEPT_DATA cpu_to_le16(99)
/* Control field in struct iwl_sensitivity_cmd */ /* Control field in struct iwl_sensitivity_cmd */
#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0) #define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0)
#define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1) #define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1)
@ -3468,6 +3541,14 @@ struct iwl_sensitivity_cmd {
__le16 table[HD_TABLE_SIZE]; /* use HD_* as index */ __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */
} __packed; } __packed;
/*
*
*/
struct iwl_enhance_sensitivity_cmd {
__le16 control; /* always use "1" */
__le16 enhance_table[ENHANCE_HD_TABLE_SIZE]; /* use HD_* as index */
} __attribute__ ((packed));
/** /**
* REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response) * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response)

View File

@ -2627,7 +2627,7 @@ static void iwl_force_rf_reset(struct iwl_priv *priv)
} }
int iwl_force_reset(struct iwl_priv *priv, int mode) int iwl_force_reset(struct iwl_priv *priv, int mode, bool external)
{ {
struct iwl_force_reset *force_reset; struct iwl_force_reset *force_reset;
@ -2640,6 +2640,7 @@ int iwl_force_reset(struct iwl_priv *priv, int mode)
} }
force_reset = &priv->force_reset[mode]; force_reset = &priv->force_reset[mode];
force_reset->reset_request_count++; force_reset->reset_request_count++;
if (!external) {
if (force_reset->last_force_reset_jiffies && if (force_reset->last_force_reset_jiffies &&
time_after(force_reset->last_force_reset_jiffies + time_after(force_reset->last_force_reset_jiffies +
force_reset->reset_duration, jiffies)) { force_reset->reset_duration, jiffies)) {
@ -2647,6 +2648,7 @@ int iwl_force_reset(struct iwl_priv *priv, int mode)
force_reset->reset_reject_count++; force_reset->reset_reject_count++;
return -EAGAIN; return -EAGAIN;
} }
}
force_reset->reset_success_count++; force_reset->reset_success_count++;
force_reset->last_force_reset_jiffies = jiffies; force_reset->last_force_reset_jiffies = jiffies;
IWL_DEBUG_INFO(priv, "perform force reset (%d)\n", mode); IWL_DEBUG_INFO(priv, "perform force reset (%d)\n", mode);
@ -2655,6 +2657,19 @@ int iwl_force_reset(struct iwl_priv *priv, int mode)
iwl_force_rf_reset(priv); iwl_force_rf_reset(priv);
break; break;
case IWL_FW_RESET: case IWL_FW_RESET:
/*
* if the request is from external(ex: debugfs),
* then always perform the request in regardless the module
* parameter setting
* if the request is from internal (uCode error or driver
* detect failure), then fw_restart module parameter
* need to be check before performing firmware reload
*/
if (!external && !priv->cfg->mod_params->restart_fw) {
IWL_DEBUG_INFO(priv, "Cancel firmware reload based on "
"module parameter setting\n");
break;
}
IWL_ERR(priv, "On demand firmware reload\n"); IWL_ERR(priv, "On demand firmware reload\n");
/* Set the FW error flag -- cleared on iwl_down */ /* Set the FW error flag -- cleared on iwl_down */
set_bit(STATUS_FW_ERROR, &priv->status); set_bit(STATUS_FW_ERROR, &priv->status);
@ -2713,7 +2728,7 @@ static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt)
"queue %d stuck %d time. Fw reload.\n", "queue %d stuck %d time. Fw reload.\n",
q->id, q->repeat_same_read_ptr); q->id, q->repeat_same_read_ptr);
q->repeat_same_read_ptr = 0; q->repeat_same_read_ptr = 0;
iwl_force_reset(priv, IWL_FW_RESET); iwl_force_reset(priv, IWL_FW_RESET, false);
} else { } else {
q->repeat_same_read_ptr++; q->repeat_same_read_ptr++;
IWL_DEBUG_RADIO(priv, IWL_DEBUG_RADIO(priv,

View File

@ -205,6 +205,9 @@ struct iwl_lib_ops {
/* check for ack health */ /* check for ack health */
bool (*check_ack_health)(struct iwl_priv *priv, bool (*check_ack_health)(struct iwl_priv *priv,
struct iwl_rx_packet *pkt); struct iwl_rx_packet *pkt);
int (*txfifo_flush)(struct iwl_priv *priv, u16 flush_control);
void (*dev_txfifo_flush)(struct iwl_priv *priv, u16 flush_control);
struct iwl_debugfs_ops debugfs_ops; struct iwl_debugfs_ops debugfs_ops;
}; };
@ -525,7 +528,7 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw,
struct cfg80211_scan_request *req); struct cfg80211_scan_request *req);
void iwl_bg_start_internal_scan(struct work_struct *work); void iwl_bg_start_internal_scan(struct work_struct *work);
void iwl_internal_short_hw_scan(struct iwl_priv *priv); void iwl_internal_short_hw_scan(struct iwl_priv *priv);
int iwl_force_reset(struct iwl_priv *priv, int mode); int iwl_force_reset(struct iwl_priv *priv, int mode, bool external);
u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
const u8 *ta, const u8 *ie, int ie_len, int left); const u8 *ta, const u8 *ie, int ie_len, int left);
void iwl_setup_rx_scan_handlers(struct iwl_priv *priv); void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);

View File

@ -298,6 +298,7 @@
#define CSR_HW_REV_TYPE_1000 (0x0000060) #define CSR_HW_REV_TYPE_1000 (0x0000060)
#define CSR_HW_REV_TYPE_6x00 (0x0000070) #define CSR_HW_REV_TYPE_6x00 (0x0000070)
#define CSR_HW_REV_TYPE_6x50 (0x0000080) #define CSR_HW_REV_TYPE_6x50 (0x0000080)
#define CSR_HW_REV_TYPE_6x50g2 (0x0000084)
#define CSR_HW_REV_TYPE_6x00g2 (0x00000B0) #define CSR_HW_REV_TYPE_6x00g2 (0x00000B0)
#define CSR_HW_REV_TYPE_NONE (0x00000F0) #define CSR_HW_REV_TYPE_NONE (0x00000F0)

View File

@ -1487,7 +1487,7 @@ static ssize_t iwl_dbgfs_force_reset_write(struct file *file,
switch (reset) { switch (reset) {
case IWL_RF_RESET: case IWL_RF_RESET:
case IWL_FW_RESET: case IWL_FW_RESET:
ret = iwl_force_reset(priv, reset); ret = iwl_force_reset(priv, reset, true);
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -1495,6 +1495,30 @@ static ssize_t iwl_dbgfs_force_reset_write(struct file *file,
return ret ? ret : count; return ret ? ret : count;
} }
static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos) {
struct iwl_priv *priv = file->private_data;
char buf[8];
int buf_size;
int flush;
memset(buf, 0, sizeof(buf));
buf_size = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
if (sscanf(buf, "%d", &flush) != 1)
return -EINVAL;
if (iwl_is_rfkill(priv))
return -EFAULT;
priv->cfg->ops->lib->dev_txfifo_flush(priv, IWL_DROP_ALL);
return count;
}
DEBUGFS_READ_FILE_OPS(rx_statistics); DEBUGFS_READ_FILE_OPS(rx_statistics);
DEBUGFS_READ_FILE_OPS(tx_statistics); DEBUGFS_READ_FILE_OPS(tx_statistics);
DEBUGFS_READ_WRITE_FILE_OPS(traffic_log); DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
@ -1516,6 +1540,7 @@ DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta);
DEBUGFS_READ_WRITE_FILE_OPS(force_reset); DEBUGFS_READ_WRITE_FILE_OPS(force_reset);
DEBUGFS_READ_FILE_OPS(rxon_flags); DEBUGFS_READ_FILE_OPS(rxon_flags);
DEBUGFS_READ_FILE_OPS(rxon_filter_flags); DEBUGFS_READ_FILE_OPS(rxon_filter_flags);
DEBUGFS_WRITE_FILE_OPS(txfifo_flush);
/* /*
* Create the debugfs files and directories * Create the debugfs files and directories
@ -1574,6 +1599,8 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR);
DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR);
if (priv->cfg->ops->lib->dev_txfifo_flush)
DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR);
if (priv->cfg->sensitivity_calib_by_driver) if (priv->cfg->sensitivity_calib_by_driver)
DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR);

View File

@ -570,6 +570,7 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_INIT_EVTLOG_PTR = 11, IWL_UCODE_TLV_INIT_EVTLOG_PTR = 11,
IWL_UCODE_TLV_INIT_EVTLOG_SIZE = 12, IWL_UCODE_TLV_INIT_EVTLOG_SIZE = 12,
IWL_UCODE_TLV_INIT_ERRLOG_PTR = 13, IWL_UCODE_TLV_INIT_ERRLOG_PTR = 13,
IWL_UCODE_TLV_ENHANCE_SENS_TBL = 14,
}; };
struct iwl_ucode_tlv { struct iwl_ucode_tlv {
@ -1193,7 +1194,9 @@ struct iwl_priv {
u8 start_calib; u8 start_calib;
struct iwl_sensitivity_data sensitivity_data; struct iwl_sensitivity_data sensitivity_data;
struct iwl_chain_noise_data chain_noise_data; struct iwl_chain_noise_data chain_noise_data;
bool enhance_sensitivity_table;
__le16 sensitivity_tbl[HD_TABLE_SIZE]; __le16 sensitivity_tbl[HD_TABLE_SIZE];
__le16 enhance_sensitivity_tbl[ENHANCE_HD_TABLE_ENTRIES];
struct iwl_ht_config current_ht_config; struct iwl_ht_config current_ht_config;
@ -1345,6 +1348,7 @@ struct iwl_priv {
struct work_struct ct_enter; struct work_struct ct_enter;
struct work_struct ct_exit; struct work_struct ct_exit;
struct work_struct start_internal_scan; struct work_struct start_internal_scan;
struct work_struct tx_flush;
struct tasklet_struct irq_tasklet; struct tasklet_struct irq_tasklet;

View File

@ -276,6 +276,10 @@ struct iwl_eeprom_enhanced_txpwr {
#define EEPROM_6050_TX_POWER_VERSION (4) #define EEPROM_6050_TX_POWER_VERSION (4)
#define EEPROM_6050_EEPROM_VERSION (0x532) #define EEPROM_6050_EEPROM_VERSION (0x532)
/* 6x50g2 Specific */
#define EEPROM_6050G2_TX_POWER_VERSION (6)
#define EEPROM_6050G2_EEPROM_VERSION (0x553)
/* 6x00g2 Specific */ /* 6x00g2 Specific */
#define EEPROM_6000G2_TX_POWER_VERSION (6) #define EEPROM_6000G2_TX_POWER_VERSION (6)
#define EEPROM_6000G2_EEPROM_VERSION (0x709) #define EEPROM_6000G2_EEPROM_VERSION (0x709)

View File

@ -398,12 +398,7 @@
*/ */
#define FH_TSSR_TX_ERROR_REG (FH_TSSR_LOWER_BOUND + 0x018) #define FH_TSSR_TX_ERROR_REG (FH_TSSR_LOWER_BOUND + 0x018)
#define FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_chnl) ((1 << (_chnl)) << 24) #define FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16)
#define FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_chnl) ((1 << (_chnl)) << 16)
#define FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) \
(FH_TSSR_TX_STATUS_REG_BIT_BUFS_EMPTY(_chnl) | \
FH_TSSR_TX_STATUS_REG_BIT_NO_PEND_REQ(_chnl))
/* Tx service channels */ /* Tx service channels */
#define FH_SRVC_CHNL (9) #define FH_SRVC_CHNL (9)

View File

@ -49,6 +49,7 @@ const char *get_cmd_string(u8 cmd)
IWL_CMD(REPLY_ADD_STA); IWL_CMD(REPLY_ADD_STA);
IWL_CMD(REPLY_REMOVE_STA); IWL_CMD(REPLY_REMOVE_STA);
IWL_CMD(REPLY_REMOVE_ALL_STA); IWL_CMD(REPLY_REMOVE_ALL_STA);
IWL_CMD(REPLY_TXFIFO_FLUSH);
IWL_CMD(REPLY_WEPKEY); IWL_CMD(REPLY_WEPKEY);
IWL_CMD(REPLY_3945_RX); IWL_CMD(REPLY_3945_RX);
IWL_CMD(REPLY_TX); IWL_CMD(REPLY_TX);

View File

@ -238,7 +238,7 @@ void iwl_recover_from_statistics(struct iwl_priv *priv,
*/ */
IWL_ERR(priv, "low ack count detected, " IWL_ERR(priv, "low ack count detected, "
"restart firmware\n"); "restart firmware\n");
if (!iwl_force_reset(priv, IWL_FW_RESET)) if (!iwl_force_reset(priv, IWL_FW_RESET, false))
return; return;
} }
} }
@ -249,7 +249,7 @@ void iwl_recover_from_statistics(struct iwl_priv *priv,
* high plcp error detected * high plcp error detected
* reset Radio * reset Radio
*/ */
iwl_force_reset(priv, IWL_RF_RESET); iwl_force_reset(priv, IWL_RF_RESET, false);
} }
} }
} }

View File

@ -98,6 +98,17 @@ static inline void iwl_clear_driver_stations(struct iwl_priv *priv)
spin_lock_irqsave(&priv->sta_lock, flags); spin_lock_irqsave(&priv->sta_lock, flags);
memset(priv->stations, 0, sizeof(priv->stations)); memset(priv->stations, 0, sizeof(priv->stations));
priv->num_stations = 0; priv->num_stations = 0;
/*
* Remove all key information that is not stored as part of station
* information since mac80211 may not have had a
* chance to remove all the keys. When device is reconfigured by
* mac80211 after an error all keys will be reconfigured.
*/
priv->ucode_key_table = 0;
priv->key_mapping_key = 0;
memset(priv->wep_keys, 0, sizeof(priv->wep_keys));
spin_unlock_irqrestore(&priv->sta_lock, flags); spin_unlock_irqrestore(&priv->sta_lock, flags);
} }

View File

@ -226,6 +226,18 @@ setuserscan
All entries in the scan table (not just the new scan data when keep=1) All entries in the scan table (not just the new scan data when keep=1)
will be displayed upon completion by use of the getscantable ioctl. will be displayed upon completion by use of the getscantable ioctl.
hostsleep
This command is used to enable/disable host sleep.
Note: Host sleep parameters should be configured using
"ethtool -s ethX wol X" command before enabling host sleep.
Path: /sys/kernel/debug/libertas_wireless/ethX/
Usage:
cat hostsleep: reads the current hostsleep state
echo "1" > hostsleep : enable host sleep.
echo "0" > hostsleep : disable host sleep
======================== ========================
IWCONFIG COMMANDS IWCONFIG COMMANDS
======================== ========================

View File

@ -181,7 +181,7 @@ static int lbs_ret_host_sleep_cfg(struct lbs_private *priv, unsigned long dummy,
struct cmd_header *resp) struct cmd_header *resp)
{ {
lbs_deb_enter(LBS_DEB_CMD); lbs_deb_enter(LBS_DEB_CMD);
if (priv->wol_criteria == EHS_REMOVE_WAKEUP) { if (priv->is_host_sleep_activated) {
priv->is_host_sleep_configured = 0; priv->is_host_sleep_configured = 0;
if (priv->psstate == PS_STATE_FULL_POWER) { if (priv->psstate == PS_STATE_FULL_POWER) {
priv->is_host_sleep_activated = 0; priv->is_host_sleep_activated = 0;
@ -361,6 +361,65 @@ int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
return ret; return ret;
} }
static int lbs_ret_host_sleep_activate(struct lbs_private *priv,
unsigned long dummy,
struct cmd_header *cmd)
{
lbs_deb_enter(LBS_DEB_FW);
priv->is_host_sleep_activated = 1;
wake_up_interruptible(&priv->host_sleep_q);
lbs_deb_leave(LBS_DEB_FW);
return 0;
}
int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep)
{
struct cmd_header cmd;
int ret = 0;
uint32_t criteria = EHS_REMOVE_WAKEUP;
lbs_deb_enter(LBS_DEB_CMD);
if (host_sleep) {
if (priv->is_host_sleep_activated != 1) {
memset(&cmd, 0, sizeof(cmd));
ret = lbs_host_sleep_cfg(priv, priv->wol_criteria,
(struct wol_config *)NULL);
if (ret) {
lbs_pr_info("Host sleep configuration failed: "
"%d\n", ret);
return ret;
}
if (priv->psstate == PS_STATE_FULL_POWER) {
ret = __lbs_cmd(priv,
CMD_802_11_HOST_SLEEP_ACTIVATE,
&cmd,
sizeof(cmd),
lbs_ret_host_sleep_activate, 0);
if (ret)
lbs_pr_info("HOST_SLEEP_ACTIVATE "
"failed: %d\n", ret);
}
if (!wait_event_interruptible_timeout(
priv->host_sleep_q,
priv->is_host_sleep_activated,
(10 * HZ))) {
lbs_pr_err("host_sleep_q: timer expired\n");
ret = -1;
}
} else {
lbs_pr_err("host sleep: already enabled\n");
}
} else {
if (priv->is_host_sleep_activated)
ret = lbs_host_sleep_cfg(priv, criteria,
(struct wol_config *)NULL);
}
return ret;
}
/** /**
* @brief Set an SNMP MIB value * @brief Set an SNMP MIB value
* *

View File

@ -127,4 +127,6 @@ int lbs_set_tx_power(struct lbs_private *priv, s16 dbm);
int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep); int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep);
int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep);
#endif /* _LBS_CMD_H */ #endif /* _LBS_CMD_H */

View File

@ -124,6 +124,70 @@ out_unlock:
return ret; return ret;
} }
static ssize_t lbs_host_sleep_write(struct file *file,
const char __user *user_buf, size_t count,
loff_t *ppos)
{
struct lbs_private *priv = file->private_data;
ssize_t buf_size, ret;
int host_sleep;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
if (!buf)
return -ENOMEM;
buf_size = min(count, len - 1);
if (copy_from_user(buf, user_buf, buf_size)) {
ret = -EFAULT;
goto out_unlock;
}
ret = sscanf(buf, "%d", &host_sleep);
if (ret != 1) {
ret = -EINVAL;
goto out_unlock;
}
if (host_sleep == 0)
ret = lbs_set_host_sleep(priv, 0);
else if (host_sleep == 1) {
if (priv->wol_criteria == EHS_REMOVE_WAKEUP) {
lbs_pr_info("wake parameters not configured");
ret = -EINVAL;
goto out_unlock;
}
ret = lbs_set_host_sleep(priv, 1);
} else {
lbs_pr_err("invalid option\n");
ret = -EINVAL;
}
if (!ret)
ret = count;
out_unlock:
free_page(addr);
return ret;
}
static ssize_t lbs_host_sleep_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
struct lbs_private *priv = file->private_data;
ssize_t ret;
size_t pos = 0;
unsigned long addr = get_zeroed_page(GFP_KERNEL);
char *buf = (char *)addr;
if (!buf)
return -ENOMEM;
pos += snprintf(buf, len, "%d\n", priv->is_host_sleep_activated);
ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
free_page(addr);
return ret;
}
/* /*
* When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might
* get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the
@ -675,6 +739,8 @@ static const struct lbs_debugfs_files debugfs_files[] = {
{ "info", 0444, FOPS(lbs_dev_info, write_file_dummy), }, { "info", 0444, FOPS(lbs_dev_info, write_file_dummy), },
{ "sleepparams", 0644, FOPS(lbs_sleepparams_read, { "sleepparams", 0644, FOPS(lbs_sleepparams_read,
lbs_sleepparams_write), }, lbs_sleepparams_write), },
{ "hostsleep", 0644, FOPS(lbs_host_sleep_read,
lbs_host_sleep_write), },
}; };
static const struct lbs_debugfs_files debugfs_events_files[] = { static const struct lbs_debugfs_files debugfs_events_files[] = {

View File

@ -544,20 +544,8 @@ static int lbs_thread(void *data)
return 0; return 0;
} }
static int lbs_ret_host_sleep_activate(struct lbs_private *priv,
unsigned long dummy,
struct cmd_header *cmd)
{
lbs_deb_enter(LBS_DEB_FW);
priv->is_host_sleep_activated = 1;
wake_up_interruptible(&priv->host_sleep_q);
lbs_deb_leave(LBS_DEB_FW);
return 0;
}
int lbs_suspend(struct lbs_private *priv) int lbs_suspend(struct lbs_private *priv)
{ {
struct cmd_header cmd;
int ret; int ret;
lbs_deb_enter(LBS_DEB_FW); lbs_deb_enter(LBS_DEB_FW);
@ -571,25 +559,8 @@ int lbs_suspend(struct lbs_private *priv)
priv->deep_sleep_required = 1; priv->deep_sleep_required = 1;
} }
memset(&cmd, 0, sizeof(cmd)); ret = lbs_set_host_sleep(priv, 1);
ret = lbs_host_sleep_cfg(priv, priv->wol_criteria,
(struct wol_config *)NULL);
if (ret) {
lbs_pr_info("Host sleep configuration failed: %d\n", ret);
return ret;
}
if (priv->psstate == PS_STATE_FULL_POWER) {
ret = __lbs_cmd(priv, CMD_802_11_HOST_SLEEP_ACTIVATE, &cmd,
sizeof(cmd), lbs_ret_host_sleep_activate, 0);
if (ret)
lbs_pr_info("HOST_SLEEP_ACTIVATE failed: %d\n", ret);
}
if (!wait_event_interruptible_timeout(priv->host_sleep_q,
priv->is_host_sleep_activated, (10 * HZ))) {
lbs_pr_err("host_sleep_q: timer expired\n");
ret = -1;
}
netif_device_detach(priv->dev); netif_device_detach(priv->dev);
if (priv->mesh_dev) if (priv->mesh_dev)
netif_device_detach(priv->mesh_dev); netif_device_detach(priv->mesh_dev);
@ -602,11 +573,10 @@ EXPORT_SYMBOL_GPL(lbs_suspend);
int lbs_resume(struct lbs_private *priv) int lbs_resume(struct lbs_private *priv)
{ {
int ret; int ret;
uint32_t criteria = EHS_REMOVE_WAKEUP;
lbs_deb_enter(LBS_DEB_FW); lbs_deb_enter(LBS_DEB_FW);
ret = lbs_host_sleep_cfg(priv, criteria, (struct wol_config *)NULL); ret = lbs_set_host_sleep(priv, 0);
netif_device_attach(priv->dev); netif_device_attach(priv->dev);
if (priv->mesh_dev) if (priv->mesh_dev)

View File

@ -2067,7 +2067,7 @@ send_simple_event(islpci_private *priv, const char *str)
memptr = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL); memptr = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
if (!memptr) if (!memptr)
return; return;
BUG_ON(n > IW_CUSTOM_MAX); BUG_ON(n >= IW_CUSTOM_MAX);
wrqu.data.pointer = memptr; wrqu.data.pointer = memptr;
wrqu.data.length = n; wrqu.data.length = n;
strcpy(memptr, str); strcpy(memptr, str);

View File

@ -586,9 +586,11 @@ static void rt2400pci_link_stats(struct rt2x00_dev *rt2x00dev,
static inline void rt2400pci_set_vgc(struct rt2x00_dev *rt2x00dev, static inline void rt2400pci_set_vgc(struct rt2x00_dev *rt2x00dev,
struct link_qual *qual, u8 vgc_level) struct link_qual *qual, u8 vgc_level)
{ {
if (qual->vgc_level_reg != vgc_level) {
rt2400pci_bbp_write(rt2x00dev, 13, vgc_level); rt2400pci_bbp_write(rt2x00dev, 13, vgc_level);
qual->vgc_level = vgc_level; qual->vgc_level = vgc_level;
qual->vgc_level_reg = vgc_level; qual->vgc_level_reg = vgc_level;
}
} }
static void rt2400pci_reset_tuner(struct rt2x00_dev *rt2x00dev, static void rt2400pci_reset_tuner(struct rt2x00_dev *rt2x00dev,
@ -877,7 +879,8 @@ static void rt2400pci_toggle_rx(struct rt2x00_dev *rt2x00dev,
static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev, static void rt2400pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
enum dev_state state) enum dev_state state)
{ {
int mask = (state == STATE_RADIO_IRQ_OFF); int mask = (state == STATE_RADIO_IRQ_OFF) ||
(state == STATE_RADIO_IRQ_OFF_ISR);
u32 reg; u32 reg;
/* /*
@ -978,7 +981,9 @@ static int rt2400pci_set_device_state(struct rt2x00_dev *rt2x00dev,
rt2400pci_toggle_rx(rt2x00dev, state); rt2400pci_toggle_rx(rt2x00dev, state);
break; break;
case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_ON:
case STATE_RADIO_IRQ_ON_ISR:
case STATE_RADIO_IRQ_OFF: case STATE_RADIO_IRQ_OFF:
case STATE_RADIO_IRQ_OFF_ISR:
rt2400pci_toggle_irq(rt2x00dev, state); rt2400pci_toggle_irq(rt2x00dev, state);
break; break;
case STATE_DEEP_SLEEP: case STATE_DEEP_SLEEP:
@ -1233,23 +1238,10 @@ static void rt2400pci_txdone(struct rt2x00_dev *rt2x00dev,
} }
} }
static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance) static irqreturn_t rt2400pci_interrupt_thread(int irq, void *dev_instance)
{ {
struct rt2x00_dev *rt2x00dev = dev_instance; struct rt2x00_dev *rt2x00dev = dev_instance;
u32 reg; u32 reg = rt2x00dev->irqvalue[0];
/*
* Get the interrupt sources & saved to local variable.
* Write register value back to clear pending interrupts.
*/
rt2x00pci_register_read(rt2x00dev, CSR7, &reg);
rt2x00pci_register_write(rt2x00dev, CSR7, reg);
if (!reg)
return IRQ_NONE;
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return IRQ_HANDLED;
/* /*
* Handle interrupts, walk through all bits * Handle interrupts, walk through all bits
@ -1287,9 +1279,40 @@ static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance)
if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING))
rt2400pci_txdone(rt2x00dev, QID_AC_BK); rt2400pci_txdone(rt2x00dev, QID_AC_BK);
/* Enable interrupts again. */
rt2x00dev->ops->lib->set_device_state(rt2x00dev,
STATE_RADIO_IRQ_ON_ISR);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t rt2400pci_interrupt(int irq, void *dev_instance)
{
struct rt2x00_dev *rt2x00dev = dev_instance;
u32 reg;
/*
* Get the interrupt sources & saved to local variable.
* Write register value back to clear pending interrupts.
*/
rt2x00pci_register_read(rt2x00dev, CSR7, &reg);
rt2x00pci_register_write(rt2x00dev, CSR7, reg);
if (!reg)
return IRQ_NONE;
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return IRQ_HANDLED;
/* Store irqvalues for use in the interrupt thread. */
rt2x00dev->irqvalue[0] = reg;
/* Disable interrupts, will be enabled again in the interrupt thread. */
rt2x00dev->ops->lib->set_device_state(rt2x00dev,
STATE_RADIO_IRQ_OFF_ISR);
return IRQ_WAKE_THREAD;
}
/* /*
* Device probe functions. * Device probe functions.
*/ */
@ -1399,8 +1422,8 @@ static int rt2400pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
/* /*
* Check if the BBP tuning should be enabled. * Check if the BBP tuning should be enabled.
*/ */
if (!rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_AGCVGC_TUNING)) if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_RX_AGCVGC_TUNING))
__set_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags); __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
return 0; return 0;
} }
@ -1566,7 +1589,8 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = {
.remove_interface = rt2x00mac_remove_interface, .remove_interface = rt2x00mac_remove_interface,
.config = rt2x00mac_config, .config = rt2x00mac_config,
.configure_filter = rt2x00mac_configure_filter, .configure_filter = rt2x00mac_configure_filter,
.set_tim = rt2x00mac_set_tim, .sw_scan_start = rt2x00mac_sw_scan_start,
.sw_scan_complete = rt2x00mac_sw_scan_complete,
.get_stats = rt2x00mac_get_stats, .get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed, .bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2400pci_conf_tx, .conf_tx = rt2400pci_conf_tx,
@ -1577,6 +1601,7 @@ static const struct ieee80211_ops rt2400pci_mac80211_ops = {
static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = { static const struct rt2x00lib_ops rt2400pci_rt2x00_ops = {
.irq_handler = rt2400pci_interrupt, .irq_handler = rt2400pci_interrupt,
.irq_handler_thread = rt2400pci_interrupt_thread,
.probe_hw = rt2400pci_probe_hw, .probe_hw = rt2400pci_probe_hw,
.initialize = rt2x00pci_initialize, .initialize = rt2x00pci_initialize,
.uninitialize = rt2x00pci_uninitialize, .uninitialize = rt2x00pci_uninitialize,

View File

@ -626,6 +626,7 @@ static inline void rt2500pci_set_vgc(struct rt2x00_dev *rt2x00dev,
{ {
if (qual->vgc_level_reg != vgc_level) { if (qual->vgc_level_reg != vgc_level) {
rt2500pci_bbp_write(rt2x00dev, 17, vgc_level); rt2500pci_bbp_write(rt2x00dev, 17, vgc_level);
qual->vgc_level = vgc_level;
qual->vgc_level_reg = vgc_level; qual->vgc_level_reg = vgc_level;
} }
} }
@ -700,13 +701,10 @@ dynamic_cca_tune:
* R17 is inside the dynamic tuning range, * R17 is inside the dynamic tuning range,
* start tuning the link based on the false cca counter. * start tuning the link based on the false cca counter.
*/ */
if (qual->false_cca > 512 && qual->vgc_level_reg < 0x40) { if (qual->false_cca > 512 && qual->vgc_level_reg < 0x40)
rt2500pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level_reg); rt2500pci_set_vgc(rt2x00dev, qual, ++qual->vgc_level_reg);
qual->vgc_level = qual->vgc_level_reg; else if (qual->false_cca < 100 && qual->vgc_level_reg > 0x32)
} else if (qual->false_cca < 100 && qual->vgc_level_reg > 0x32) {
rt2500pci_set_vgc(rt2x00dev, qual, --qual->vgc_level_reg); rt2500pci_set_vgc(rt2x00dev, qual, --qual->vgc_level_reg);
qual->vgc_level = qual->vgc_level_reg;
}
} }
/* /*
@ -1035,7 +1033,8 @@ static void rt2500pci_toggle_rx(struct rt2x00_dev *rt2x00dev,
static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev, static void rt2500pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
enum dev_state state) enum dev_state state)
{ {
int mask = (state == STATE_RADIO_IRQ_OFF); int mask = (state == STATE_RADIO_IRQ_OFF) ||
(state == STATE_RADIO_IRQ_OFF_ISR);
u32 reg; u32 reg;
/* /*
@ -1136,7 +1135,9 @@ static int rt2500pci_set_device_state(struct rt2x00_dev *rt2x00dev,
rt2500pci_toggle_rx(rt2x00dev, state); rt2500pci_toggle_rx(rt2x00dev, state);
break; break;
case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_ON:
case STATE_RADIO_IRQ_ON_ISR:
case STATE_RADIO_IRQ_OFF: case STATE_RADIO_IRQ_OFF:
case STATE_RADIO_IRQ_OFF_ISR:
rt2500pci_toggle_irq(rt2x00dev, state); rt2500pci_toggle_irq(rt2x00dev, state);
break; break;
case STATE_DEEP_SLEEP: case STATE_DEEP_SLEEP:
@ -1369,23 +1370,10 @@ static void rt2500pci_txdone(struct rt2x00_dev *rt2x00dev,
} }
} }
static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance) static irqreturn_t rt2500pci_interrupt_thread(int irq, void *dev_instance)
{ {
struct rt2x00_dev *rt2x00dev = dev_instance; struct rt2x00_dev *rt2x00dev = dev_instance;
u32 reg; u32 reg = rt2x00dev->irqvalue[0];
/*
* Get the interrupt sources & saved to local variable.
* Write register value back to clear pending interrupts.
*/
rt2x00pci_register_read(rt2x00dev, CSR7, &reg);
rt2x00pci_register_write(rt2x00dev, CSR7, reg);
if (!reg)
return IRQ_NONE;
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return IRQ_HANDLED;
/* /*
* Handle interrupts, walk through all bits * Handle interrupts, walk through all bits
@ -1423,9 +1411,41 @@ static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance)
if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING)) if (rt2x00_get_field32(reg, CSR7_TXDONE_TXRING))
rt2500pci_txdone(rt2x00dev, QID_AC_BK); rt2500pci_txdone(rt2x00dev, QID_AC_BK);
/* Enable interrupts again. */
rt2x00dev->ops->lib->set_device_state(rt2x00dev,
STATE_RADIO_IRQ_ON_ISR);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t rt2500pci_interrupt(int irq, void *dev_instance)
{
struct rt2x00_dev *rt2x00dev = dev_instance;
u32 reg;
/*
* Get the interrupt sources & saved to local variable.
* Write register value back to clear pending interrupts.
*/
rt2x00pci_register_read(rt2x00dev, CSR7, &reg);
rt2x00pci_register_write(rt2x00dev, CSR7, reg);
if (!reg)
return IRQ_NONE;
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return IRQ_HANDLED;
/* Store irqvalues for use in the interrupt thread. */
rt2x00dev->irqvalue[0] = reg;
/* Disable interrupts, will be enabled again in the interrupt thread. */
rt2x00dev->ops->lib->set_device_state(rt2x00dev,
STATE_RADIO_IRQ_OFF_ISR);
return IRQ_WAKE_THREAD;
}
/* /*
* Device probe functions. * Device probe functions.
*/ */
@ -1557,9 +1577,8 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev)
* Check if the BBP tuning should be enabled. * Check if the BBP tuning should be enabled.
*/ */
rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom); rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom);
if (!rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE))
if (rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE)) __set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
__set_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags);
/* /*
* Read the RSSI <-> dBm offset information. * Read the RSSI <-> dBm offset information.
@ -1864,7 +1883,8 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = {
.remove_interface = rt2x00mac_remove_interface, .remove_interface = rt2x00mac_remove_interface,
.config = rt2x00mac_config, .config = rt2x00mac_config,
.configure_filter = rt2x00mac_configure_filter, .configure_filter = rt2x00mac_configure_filter,
.set_tim = rt2x00mac_set_tim, .sw_scan_start = rt2x00mac_sw_scan_start,
.sw_scan_complete = rt2x00mac_sw_scan_complete,
.get_stats = rt2x00mac_get_stats, .get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed, .bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2x00mac_conf_tx, .conf_tx = rt2x00mac_conf_tx,
@ -1875,6 +1895,7 @@ static const struct ieee80211_ops rt2500pci_mac80211_ops = {
static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = { static const struct rt2x00lib_ops rt2500pci_rt2x00_ops = {
.irq_handler = rt2500pci_interrupt, .irq_handler = rt2500pci_interrupt,
.irq_handler_thread = rt2500pci_interrupt_thread,
.probe_hw = rt2500pci_probe_hw, .probe_hw = rt2500pci_probe_hw,
.initialize = rt2x00pci_initialize, .initialize = rt2x00pci_initialize,
.uninitialize = rt2x00pci_uninitialize, .uninitialize = rt2x00pci_uninitialize,

View File

@ -1004,7 +1004,9 @@ static int rt2500usb_set_device_state(struct rt2x00_dev *rt2x00dev,
rt2500usb_toggle_rx(rt2x00dev, state); rt2500usb_toggle_rx(rt2x00dev, state);
break; break;
case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_ON:
case STATE_RADIO_IRQ_ON_ISR:
case STATE_RADIO_IRQ_OFF: case STATE_RADIO_IRQ_OFF:
case STATE_RADIO_IRQ_OFF_ISR:
/* No support, but no error either */ /* No support, but no error either */
break; break;
case STATE_DEEP_SLEEP: case STATE_DEEP_SLEEP:
@ -1470,13 +1472,6 @@ static int rt2500usb_init_eeprom(struct rt2x00_dev *rt2x00dev)
if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO))
__set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_BUTTON, &rt2x00dev->flags);
/*
* Check if the BBP tuning should be disabled.
*/
rt2x00_eeprom_read(rt2x00dev, EEPROM_NIC, &eeprom);
if (rt2x00_get_field16(eeprom, EEPROM_NIC_DYN_BBP_TUNE))
__set_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags);
/* /*
* Read the RSSI <-> dBm offset information. * Read the RSSI <-> dBm offset information.
*/ */
@ -1743,7 +1738,7 @@ static int rt2500usb_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
__set_bit(DRIVER_REQUIRE_COPY_IV, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_COPY_IV, &rt2x00dev->flags);
} }
__set_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags); __set_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags);
/* /*
* Set the rssi offset. * Set the rssi offset.
@ -1763,6 +1758,8 @@ static const struct ieee80211_ops rt2500usb_mac80211_ops = {
.configure_filter = rt2x00mac_configure_filter, .configure_filter = rt2x00mac_configure_filter,
.set_tim = rt2x00mac_set_tim, .set_tim = rt2x00mac_set_tim,
.set_key = rt2x00mac_set_key, .set_key = rt2x00mac_set_key,
.sw_scan_start = rt2x00mac_sw_scan_start,
.sw_scan_complete = rt2x00mac_sw_scan_complete,
.get_stats = rt2x00mac_get_stats, .get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed, .bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2x00mac_conf_tx, .conf_tx = rt2x00mac_conf_tx,
@ -1778,6 +1775,7 @@ static const struct rt2x00lib_ops rt2500usb_rt2x00_ops = {
.rfkill_poll = rt2500usb_rfkill_poll, .rfkill_poll = rt2500usb_rfkill_poll,
.link_stats = rt2500usb_link_stats, .link_stats = rt2500usb_link_stats,
.reset_tuner = rt2500usb_reset_tuner, .reset_tuner = rt2500usb_reset_tuner,
.watchdog = rt2x00usb_watchdog,
.write_tx_desc = rt2500usb_write_tx_desc, .write_tx_desc = rt2500usb_write_tx_desc,
.write_beacon = rt2500usb_write_beacon, .write_beacon = rt2500usb_write_beacon,
.get_tx_data_len = rt2500usb_get_tx_data_len, .get_tx_data_len = rt2500usb_get_tx_data_len,

View File

@ -74,7 +74,7 @@
* Signal information. * Signal information.
* Default offset is required for RSSI <-> dBm conversion. * Default offset is required for RSSI <-> dBm conversion.
*/ */
#define DEFAULT_RSSI_OFFSET 120 /* FIXME */ #define DEFAULT_RSSI_OFFSET 120
/* /*
* Register layout information. * Register layout information.
@ -719,14 +719,20 @@
#define TBTT_TIMER 0x1124 #define TBTT_TIMER 0x1124
/* /*
* INT_TIMER_CFG: * INT_TIMER_CFG: timer configuration
* PRE_TBTT_TIMER: leadtime to tbtt for pretbtt interrupt in units of 1/16 TU
* GP_TIMER: period of general purpose timer in units of 1/16 TU
*/ */
#define INT_TIMER_CFG 0x1128 #define INT_TIMER_CFG 0x1128
#define INT_TIMER_CFG_PRE_TBTT_TIMER FIELD32(0x0000ffff)
#define INT_TIMER_CFG_GP_TIMER FIELD32(0xffff0000)
/* /*
* INT_TIMER_EN: GP-timer and pre-tbtt Int enable * INT_TIMER_EN: GP-timer and pre-tbtt Int enable
*/ */
#define INT_TIMER_EN 0x112c #define INT_TIMER_EN 0x112c
#define INT_TIMER_EN_PRE_TBTT_TIMER FIELD32(0x00000001)
#define INT_TIMER_EN_GP_TIMER FIELD32(0x00000002)
/* /*
* CH_IDLE_STA: channel idle time * CH_IDLE_STA: channel idle time
@ -802,6 +808,18 @@
*/ */
#define EDCA_TID_AC_MAP 0x1310 #define EDCA_TID_AC_MAP 0x1310
/*
* TX_PWR_CFG:
*/
#define TX_PWR_CFG_RATE0 FIELD32(0x0000000f)
#define TX_PWR_CFG_RATE1 FIELD32(0x000000f0)
#define TX_PWR_CFG_RATE2 FIELD32(0x00000f00)
#define TX_PWR_CFG_RATE3 FIELD32(0x0000f000)
#define TX_PWR_CFG_RATE4 FIELD32(0x000f0000)
#define TX_PWR_CFG_RATE5 FIELD32(0x00f00000)
#define TX_PWR_CFG_RATE6 FIELD32(0x0f000000)
#define TX_PWR_CFG_RATE7 FIELD32(0xf0000000)
/* /*
* TX_PWR_CFG_0: * TX_PWR_CFG_0:
*/ */
@ -1853,9 +1871,15 @@ struct mac_iveiv_entry {
#define EEPROM_TXPOWER_A_2 FIELD16(0xff00) #define EEPROM_TXPOWER_A_2 FIELD16(0xff00)
/* /*
* EEPROM TXpower byrate: 20MHZ power * EEPROM TXPOWER by rate: tx power per tx rate for HT20 mode
*/ */
#define EEPROM_TXPOWER_BYRATE 0x006f #define EEPROM_TXPOWER_BYRATE 0x006f
#define EEPROM_TXPOWER_BYRATE_SIZE 9
#define EEPROM_TXPOWER_BYRATE_RATE0 FIELD16(0x000f)
#define EEPROM_TXPOWER_BYRATE_RATE1 FIELD16(0x00f0)
#define EEPROM_TXPOWER_BYRATE_RATE2 FIELD16(0x0f00)
#define EEPROM_TXPOWER_BYRATE_RATE3 FIELD16(0xf000)
/* /*
* EEPROM BBP. * EEPROM BBP.

View File

@ -33,6 +33,7 @@
Abstract: rt2800 generic device routines. Abstract: rt2800 generic device routines.
*/ */
#include <linux/crc-ccitt.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -272,6 +273,160 @@ int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev)
} }
EXPORT_SYMBOL_GPL(rt2800_wait_wpdma_ready); EXPORT_SYMBOL_GPL(rt2800_wait_wpdma_ready);
static bool rt2800_check_firmware_crc(const u8 *data, const size_t len)
{
u16 fw_crc;
u16 crc;
/*
* The last 2 bytes in the firmware array are the crc checksum itself,
* this means that we should never pass those 2 bytes to the crc
* algorithm.
*/
fw_crc = (data[len - 2] << 8 | data[len - 1]);
/*
* Use the crc ccitt algorithm.
* This will return the same value as the legacy driver which
* used bit ordering reversion on the both the firmware bytes
* before input input as well as on the final output.
* Obviously using crc ccitt directly is much more efficient.
*/
crc = crc_ccitt(~0, data, len - 2);
/*
* There is a small difference between the crc-itu-t + bitrev and
* the crc-ccitt crc calculation. In the latter method the 2 bytes
* will be swapped, use swab16 to convert the crc to the correct
* value.
*/
crc = swab16(crc);
return fw_crc == crc;
}
int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len)
{
size_t offset = 0;
size_t fw_len;
bool multiple;
/*
* PCI(e) & SOC devices require firmware with a length
* of 8kb. USB devices require firmware files with a length
* of 4kb. Certain USB chipsets however require different firmware,
* which Ralink only provides attached to the original firmware
* file. Thus for USB devices, firmware files have a length
* which is a multiple of 4kb.
*/
if (rt2x00_is_usb(rt2x00dev)) {
fw_len = 4096;
multiple = true;
} else {
fw_len = 8192;
multiple = true;
}
/*
* Validate the firmware length
*/
if (len != fw_len && (!multiple || (len % fw_len) != 0))
return FW_BAD_LENGTH;
/*
* Check if the chipset requires one of the upper parts
* of the firmware.
*/
if (rt2x00_is_usb(rt2x00dev) &&
!rt2x00_rt(rt2x00dev, RT2860) &&
!rt2x00_rt(rt2x00dev, RT2872) &&
!rt2x00_rt(rt2x00dev, RT3070) &&
((len / fw_len) == 1))
return FW_BAD_VERSION;
/*
* 8kb firmware files must be checked as if it were
* 2 separate firmware files.
*/
while (offset < len) {
if (!rt2800_check_firmware_crc(data + offset, fw_len))
return FW_BAD_CRC;
offset += fw_len;
}
return FW_OK;
}
EXPORT_SYMBOL_GPL(rt2800_check_firmware);
int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len)
{
unsigned int i;
u32 reg;
/*
* Wait for stable hardware.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
if (reg && reg != ~0)
break;
msleep(1);
}
if (i == REGISTER_BUSY_COUNT) {
ERROR(rt2x00dev, "Unstable hardware.\n");
return -EBUSY;
}
if (rt2x00_is_pci(rt2x00dev))
rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002);
/*
* Disable DMA, will be reenabled later when enabling
* the radio.
*/
rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
/*
* Write firmware to the device.
*/
rt2800_drv_write_firmware(rt2x00dev, data, len);
/*
* Wait for device to stabilize.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY))
break;
msleep(1);
}
if (i == REGISTER_BUSY_COUNT) {
ERROR(rt2x00dev, "PBF system register not ready.\n");
return -EBUSY;
}
/*
* Initialize firmware.
*/
rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
msleep(1);
return 0;
}
EXPORT_SYMBOL_GPL(rt2800_load_firmware);
void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc) void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
{ {
u32 word; u32 word;
@ -325,9 +480,53 @@ void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc)
} }
EXPORT_SYMBOL_GPL(rt2800_write_txwi); EXPORT_SYMBOL_GPL(rt2800_write_txwi);
void rt2800_process_rxwi(struct sk_buff *skb, struct rxdone_entry_desc *rxdesc) static int rt2800_agc_to_rssi(struct rt2x00_dev *rt2x00dev, int rxwi_w2)
{ {
__le32 *rxwi = (__le32 *) skb->data; int rssi0 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI0);
int rssi1 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI1);
int rssi2 = rt2x00_get_field32(rxwi_w2, RXWI_W2_RSSI2);
u16 eeprom;
u8 offset0;
u8 offset1;
u8 offset2;
if (rt2x00dev->rx_status.band == IEEE80211_BAND_2GHZ) {
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG, &eeprom);
offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET0);
offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG_OFFSET1);
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_BG2, &eeprom);
offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_BG2_OFFSET2);
} else {
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A, &eeprom);
offset0 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET0);
offset1 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A_OFFSET1);
rt2x00_eeprom_read(rt2x00dev, EEPROM_RSSI_A2, &eeprom);
offset2 = rt2x00_get_field16(eeprom, EEPROM_RSSI_A2_OFFSET2);
}
/*
* Convert the value from the descriptor into the RSSI value
* If the value in the descriptor is 0, it is considered invalid
* and the default (extremely low) rssi value is assumed
*/
rssi0 = (rssi0) ? (-12 - offset0 - rt2x00dev->lna_gain - rssi0) : -128;
rssi1 = (rssi1) ? (-12 - offset1 - rt2x00dev->lna_gain - rssi1) : -128;
rssi2 = (rssi2) ? (-12 - offset2 - rt2x00dev->lna_gain - rssi2) : -128;
/*
* mac80211 only accepts a single RSSI value. Calculating the
* average doesn't deliver a fair answer either since -60:-60 would
* be considered equally good as -50:-70 while the second is the one
* which gives less energy...
*/
rssi0 = max(rssi0, rssi1);
return max(rssi0, rssi2);
}
void rt2800_process_rxwi(struct queue_entry *entry,
struct rxdone_entry_desc *rxdesc)
{
__le32 *rxwi = (__le32 *) entry->skb->data;
u32 word; u32 word;
rt2x00_desc_read(rxwi, 0, &word); rt2x00_desc_read(rxwi, 0, &word);
@ -358,14 +557,15 @@ void rt2800_process_rxwi(struct sk_buff *skb, struct rxdone_entry_desc *rxdesc)
rt2x00_desc_read(rxwi, 2, &word); rt2x00_desc_read(rxwi, 2, &word);
rxdesc->rssi = /*
(rt2x00_get_field32(word, RXWI_W2_RSSI0) + * Convert descriptor AGC value to RSSI value.
rt2x00_get_field32(word, RXWI_W2_RSSI1)) / 2; */
rxdesc->rssi = rt2800_agc_to_rssi(entry->queue->rt2x00dev, word);
/* /*
* Remove RXWI descriptor from start of buffer. * Remove RXWI descriptor from start of buffer.
*/ */
skb_pull(skb, RXWI_DESC_SIZE); skb_pull(entry->skb, RXWI_DESC_SIZE);
} }
EXPORT_SYMBOL_GPL(rt2800_process_rxwi); EXPORT_SYMBOL_GPL(rt2800_process_rxwi);
@ -428,7 +628,7 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
dev_kfree_skb_any(entry->skb); dev_kfree_skb_any(entry->skb);
entry->skb = NULL; entry->skb = NULL;
} }
EXPORT_SYMBOL(rt2800_write_beacon); EXPORT_SYMBOL_GPL(rt2800_write_beacon);
static void inline rt2800_clear_beacon(struct rt2x00_dev *rt2x00dev, static void inline rt2800_clear_beacon(struct rt2x00_dev *rt2x00dev,
unsigned int beacon_base) unsigned int beacon_base)
@ -760,8 +960,18 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1); rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 1);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_SYNC, conf->sync); rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_SYNC, conf->sync);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE,
(conf->sync == TSF_SYNC_BEACON)); (conf->sync == TSF_SYNC_ADHOC ||
conf->sync == TSF_SYNC_AP_NONE));
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg); rt2800_register_write(rt2x00dev, BCN_TIME_CFG, reg);
/*
* Enable pre tbtt interrupt for beaconing modes
*/
rt2800_register_read(rt2x00dev, INT_TIMER_EN, &reg);
rt2x00_set_field32(&reg, INT_TIMER_EN_PRE_TBTT_TIMER,
(conf->sync == TSF_SYNC_AP_NONE));
rt2800_register_write(rt2x00dev, INT_TIMER_EN, reg);
} }
if (flags & CONFIG_UPDATE_MAC) { if (flags & CONFIG_UPDATE_MAC) {
@ -1086,66 +1296,115 @@ static void rt2800_config_channel(struct rt2x00_dev *rt2x00dev,
} }
static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev, static void rt2800_config_txpower(struct rt2x00_dev *rt2x00dev,
const int txpower) const int max_txpower)
{ {
u8 txpower;
u8 max_value = (u8)max_txpower;
u16 eeprom;
int i;
u32 reg; u32 reg;
u32 value = TXPOWER_G_TO_DEV(txpower);
u8 r1; u8 r1;
u32 offset;
/*
* set to normal tx power mode: +/- 0dBm
*/
rt2800_bbp_read(rt2x00dev, 1, &r1); rt2800_bbp_read(rt2x00dev, 1, &r1);
rt2x00_set_field8(&r1, BBP1_TX_POWER, 0); rt2x00_set_field8(&r1, BBP1_TX_POWER, 0);
rt2800_bbp_write(rt2x00dev, 1, r1); rt2800_bbp_write(rt2x00dev, 1, r1);
rt2800_register_read(rt2x00dev, TX_PWR_CFG_0, &reg); /*
rt2x00_set_field32(&reg, TX_PWR_CFG_0_1MBS, value); * The eeprom contains the tx power values for each rate. These
rt2x00_set_field32(&reg, TX_PWR_CFG_0_2MBS, value); * values map to 100% tx power. Each 16bit word contains four tx
rt2x00_set_field32(&reg, TX_PWR_CFG_0_55MBS, value); * power values and the order is the same as used in the TX_PWR_CFG
rt2x00_set_field32(&reg, TX_PWR_CFG_0_11MBS, value); * registers.
rt2x00_set_field32(&reg, TX_PWR_CFG_0_6MBS, value); */
rt2x00_set_field32(&reg, TX_PWR_CFG_0_9MBS, value); offset = TX_PWR_CFG_0;
rt2x00_set_field32(&reg, TX_PWR_CFG_0_12MBS, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_0_18MBS, value);
rt2800_register_write(rt2x00dev, TX_PWR_CFG_0, reg);
rt2800_register_read(rt2x00dev, TX_PWR_CFG_1, &reg); for (i = 0; i < EEPROM_TXPOWER_BYRATE_SIZE; i += 2) {
rt2x00_set_field32(&reg, TX_PWR_CFG_1_24MBS, value); /* just to be safe */
rt2x00_set_field32(&reg, TX_PWR_CFG_1_36MBS, value); if (offset > TX_PWR_CFG_4)
rt2x00_set_field32(&reg, TX_PWR_CFG_1_48MBS, value); break;
rt2x00_set_field32(&reg, TX_PWR_CFG_1_54MBS, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS0, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS1, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS2, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_1_MCS3, value);
rt2800_register_write(rt2x00dev, TX_PWR_CFG_1, reg);
rt2800_register_read(rt2x00dev, TX_PWR_CFG_2, &reg); rt2800_register_read(rt2x00dev, offset, &reg);
rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS4, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS5, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS6, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS7, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS8, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS9, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS10, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_2_MCS11, value);
rt2800_register_write(rt2x00dev, TX_PWR_CFG_2, reg);
rt2800_register_read(rt2x00dev, TX_PWR_CFG_3, &reg); /* read the next four txpower values */
rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS12, value); rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i,
rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS13, value); &eeprom);
rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS14, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_3_MCS15, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN1, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN2, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN3, value);
rt2x00_set_field32(&reg, TX_PWR_CFG_3_UKNOWN4, value);
rt2800_register_write(rt2x00dev, TX_PWR_CFG_3, reg);
rt2800_register_read(rt2x00dev, TX_PWR_CFG_4, &reg); /* TX_PWR_CFG_0: 1MBS, TX_PWR_CFG_1: 24MBS,
rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN5, value); * TX_PWR_CFG_2: MCS4, TX_PWR_CFG_3: MCS12,
rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN6, value); * TX_PWR_CFG_4: unknown */
rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN7, value); txpower = rt2x00_get_field16(eeprom,
rt2x00_set_field32(&reg, TX_PWR_CFG_4_UKNOWN8, value); EEPROM_TXPOWER_BYRATE_RATE0);
rt2800_register_write(rt2x00dev, TX_PWR_CFG_4, reg); rt2x00_set_field32(&reg, TX_PWR_CFG_RATE0,
min(txpower, max_value));
/* TX_PWR_CFG_0: 2MBS, TX_PWR_CFG_1: 36MBS,
* TX_PWR_CFG_2: MCS5, TX_PWR_CFG_3: MCS13,
* TX_PWR_CFG_4: unknown */
txpower = rt2x00_get_field16(eeprom,
EEPROM_TXPOWER_BYRATE_RATE1);
rt2x00_set_field32(&reg, TX_PWR_CFG_RATE1,
min(txpower, max_value));
/* TX_PWR_CFG_0: 55MBS, TX_PWR_CFG_1: 48MBS,
* TX_PWR_CFG_2: MCS6, TX_PWR_CFG_3: MCS14,
* TX_PWR_CFG_4: unknown */
txpower = rt2x00_get_field16(eeprom,
EEPROM_TXPOWER_BYRATE_RATE2);
rt2x00_set_field32(&reg, TX_PWR_CFG_RATE2,
min(txpower, max_value));
/* TX_PWR_CFG_0: 11MBS, TX_PWR_CFG_1: 54MBS,
* TX_PWR_CFG_2: MCS7, TX_PWR_CFG_3: MCS15,
* TX_PWR_CFG_4: unknown */
txpower = rt2x00_get_field16(eeprom,
EEPROM_TXPOWER_BYRATE_RATE3);
rt2x00_set_field32(&reg, TX_PWR_CFG_RATE3,
min(txpower, max_value));
/* read the next four txpower values */
rt2x00_eeprom_read(rt2x00dev, EEPROM_TXPOWER_BYRATE + i + 1,
&eeprom);
/* TX_PWR_CFG_0: 6MBS, TX_PWR_CFG_1: MCS0,
* TX_PWR_CFG_2: MCS8, TX_PWR_CFG_3: unknown,
* TX_PWR_CFG_4: unknown */
txpower = rt2x00_get_field16(eeprom,
EEPROM_TXPOWER_BYRATE_RATE0);
rt2x00_set_field32(&reg, TX_PWR_CFG_RATE4,
min(txpower, max_value));
/* TX_PWR_CFG_0: 9MBS, TX_PWR_CFG_1: MCS1,
* TX_PWR_CFG_2: MCS9, TX_PWR_CFG_3: unknown,
* TX_PWR_CFG_4: unknown */
txpower = rt2x00_get_field16(eeprom,
EEPROM_TXPOWER_BYRATE_RATE1);
rt2x00_set_field32(&reg, TX_PWR_CFG_RATE5,
min(txpower, max_value));
/* TX_PWR_CFG_0: 12MBS, TX_PWR_CFG_1: MCS2,
* TX_PWR_CFG_2: MCS10, TX_PWR_CFG_3: unknown,
* TX_PWR_CFG_4: unknown */
txpower = rt2x00_get_field16(eeprom,
EEPROM_TXPOWER_BYRATE_RATE2);
rt2x00_set_field32(&reg, TX_PWR_CFG_RATE6,
min(txpower, max_value));
/* TX_PWR_CFG_0: 18MBS, TX_PWR_CFG_1: MCS3,
* TX_PWR_CFG_2: MCS11, TX_PWR_CFG_3: unknown,
* TX_PWR_CFG_4: unknown */
txpower = rt2x00_get_field16(eeprom,
EEPROM_TXPOWER_BYRATE_RATE3);
rt2x00_set_field32(&reg, TX_PWR_CFG_RATE7,
min(txpower, max_value));
rt2800_register_write(rt2x00dev, offset, reg);
/* next TX_PWR_CFG register */
offset += 4;
}
} }
static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev, static void rt2800_config_retry_limit(struct rt2x00_dev *rt2x00dev,
@ -1316,7 +1575,7 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000); rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, 0x00000000);
rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg); rt2800_register_read(rt2x00dev, BCN_TIME_CFG, &reg);
rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL, 0); rt2x00_set_field32(&reg, BCN_TIME_CFG_BEACON_INTERVAL, 1600);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 0); rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_TICKING, 0);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_SYNC, 0); rt2x00_set_field32(&reg, BCN_TIME_CFG_TSF_SYNC, 0);
rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0); rt2x00_set_field32(&reg, BCN_TIME_CFG_TBTT_ENABLE, 0);
@ -1638,6 +1897,13 @@ int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
rt2800_register_read(rt2x00dev, TX_STA_CNT1, &reg); rt2800_register_read(rt2x00dev, TX_STA_CNT1, &reg);
rt2800_register_read(rt2x00dev, TX_STA_CNT2, &reg); rt2800_register_read(rt2x00dev, TX_STA_CNT2, &reg);
/*
* Setup leadtime for pre tbtt interrupt to 6ms
*/
rt2800_register_read(rt2x00dev, INT_TIMER_CFG, &reg);
rt2x00_set_field32(&reg, INT_TIMER_CFG_PRE_TBTT_TIMER, 6 << 4);
rt2800_register_write(rt2x00dev, INT_TIMER_CFG, reg);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(rt2800_init_registers); EXPORT_SYMBOL_GPL(rt2800_init_registers);
@ -2630,8 +2896,8 @@ EXPORT_SYMBOL_GPL(rt2800_probe_hw_mode);
/* /*
* IEEE80211 stack callback functions. * IEEE80211 stack callback functions.
*/ */
static void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32,
u32 *iv32, u16 *iv16) u16 *iv16)
{ {
struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_dev *rt2x00dev = hw->priv;
struct mac_iveiv_entry iveiv_entry; struct mac_iveiv_entry iveiv_entry;
@ -2644,8 +2910,9 @@ static void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx,
memcpy(iv16, &iveiv_entry.iv[0], sizeof(*iv16)); memcpy(iv16, &iveiv_entry.iv[0], sizeof(*iv16));
memcpy(iv32, &iveiv_entry.iv[4], sizeof(*iv32)); memcpy(iv32, &iveiv_entry.iv[4], sizeof(*iv32));
} }
EXPORT_SYMBOL_GPL(rt2800_get_tkip_seq);
static int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value) int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
{ {
struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_dev *rt2x00dev = hw->priv;
u32 reg; u32 reg;
@ -2681,8 +2948,9 @@ static int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(rt2800_set_rts_threshold);
static int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
const struct ieee80211_tx_queue_params *params) const struct ieee80211_tx_queue_params *params)
{ {
struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_dev *rt2x00dev = hw->priv;
@ -2748,8 +3016,9 @@ static int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(rt2800_conf_tx);
static u64 rt2800_get_tsf(struct ieee80211_hw *hw) u64 rt2800_get_tsf(struct ieee80211_hw *hw)
{ {
struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_dev *rt2x00dev = hw->priv;
u64 tsf; u64 tsf;
@ -2762,12 +3031,11 @@ static u64 rt2800_get_tsf(struct ieee80211_hw *hw)
return tsf; return tsf;
} }
EXPORT_SYMBOL_GPL(rt2800_get_tsf);
static int rt2800_ampdu_action(struct ieee80211_hw *hw, int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action, enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, struct ieee80211_sta *sta, u16 tid, u16 *ssn)
u16 tid, u16 *ssn)
{ {
int ret = 0; int ret = 0;
@ -2791,27 +3059,7 @@ static int rt2800_ampdu_action(struct ieee80211_hw *hw,
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(rt2800_ampdu_action);
const struct ieee80211_ops rt2800_mac80211_ops = {
.tx = rt2x00mac_tx,
.start = rt2x00mac_start,
.stop = rt2x00mac_stop,
.add_interface = rt2x00mac_add_interface,
.remove_interface = rt2x00mac_remove_interface,
.config = rt2x00mac_config,
.configure_filter = rt2x00mac_configure_filter,
.set_tim = rt2x00mac_set_tim,
.set_key = rt2x00mac_set_key,
.get_stats = rt2x00mac_get_stats,
.get_tkip_seq = rt2800_get_tkip_seq,
.set_rts_threshold = rt2800_set_rts_threshold,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2800_conf_tx,
.get_tsf = rt2800_get_tsf,
.rfkill_poll = rt2x00mac_rfkill_poll,
.ampdu_action = rt2800_ampdu_action,
};
EXPORT_SYMBOL_GPL(rt2800_mac80211_ops);
MODULE_AUTHOR(DRV_PROJECT ", Bartlomiej Zolnierkiewicz"); MODULE_AUTHOR(DRV_PROJECT ", Bartlomiej Zolnierkiewicz");
MODULE_VERSION(DRV_VERSION); MODULE_VERSION(DRV_VERSION);

View File

@ -41,6 +41,8 @@ struct rt2800_ops {
const unsigned int offset, const unsigned int offset,
const struct rt2x00_field32 field, u32 *reg); const struct rt2x00_field32 field, u32 *reg);
int (*drv_write_firmware)(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len);
int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev); int (*drv_init_registers)(struct rt2x00_dev *rt2x00dev);
}; };
@ -48,7 +50,7 @@ static inline void rt2800_register_read(struct rt2x00_dev *rt2x00dev,
const unsigned int offset, const unsigned int offset,
u32 *value) u32 *value)
{ {
const struct rt2800_ops *rt2800ops = rt2x00dev->priv; const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
rt2800ops->register_read(rt2x00dev, offset, value); rt2800ops->register_read(rt2x00dev, offset, value);
} }
@ -57,7 +59,7 @@ static inline void rt2800_register_read_lock(struct rt2x00_dev *rt2x00dev,
const unsigned int offset, const unsigned int offset,
u32 *value) u32 *value)
{ {
const struct rt2800_ops *rt2800ops = rt2x00dev->priv; const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
rt2800ops->register_read_lock(rt2x00dev, offset, value); rt2800ops->register_read_lock(rt2x00dev, offset, value);
} }
@ -66,7 +68,7 @@ static inline void rt2800_register_write(struct rt2x00_dev *rt2x00dev,
const unsigned int offset, const unsigned int offset,
u32 value) u32 value)
{ {
const struct rt2800_ops *rt2800ops = rt2x00dev->priv; const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
rt2800ops->register_write(rt2x00dev, offset, value); rt2800ops->register_write(rt2x00dev, offset, value);
} }
@ -75,7 +77,7 @@ static inline void rt2800_register_write_lock(struct rt2x00_dev *rt2x00dev,
const unsigned int offset, const unsigned int offset,
u32 value) u32 value)
{ {
const struct rt2800_ops *rt2800ops = rt2x00dev->priv; const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
rt2800ops->register_write_lock(rt2x00dev, offset, value); rt2800ops->register_write_lock(rt2x00dev, offset, value);
} }
@ -84,7 +86,7 @@ static inline void rt2800_register_multiread(struct rt2x00_dev *rt2x00dev,
const unsigned int offset, const unsigned int offset,
void *value, const u32 length) void *value, const u32 length)
{ {
const struct rt2800_ops *rt2800ops = rt2x00dev->priv; const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
rt2800ops->register_multiread(rt2x00dev, offset, value, length); rt2800ops->register_multiread(rt2x00dev, offset, value, length);
} }
@ -94,7 +96,7 @@ static inline void rt2800_register_multiwrite(struct rt2x00_dev *rt2x00dev,
const void *value, const void *value,
const u32 length) const u32 length)
{ {
const struct rt2800_ops *rt2800ops = rt2x00dev->priv; const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
rt2800ops->register_multiwrite(rt2x00dev, offset, value, length); rt2800ops->register_multiwrite(rt2x00dev, offset, value, length);
} }
@ -104,14 +106,22 @@ static inline int rt2800_regbusy_read(struct rt2x00_dev *rt2x00dev,
const struct rt2x00_field32 field, const struct rt2x00_field32 field,
u32 *reg) u32 *reg)
{ {
const struct rt2800_ops *rt2800ops = rt2x00dev->priv; const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg); return rt2800ops->regbusy_read(rt2x00dev, offset, field, reg);
} }
static inline int rt2800_drv_write_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len)
{
const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
return rt2800ops->drv_write_firmware(rt2x00dev, data, len);
}
static inline int rt2800_drv_init_registers(struct rt2x00_dev *rt2x00dev) static inline int rt2800_drv_init_registers(struct rt2x00_dev *rt2x00dev)
{ {
const struct rt2800_ops *rt2800ops = rt2x00dev->priv; const struct rt2800_ops *rt2800ops = rt2x00dev->ops->drv;
return rt2800ops->drv_init_registers(rt2x00dev); return rt2800ops->drv_init_registers(rt2x00dev);
} }
@ -120,8 +130,13 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
const u8 command, const u8 token, const u8 command, const u8 token,
const u8 arg0, const u8 arg1); const u8 arg0, const u8 arg1);
int rt2800_check_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len);
int rt2800_load_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len);
void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc); void rt2800_write_txwi(__le32 *txwi, struct txentry_desc *txdesc);
void rt2800_process_rxwi(struct sk_buff *skb, struct rxdone_entry_desc *txdesc); void rt2800_process_rxwi(struct queue_entry *entry, struct rxdone_entry_desc *txdesc);
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc); void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc);
@ -159,6 +174,14 @@ int rt2800_validate_eeprom(struct rt2x00_dev *rt2x00dev);
int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev); int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev);
int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev); int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev);
extern const struct ieee80211_ops rt2800_mac80211_ops; void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32,
u16 *iv16);
int rt2800_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
const struct ieee80211_tx_queue_params *params);
u64 rt2800_get_tsf(struct ieee80211_hw *hw);
int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
enum ieee80211_ampdu_mlme_action action,
struct ieee80211_sta *sta, u16 tid, u16 *ssn);
#endif /* RT2800LIB_H */ #endif /* RT2800LIB_H */

View File

@ -31,7 +31,6 @@
Supported chipsets: RT2800E & RT2800ED. Supported chipsets: RT2800E & RT2800ED.
*/ */
#include <linux/crc-ccitt.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/init.h> #include <linux/init.h>
@ -192,81 +191,13 @@ static char *rt2800pci_get_firmware_name(struct rt2x00_dev *rt2x00dev)
return FIRMWARE_RT2860; return FIRMWARE_RT2860;
} }
static int rt2800pci_check_firmware(struct rt2x00_dev *rt2x00dev, static int rt2800pci_write_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len) const u8 *data, const size_t len)
{ {
u16 fw_crc;
u16 crc;
/*
* Only support 8kb firmware files.
*/
if (len != 8192)
return FW_BAD_LENGTH;
/*
* The last 2 bytes in the firmware array are the crc checksum itself,
* this means that we should never pass those 2 bytes to the crc
* algorithm.
*/
fw_crc = (data[len - 2] << 8 | data[len - 1]);
/*
* Use the crc ccitt algorithm.
* This will return the same value as the legacy driver which
* used bit ordering reversion on the both the firmware bytes
* before input input as well as on the final output.
* Obviously using crc ccitt directly is much more efficient.
*/
crc = crc_ccitt(~0, data, len - 2);
/*
* There is a small difference between the crc-itu-t + bitrev and
* the crc-ccitt crc calculation. In the latter method the 2 bytes
* will be swapped, use swab16 to convert the crc to the correct
* value.
*/
crc = swab16(crc);
return (fw_crc == crc) ? FW_OK : FW_BAD_CRC;
}
static int rt2800pci_load_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len)
{
unsigned int i;
u32 reg; u32 reg;
/*
* Wait for stable hardware.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
if (reg && reg != ~0)
break;
msleep(1);
}
if (i == REGISTER_BUSY_COUNT) {
ERROR(rt2x00dev, "Unstable hardware.\n");
return -EBUSY;
}
rt2800_register_write(rt2x00dev, PWR_PIN_CFG, 0x00000002);
rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000); rt2800_register_write(rt2x00dev, AUTOWAKEUP_CFG, 0x00000000);
/*
* Disable DMA, will be reenabled later when enabling
* the radio.
*/
rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, &reg);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_TX_DMA, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_DMA_BUSY, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_ENABLE_RX_DMA, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_RX_DMA_BUSY, 0);
rt2x00_set_field32(&reg, WPDMA_GLO_CFG_TX_WRITEBACK_DONE, 1);
rt2800_register_write(rt2x00dev, WPDMA_GLO_CFG, reg);
/* /*
* enable Host program ram write selection * enable Host program ram write selection
*/ */
@ -283,29 +214,6 @@ static int rt2800pci_load_firmware(struct rt2x00_dev *rt2x00dev,
rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000); rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000);
rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001); rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00001);
/*
* Wait for device to stabilize.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY))
break;
msleep(1);
}
if (i == REGISTER_BUSY_COUNT) {
ERROR(rt2x00dev, "PBF system register not ready.\n");
return -EBUSY;
}
/*
* Disable interrupts
*/
rt2x00dev->ops->lib->set_device_state(rt2x00dev, STATE_RADIO_IRQ_OFF);
/*
* Initialize BBP R/W access agent
*/
rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0); rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0); rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
@ -422,7 +330,8 @@ static void rt2800pci_toggle_rx(struct rt2x00_dev *rt2x00dev,
static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
enum dev_state state) enum dev_state state)
{ {
int mask = (state == STATE_RADIO_IRQ_ON); int mask = (state == STATE_RADIO_IRQ_ON) ||
(state == STATE_RADIO_IRQ_ON_ISR);
u32 reg; u32 reg;
/* /*
@ -631,7 +540,9 @@ static int rt2800pci_set_device_state(struct rt2x00_dev *rt2x00dev,
rt2800pci_toggle_rx(rt2x00dev, state); rt2800pci_toggle_rx(rt2x00dev, state);
break; break;
case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_ON:
case STATE_RADIO_IRQ_ON_ISR:
case STATE_RADIO_IRQ_OFF: case STATE_RADIO_IRQ_OFF:
case STATE_RADIO_IRQ_OFF_ISR:
rt2800pci_toggle_irq(rt2x00dev, state); rt2800pci_toggle_irq(rt2x00dev, state);
break; break;
case STATE_DEEP_SLEEP: case STATE_DEEP_SLEEP:
@ -805,7 +716,7 @@ static void rt2800pci_fill_rxdone(struct queue_entry *entry,
/* /*
* Process the RXWI structure that is at the start of the buffer. * Process the RXWI structure that is at the start of the buffer.
*/ */
rt2800_process_rxwi(entry->skb, rxdesc); rt2800_process_rxwi(entry, rxdesc);
/* /*
* Set RX IDX in register to inform hardware that we have handled * Set RX IDX in register to inform hardware that we have handled
@ -929,6 +840,48 @@ static void rt2800pci_wakeup(struct rt2x00_dev *rt2x00dev)
rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); rt2800_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
} }
static irqreturn_t rt2800pci_interrupt_thread(int irq, void *dev_instance)
{
struct rt2x00_dev *rt2x00dev = dev_instance;
u32 reg = rt2x00dev->irqvalue[0];
/*
* 1 - Pre TBTT interrupt.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_PRE_TBTT))
rt2x00lib_pretbtt(rt2x00dev);
/*
* 2 - Beacondone interrupt.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT))
rt2x00lib_beacondone(rt2x00dev);
/*
* 3 - Rx ring done interrupt.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE))
rt2x00pci_rxdone(rt2x00dev);
/*
* 4 - Tx done interrupt.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS))
rt2800pci_txdone(rt2x00dev);
/*
* 5 - Auto wakeup interrupt.
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP))
rt2800pci_wakeup(rt2x00dev);
/* Enable interrupts again. */
rt2x00dev->ops->lib->set_device_state(rt2x00dev,
STATE_RADIO_IRQ_ON_ISR);
return IRQ_HANDLED;
}
static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance) static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)
{ {
struct rt2x00_dev *rt2x00dev = dev_instance; struct rt2x00_dev *rt2x00dev = dev_instance;
@ -944,25 +897,15 @@ static irqreturn_t rt2800pci_interrupt(int irq, void *dev_instance)
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return IRQ_HANDLED; return IRQ_HANDLED;
/* /* Store irqvalue for use in the interrupt thread. */
* 1 - Rx ring done interrupt. rt2x00dev->irqvalue[0] = reg;
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_RX_DONE))
rt2x00pci_rxdone(rt2x00dev);
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TX_FIFO_STATUS)) /* Disable interrupts, will be enabled again in the interrupt thread. */
rt2800pci_txdone(rt2x00dev); rt2x00dev->ops->lib->set_device_state(rt2x00dev,
STATE_RADIO_IRQ_OFF_ISR);
/*
* Current beacon was sent out, fetch the next one
*/
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_TBTT))
rt2x00lib_beacondone(rt2x00dev);
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_AUTO_WAKEUP)) return IRQ_WAKE_THREAD;
rt2800pci_wakeup(rt2x00dev);
return IRQ_HANDLED;
} }
/* /*
@ -983,26 +926,10 @@ static int rt2800pci_validate_eeprom(struct rt2x00_dev *rt2x00dev)
return rt2800_validate_eeprom(rt2x00dev); return rt2800_validate_eeprom(rt2x00dev);
} }
static const struct rt2800_ops rt2800pci_rt2800_ops = {
.register_read = rt2x00pci_register_read,
.register_read_lock = rt2x00pci_register_read, /* same for PCI */
.register_write = rt2x00pci_register_write,
.register_write_lock = rt2x00pci_register_write, /* same for PCI */
.register_multiread = rt2x00pci_register_multiread,
.register_multiwrite = rt2x00pci_register_multiwrite,
.regbusy_read = rt2x00pci_regbusy_read,
.drv_init_registers = rt2800pci_init_registers,
};
static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev) static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
{ {
int retval; int retval;
rt2x00dev->priv = (void *)&rt2800pci_rt2800_ops;
/* /*
* Allocate eeprom data. * Allocate eeprom data.
*/ */
@ -1028,6 +955,12 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(DRIVER_SUPPORT_CONTROL_FILTERS, &rt2x00dev->flags); __set_bit(DRIVER_SUPPORT_CONTROL_FILTERS, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL, &rt2x00dev->flags); __set_bit(DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL, &rt2x00dev->flags);
/*
* This device has a pre tbtt interrupt and thus fetches
* a new beacon directly prior to transmission.
*/
__set_bit(DRIVER_SUPPORT_PRE_TBTT_INTERRUPT, &rt2x00dev->flags);
/* /*
* This device requires firmware. * This device requires firmware.
*/ */
@ -1037,6 +970,7 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags);
if (!modparam_nohwcrypt) if (!modparam_nohwcrypt)
__set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
/* /*
* Set the rssi offset. * Set the rssi offset.
@ -1046,12 +980,46 @@ static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
return 0; return 0;
} }
static const struct ieee80211_ops rt2800pci_mac80211_ops = {
.tx = rt2x00mac_tx,
.start = rt2x00mac_start,
.stop = rt2x00mac_stop,
.add_interface = rt2x00mac_add_interface,
.remove_interface = rt2x00mac_remove_interface,
.config = rt2x00mac_config,
.configure_filter = rt2x00mac_configure_filter,
.set_key = rt2x00mac_set_key,
.sw_scan_start = rt2x00mac_sw_scan_start,
.sw_scan_complete = rt2x00mac_sw_scan_complete,
.get_stats = rt2x00mac_get_stats,
.get_tkip_seq = rt2800_get_tkip_seq,
.set_rts_threshold = rt2800_set_rts_threshold,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2800_conf_tx,
.get_tsf = rt2800_get_tsf,
.rfkill_poll = rt2x00mac_rfkill_poll,
.ampdu_action = rt2800_ampdu_action,
};
static const struct rt2800_ops rt2800pci_rt2800_ops = {
.register_read = rt2x00pci_register_read,
.register_read_lock = rt2x00pci_register_read, /* same for PCI */
.register_write = rt2x00pci_register_write,
.register_write_lock = rt2x00pci_register_write, /* same for PCI */
.register_multiread = rt2x00pci_register_multiread,
.register_multiwrite = rt2x00pci_register_multiwrite,
.regbusy_read = rt2x00pci_regbusy_read,
.drv_write_firmware = rt2800pci_write_firmware,
.drv_init_registers = rt2800pci_init_registers,
};
static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = { static const struct rt2x00lib_ops rt2800pci_rt2x00_ops = {
.irq_handler = rt2800pci_interrupt, .irq_handler = rt2800pci_interrupt,
.irq_handler_thread = rt2800pci_interrupt_thread,
.probe_hw = rt2800pci_probe_hw, .probe_hw = rt2800pci_probe_hw,
.get_firmware_name = rt2800pci_get_firmware_name, .get_firmware_name = rt2800pci_get_firmware_name,
.check_firmware = rt2800pci_check_firmware, .check_firmware = rt2800_check_firmware,
.load_firmware = rt2800pci_load_firmware, .load_firmware = rt2800_load_firmware,
.initialize = rt2x00pci_initialize, .initialize = rt2x00pci_initialize,
.uninitialize = rt2x00pci_uninitialize, .uninitialize = rt2x00pci_uninitialize,
.get_entry_state = rt2800pci_get_entry_state, .get_entry_state = rt2800pci_get_entry_state,
@ -1109,7 +1077,8 @@ static const struct rt2x00_ops rt2800pci_ops = {
.tx = &rt2800pci_queue_tx, .tx = &rt2800pci_queue_tx,
.bcn = &rt2800pci_queue_bcn, .bcn = &rt2800pci_queue_bcn,
.lib = &rt2800pci_rt2x00_ops, .lib = &rt2800pci_rt2x00_ops,
.hw = &rt2800_mac80211_ops, .drv = &rt2800pci_rt2800_ops,
.hw = &rt2800pci_mac80211_ops,
#ifdef CONFIG_RT2X00_LIB_DEBUGFS #ifdef CONFIG_RT2X00_LIB_DEBUGFS
.debugfs = &rt2800_rt2x00debug, .debugfs = &rt2800_rt2x00debug,
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ #endif /* CONFIG_RT2X00_LIB_DEBUGFS */

View File

@ -28,7 +28,6 @@
Supported chipsets: RT2800U. Supported chipsets: RT2800U.
*/ */
#include <linux/crc-ccitt.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/init.h> #include <linux/init.h>
@ -57,84 +56,10 @@ static char *rt2800usb_get_firmware_name(struct rt2x00_dev *rt2x00dev)
return FIRMWARE_RT2870; return FIRMWARE_RT2870;
} }
static bool rt2800usb_check_crc(const u8 *data, const size_t len) static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev,
{
u16 fw_crc;
u16 crc;
/*
* The last 2 bytes in the firmware array are the crc checksum itself,
* this means that we should never pass those 2 bytes to the crc
* algorithm.
*/
fw_crc = (data[len - 2] << 8 | data[len - 1]);
/*
* Use the crc ccitt algorithm.
* This will return the same value as the legacy driver which
* used bit ordering reversion on the both the firmware bytes
* before input input as well as on the final output.
* Obviously using crc ccitt directly is much more efficient.
*/
crc = crc_ccitt(~0, data, len - 2);
/*
* There is a small difference between the crc-itu-t + bitrev and
* the crc-ccitt crc calculation. In the latter method the 2 bytes
* will be swapped, use swab16 to convert the crc to the correct
* value.
*/
crc = swab16(crc);
return fw_crc == crc;
}
static int rt2800usb_check_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len) const u8 *data, const size_t len)
{ {
size_t offset = 0;
/*
* Firmware files:
* There are 2 variations of the rt2870 firmware.
* a) size: 4kb
* b) size: 8kb
* Note that (b) contains 2 separate firmware blobs of 4k
* within the file. The first blob is the same firmware as (a),
* but the second blob is for the additional chipsets.
*/
if (len != 4096 && len != 8192)
return FW_BAD_LENGTH;
/*
* Check if we need the upper 4kb firmware data or not.
*/
if ((len == 4096) &&
!rt2x00_rt(rt2x00dev, RT2860) &&
!rt2x00_rt(rt2x00dev, RT2872) &&
!rt2x00_rt(rt2x00dev, RT3070))
return FW_BAD_VERSION;
/*
* 8kb firmware files must be checked as if it were
* 2 separate firmware files.
*/
while (offset < len) {
if (!rt2800usb_check_crc(data + offset, 4096))
return FW_BAD_CRC;
offset += 4096;
}
return FW_OK;
}
static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev,
const u8 *data, const size_t len)
{
unsigned int i;
int status; int status;
u32 reg;
u32 offset; u32 offset;
u32 length; u32 length;
@ -151,21 +76,6 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev,
length = 4096; length = 4096;
} }
/*
* Wait for stable hardware.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
rt2800_register_read(rt2x00dev, MAC_CSR0, &reg);
if (reg && reg != ~0)
break;
msleep(1);
}
if (i == REGISTER_BUSY_COUNT) {
ERROR(rt2x00dev, "Unstable hardware.\n");
return -EBUSY;
}
/* /*
* Write firmware to device. * Write firmware to device.
*/ */
@ -203,28 +113,6 @@ static int rt2800usb_load_firmware(struct rt2x00_dev *rt2x00dev,
udelay(10); udelay(10);
} }
/*
* Wait for device to stabilize.
*/
for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
rt2800_register_read(rt2x00dev, PBF_SYS_CTRL, &reg);
if (rt2x00_get_field32(reg, PBF_SYS_CTRL_READY))
break;
msleep(1);
}
if (i == REGISTER_BUSY_COUNT) {
ERROR(rt2x00dev, "PBF system register not ready.\n");
return -EBUSY;
}
/*
* Initialize firmware.
*/
rt2800_register_write(rt2x00dev, H2M_BBP_AGENT, 0);
rt2800_register_write(rt2x00dev, H2M_MAILBOX_CSR, 0);
msleep(1);
return 0; return 0;
} }
@ -406,7 +294,9 @@ static int rt2800usb_set_device_state(struct rt2x00_dev *rt2x00dev,
rt2800usb_toggle_rx(rt2x00dev, state); rt2800usb_toggle_rx(rt2x00dev, state);
break; break;
case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_ON:
case STATE_RADIO_IRQ_ON_ISR:
case STATE_RADIO_IRQ_OFF: case STATE_RADIO_IRQ_OFF:
case STATE_RADIO_IRQ_OFF_ISR:
/* No support, but no error either */ /* No support, but no error either */
break; break;
case STATE_DEEP_SLEEP: case STATE_DEEP_SLEEP:
@ -563,7 +453,7 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry,
/* /*
* Process the RXWI structure. * Process the RXWI structure.
*/ */
rt2800_process_rxwi(entry->skb, rxdesc); rt2800_process_rxwi(entry, rxdesc);
} }
/* /*
@ -580,26 +470,10 @@ static int rt2800usb_validate_eeprom(struct rt2x00_dev *rt2x00dev)
return rt2800_validate_eeprom(rt2x00dev); return rt2800_validate_eeprom(rt2x00dev);
} }
static const struct rt2800_ops rt2800usb_rt2800_ops = {
.register_read = rt2x00usb_register_read,
.register_read_lock = rt2x00usb_register_read_lock,
.register_write = rt2x00usb_register_write,
.register_write_lock = rt2x00usb_register_write_lock,
.register_multiread = rt2x00usb_register_multiread,
.register_multiwrite = rt2x00usb_register_multiwrite,
.regbusy_read = rt2x00usb_regbusy_read,
.drv_init_registers = rt2800usb_init_registers,
};
static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
{ {
int retval; int retval;
rt2x00dev->priv = (void *)&rt2800usb_rt2800_ops;
/* /*
* Allocate eeprom data. * Allocate eeprom data.
*/ */
@ -632,6 +506,8 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags);
if (!modparam_nohwcrypt) if (!modparam_nohwcrypt)
__set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags);
/* /*
* Set the rssi offset. * Set the rssi offset.
@ -641,11 +517,45 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
return 0; return 0;
} }
static const struct ieee80211_ops rt2800usb_mac80211_ops = {
.tx = rt2x00mac_tx,
.start = rt2x00mac_start,
.stop = rt2x00mac_stop,
.add_interface = rt2x00mac_add_interface,
.remove_interface = rt2x00mac_remove_interface,
.config = rt2x00mac_config,
.configure_filter = rt2x00mac_configure_filter,
.set_tim = rt2x00mac_set_tim,
.set_key = rt2x00mac_set_key,
.sw_scan_start = rt2x00mac_sw_scan_start,
.sw_scan_complete = rt2x00mac_sw_scan_complete,
.get_stats = rt2x00mac_get_stats,
.get_tkip_seq = rt2800_get_tkip_seq,
.set_rts_threshold = rt2800_set_rts_threshold,
.bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt2800_conf_tx,
.get_tsf = rt2800_get_tsf,
.rfkill_poll = rt2x00mac_rfkill_poll,
.ampdu_action = rt2800_ampdu_action,
};
static const struct rt2800_ops rt2800usb_rt2800_ops = {
.register_read = rt2x00usb_register_read,
.register_read_lock = rt2x00usb_register_read_lock,
.register_write = rt2x00usb_register_write,
.register_write_lock = rt2x00usb_register_write_lock,
.register_multiread = rt2x00usb_register_multiread,
.register_multiwrite = rt2x00usb_register_multiwrite,
.regbusy_read = rt2x00usb_regbusy_read,
.drv_write_firmware = rt2800usb_write_firmware,
.drv_init_registers = rt2800usb_init_registers,
};
static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = { static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.probe_hw = rt2800usb_probe_hw, .probe_hw = rt2800usb_probe_hw,
.get_firmware_name = rt2800usb_get_firmware_name, .get_firmware_name = rt2800usb_get_firmware_name,
.check_firmware = rt2800usb_check_firmware, .check_firmware = rt2800_check_firmware,
.load_firmware = rt2800usb_load_firmware, .load_firmware = rt2800_load_firmware,
.initialize = rt2x00usb_initialize, .initialize = rt2x00usb_initialize,
.uninitialize = rt2x00usb_uninitialize, .uninitialize = rt2x00usb_uninitialize,
.clear_entry = rt2x00usb_clear_entry, .clear_entry = rt2x00usb_clear_entry,
@ -654,6 +564,7 @@ static const struct rt2x00lib_ops rt2800usb_rt2x00_ops = {
.link_stats = rt2800_link_stats, .link_stats = rt2800_link_stats,
.reset_tuner = rt2800_reset_tuner, .reset_tuner = rt2800_reset_tuner,
.link_tuner = rt2800_link_tuner, .link_tuner = rt2800_link_tuner,
.watchdog = rt2x00usb_watchdog,
.write_tx_desc = rt2800usb_write_tx_desc, .write_tx_desc = rt2800usb_write_tx_desc,
.write_tx_data = rt2800usb_write_tx_data, .write_tx_data = rt2800usb_write_tx_data,
.write_beacon = rt2800_write_beacon, .write_beacon = rt2800_write_beacon,
@ -703,7 +614,8 @@ static const struct rt2x00_ops rt2800usb_ops = {
.tx = &rt2800usb_queue_tx, .tx = &rt2800usb_queue_tx,
.bcn = &rt2800usb_queue_bcn, .bcn = &rt2800usb_queue_bcn,
.lib = &rt2800usb_rt2x00_ops, .lib = &rt2800usb_rt2x00_ops,
.hw = &rt2800_mac80211_ops, .drv = &rt2800usb_rt2800_ops,
.hw = &rt2800usb_mac80211_ops,
#ifdef CONFIG_RT2X00_LIB_DEBUGFS #ifdef CONFIG_RT2X00_LIB_DEBUGFS
.debugfs = &rt2800_rt2x00debug, .debugfs = &rt2800_rt2x00debug,
#endif /* CONFIG_RT2X00_LIB_DEBUGFS */ #endif /* CONFIG_RT2X00_LIB_DEBUGFS */

View File

@ -332,6 +332,11 @@ struct link {
* Work structure for scheduling periodic link tuning. * Work structure for scheduling periodic link tuning.
*/ */
struct delayed_work work; struct delayed_work work;
/*
* Work structure for scheduling periodic watchdog monitoring.
*/
struct delayed_work watchdog_work;
}; };
/* /*
@ -509,6 +514,11 @@ struct rt2x00lib_ops {
*/ */
irq_handler_t irq_handler; irq_handler_t irq_handler;
/*
* Threaded Interrupt handlers.
*/
irq_handler_t irq_handler_thread;
/* /*
* Device init handlers. * Device init handlers.
*/ */
@ -543,6 +553,7 @@ struct rt2x00lib_ops {
struct link_qual *qual); struct link_qual *qual);
void (*link_tuner) (struct rt2x00_dev *rt2x00dev, void (*link_tuner) (struct rt2x00_dev *rt2x00dev,
struct link_qual *qual, const u32 count); struct link_qual *qual, const u32 count);
void (*watchdog) (struct rt2x00_dev *rt2x00dev);
/* /*
* TX control handlers * TX control handlers
@ -610,6 +621,7 @@ struct rt2x00_ops {
const struct data_queue_desc *bcn; const struct data_queue_desc *bcn;
const struct data_queue_desc *atim; const struct data_queue_desc *atim;
const struct rt2x00lib_ops *lib; const struct rt2x00lib_ops *lib;
const void *drv;
const struct ieee80211_ops *hw; const struct ieee80211_ops *hw;
#ifdef CONFIG_RT2X00_LIB_DEBUGFS #ifdef CONFIG_RT2X00_LIB_DEBUGFS
const struct rt2x00debug *debugfs; const struct rt2x00debug *debugfs;
@ -628,6 +640,7 @@ enum rt2x00_flags {
DEVICE_STATE_INITIALIZED, DEVICE_STATE_INITIALIZED,
DEVICE_STATE_STARTED, DEVICE_STATE_STARTED,
DEVICE_STATE_ENABLED_RADIO, DEVICE_STATE_ENABLED_RADIO,
DEVICE_STATE_SCANNING,
/* /*
* Driver requirements * Driver requirements
@ -646,6 +659,9 @@ enum rt2x00_flags {
CONFIG_SUPPORT_HW_CRYPTO, CONFIG_SUPPORT_HW_CRYPTO,
DRIVER_SUPPORT_CONTROL_FILTERS, DRIVER_SUPPORT_CONTROL_FILTERS,
DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL, DRIVER_SUPPORT_CONTROL_FILTER_PSPOLL,
DRIVER_SUPPORT_PRE_TBTT_INTERRUPT,
DRIVER_SUPPORT_LINK_TUNING,
DRIVER_SUPPORT_WATCHDOG,
/* /*
* Driver configuration * Driver configuration
@ -655,7 +671,6 @@ enum rt2x00_flags {
CONFIG_EXTERNAL_LNA_A, CONFIG_EXTERNAL_LNA_A,
CONFIG_EXTERNAL_LNA_BG, CONFIG_EXTERNAL_LNA_BG,
CONFIG_DOUBLE_ANTENNA, CONFIG_DOUBLE_ANTENNA,
CONFIG_DISABLE_LINK_TUNING,
CONFIG_CHANNEL_HT40, CONFIG_CHANNEL_HT40,
}; };
@ -863,9 +878,10 @@ struct rt2x00_dev {
const struct firmware *fw; const struct firmware *fw;
/* /*
* Driver specific data. * Interrupt values, stored between interrupt service routine
* and interrupt thread routine.
*/ */
void *priv; u32 irqvalue[2];
}; };
/* /*
@ -1052,6 +1068,7 @@ static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev,
* Interrupt context handlers. * Interrupt context handlers.
*/ */
void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev); void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev);
void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev);
void rt2x00lib_txdone(struct queue_entry *entry, void rt2x00lib_txdone(struct queue_entry *entry,
struct txdone_entry_desc *txdesc); struct txdone_entry_desc *txdesc);
void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev, void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
@ -1081,6 +1098,8 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
#else #else
#define rt2x00mac_set_key NULL #define rt2x00mac_set_key NULL
#endif /* CONFIG_RT2X00_LIB_CRYPTO */ #endif /* CONFIG_RT2X00_LIB_CRYPTO */
void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw);
void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw);
int rt2x00mac_get_stats(struct ieee80211_hw *hw, int rt2x00mac_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats); struct ieee80211_low_level_stats *stats);
void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw, void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,

View File

@ -41,10 +41,12 @@ void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev,
switch (type) { switch (type) {
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
conf.sync = TSF_SYNC_ADHOC;
break;
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_MESH_POINT:
case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_WDS:
conf.sync = TSF_SYNC_BEACON; conf.sync = TSF_SYNC_AP_NONE;
break; break;
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
conf.sync = TSF_SYNC_INFRA; conf.sync = TSF_SYNC_INFRA;

View File

@ -69,6 +69,11 @@ int rt2x00lib_enable_radio(struct rt2x00_dev *rt2x00dev)
*/ */
rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON);
/*
* Start watchdog monitoring.
*/
rt2x00link_start_watchdog(rt2x00dev);
/* /*
* Start the TX queues. * Start the TX queues.
*/ */
@ -88,6 +93,11 @@ void rt2x00lib_disable_radio(struct rt2x00_dev *rt2x00dev)
ieee80211_stop_queues(rt2x00dev->hw); ieee80211_stop_queues(rt2x00dev->hw);
rt2x00queue_stop_queues(rt2x00dev); rt2x00queue_stop_queues(rt2x00dev);
/*
* Stop watchdog monitoring.
*/
rt2x00link_stop_watchdog(rt2x00dev);
/* /*
* Disable RX. * Disable RX.
*/ */
@ -168,10 +178,32 @@ static void rt2x00lib_intf_scheduled(struct work_struct *work)
/* /*
* Interrupt context handlers. * Interrupt context handlers.
*/ */
static void rt2x00lib_beacondone_iter(void *data, u8 *mac, static void rt2x00lib_bc_buffer_iter(void *data, u8 *mac,
struct ieee80211_vif *vif) struct ieee80211_vif *vif)
{ {
struct rt2x00_intf *intf = vif_to_intf(vif); struct rt2x00_dev *rt2x00dev = data;
struct sk_buff *skb;
/*
* Only AP mode interfaces do broad- and multicast buffering
*/
if (vif->type != NL80211_IFTYPE_AP)
return;
/*
* Send out buffered broad- and multicast frames
*/
skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif);
while (skb) {
rt2x00mac_tx(rt2x00dev->hw, skb);
skb = ieee80211_get_buffered_bc(rt2x00dev->hw, vif);
}
}
static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
struct rt2x00_dev *rt2x00dev = data;
if (vif->type != NL80211_IFTYPE_AP && if (vif->type != NL80211_IFTYPE_AP &&
vif->type != NL80211_IFTYPE_ADHOC && vif->type != NL80211_IFTYPE_ADHOC &&
@ -179,9 +211,7 @@ static void rt2x00lib_beacondone_iter(void *data, u8 *mac,
vif->type != NL80211_IFTYPE_WDS) vif->type != NL80211_IFTYPE_WDS)
return; return;
spin_lock(&intf->lock); rt2x00queue_update_beacon(rt2x00dev, vif, true);
intf->delayed_flags |= DELAYED_UPDATE_BEACON;
spin_unlock(&intf->lock);
} }
void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
@ -189,14 +219,37 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return; return;
ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, /* send buffered bc/mc frames out for every bssid */
rt2x00lib_beacondone_iter, ieee80211_iterate_active_interfaces(rt2x00dev->hw,
rt2x00lib_bc_buffer_iter,
rt2x00dev); rt2x00dev);
/*
* Devices with pre tbtt interrupt don't need to update the beacon
* here as they will fetch the next beacon directly prior to
* transmission.
*/
if (test_bit(DRIVER_SUPPORT_PRE_TBTT_INTERRUPT, &rt2x00dev->flags))
return;
ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); /* fetch next beacon */
ieee80211_iterate_active_interfaces(rt2x00dev->hw,
rt2x00lib_beaconupdate_iter,
rt2x00dev);
} }
EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); EXPORT_SYMBOL_GPL(rt2x00lib_beacondone);
void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev)
{
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return;
/* fetch next beacon */
ieee80211_iterate_active_interfaces(rt2x00dev->hw,
rt2x00lib_beaconupdate_iter,
rt2x00dev);
}
EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt);
void rt2x00lib_txdone(struct queue_entry *entry, void rt2x00lib_txdone(struct queue_entry *entry,
struct txdone_entry_desc *txdesc) struct txdone_entry_desc *txdesc)
{ {
@ -330,9 +383,17 @@ void rt2x00lib_txdone(struct queue_entry *entry,
* send the status report back. * send the status report back.
*/ */
if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) if (!(skbdesc_flags & SKBDESC_NOT_MAC80211))
/*
* Only PCI and SOC devices process the tx status in process
* context. Hence use ieee80211_tx_status for PCI and SOC
* devices and stick to ieee80211_tx_status_irqsafe for USB.
*/
if (rt2x00_is_usb(rt2x00dev))
ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb); ieee80211_tx_status_irqsafe(rt2x00dev->hw, entry->skb);
else else
dev_kfree_skb_irq(entry->skb); ieee80211_tx_status(rt2x00dev->hw, entry->skb);
else
dev_kfree_skb_any(entry->skb);
/* /*
* Make this entry available for reuse. * Make this entry available for reuse.
@ -479,7 +540,16 @@ void rt2x00lib_rxdone(struct rt2x00_dev *rt2x00dev,
*/ */
rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb); rt2x00debug_dump_frame(rt2x00dev, DUMP_FRAME_RXDONE, entry->skb);
memcpy(IEEE80211_SKB_RXCB(entry->skb), rx_status, sizeof(*rx_status)); memcpy(IEEE80211_SKB_RXCB(entry->skb), rx_status, sizeof(*rx_status));
/*
* Currently only PCI and SOC devices handle rx interrupts in process
* context. Hence, use ieee80211_rx_irqsafe for USB and ieee80211_rx_ni
* for PCI and SOC devices.
*/
if (rt2x00_is_usb(rt2x00dev))
ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb); ieee80211_rx_irqsafe(rt2x00dev->hw, entry->skb);
else
ieee80211_rx_ni(rt2x00dev->hw, entry->skb);
/* /*
* Replace the skb with the freshly allocated one. * Replace the skb with the freshly allocated one.

View File

@ -30,6 +30,7 @@
/* /*
* Interval defines * Interval defines
*/ */
#define WATCHDOG_INTERVAL round_jiffies_relative(HZ)
#define LINK_TUNE_INTERVAL round_jiffies_relative(HZ) #define LINK_TUNE_INTERVAL round_jiffies_relative(HZ)
/* /*
@ -257,11 +258,30 @@ void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev);
void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna); void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna);
/** /**
* rt2x00link_register - Initialize link tuning functionality * rt2x00link_start_watchdog - Start periodic watchdog monitoring
* @rt2x00dev: Pointer to &struct rt2x00_dev. * @rt2x00dev: Pointer to &struct rt2x00_dev.
* *
* Initialize work structure and all link tuning related * This start the watchdog periodic work, this work will
* parameters. This will not start the link tuning process itself. *be executed periodically until &rt2x00link_stop_watchdog has
* been called.
*/
void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev);
/**
* rt2x00link_stop_watchdog - Stop periodic watchdog monitoring
* @rt2x00dev: Pointer to &struct rt2x00_dev.
*
* After this function completed the watchdog monitoring will not
* be running until &rt2x00link_start_watchdog is called.
*/
void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev);
/**
* rt2x00link_register - Initialize link tuning & watchdog functionality
* @rt2x00dev: Pointer to &struct rt2x00_dev.
*
* Initialize work structure and all link tuning and watchdog related
* parameters. This will not start the periodic work itself.
*/ */
void rt2x00link_register(struct rt2x00_dev *rt2x00dev); void rt2x00link_register(struct rt2x00_dev *rt2x00dev);

View File

@ -278,6 +278,15 @@ void rt2x00link_start_tuner(struct rt2x00_dev *rt2x00dev)
if (!rt2x00dev->intf_sta_count) if (!rt2x00dev->intf_sta_count)
return; return;
/**
* While scanning, link tuning is disabled. By default
* the most sensitive settings will be used to make sure
* that all beacons and probe responses will be recieved
* during the scan.
*/
if (test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
return;
rt2x00link_reset_tuner(rt2x00dev, false); rt2x00link_reset_tuner(rt2x00dev, false);
if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags)) if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
@ -293,6 +302,7 @@ void rt2x00link_stop_tuner(struct rt2x00_dev *rt2x00dev)
void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna) void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna)
{ {
struct link_qual *qual = &rt2x00dev->link.qual; struct link_qual *qual = &rt2x00dev->link.qual;
u8 vgc_level = qual->vgc_level_reg;
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return; return;
@ -308,6 +318,13 @@ void rt2x00link_reset_tuner(struct rt2x00_dev *rt2x00dev, bool antenna)
rt2x00dev->link.count = 0; rt2x00dev->link.count = 0;
memset(qual, 0, sizeof(*qual)); memset(qual, 0, sizeof(*qual));
/*
* Restore the VGC level as stored in the registers,
* the driver can use this to determine if the register
* must be updated during reset or not.
*/
qual->vgc_level_reg = vgc_level;
/* /*
* Reset the link tuner. * Reset the link tuner.
*/ */
@ -338,7 +355,8 @@ static void rt2x00link_tuner(struct work_struct *work)
* When the radio is shutting down we should * When the radio is shutting down we should
* immediately cease all link tuning. * immediately cease all link tuning.
*/ */
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags) ||
test_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags))
return; return;
/* /*
@ -359,10 +377,11 @@ static void rt2x00link_tuner(struct work_struct *work)
qual->rssi = link->avg_rssi.avg; qual->rssi = link->avg_rssi.avg;
/* /*
* Only perform the link tuning when Link tuning * Check if link tuning is supported by the hardware, some hardware
* has been enabled (This could have been disabled from the EEPROM). * do not support link tuning at all, while other devices can disable
* the feature from the EEPROM.
*/ */
if (!test_bit(CONFIG_DISABLE_LINK_TUNING, &rt2x00dev->flags)) if (test_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags))
rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count); rt2x00dev->ops->lib->link_tuner(rt2x00dev, qual, link->count);
/* /*
@ -388,7 +407,45 @@ static void rt2x00link_tuner(struct work_struct *work)
&link->work, LINK_TUNE_INTERVAL); &link->work, LINK_TUNE_INTERVAL);
} }
void rt2x00link_start_watchdog(struct rt2x00_dev *rt2x00dev)
{
struct link *link = &rt2x00dev->link;
if (!test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) ||
!test_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags))
return;
ieee80211_queue_delayed_work(rt2x00dev->hw,
&link->watchdog_work, WATCHDOG_INTERVAL);
}
void rt2x00link_stop_watchdog(struct rt2x00_dev *rt2x00dev)
{
cancel_delayed_work_sync(&rt2x00dev->link.watchdog_work);
}
static void rt2x00link_watchdog(struct work_struct *work)
{
struct rt2x00_dev *rt2x00dev =
container_of(work, struct rt2x00_dev, link.watchdog_work.work);
struct link *link = &rt2x00dev->link;
/*
* When the radio is shutting down we should
* immediately cease the watchdog monitoring.
*/
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return;
rt2x00dev->ops->lib->watchdog(rt2x00dev);
if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags))
ieee80211_queue_delayed_work(rt2x00dev->hw,
&link->watchdog_work, WATCHDOG_INTERVAL);
}
void rt2x00link_register(struct rt2x00_dev *rt2x00dev) void rt2x00link_register(struct rt2x00_dev *rt2x00dev)
{ {
INIT_DELAYED_WORK(&rt2x00dev->link.watchdog_work, rt2x00link_watchdog);
INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner); INIT_DELAYED_WORK(&rt2x00dev->link.work, rt2x00link_tuner);
} }

View File

@ -347,9 +347,11 @@ int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed)
/* /*
* Some configuration parameters (e.g. channel and antenna values) can * Some configuration parameters (e.g. channel and antenna values) can
* only be set when the radio is enabled, but do require the RX to * only be set when the radio is enabled, but do require the RX to
* be off. * be off. During this period we should keep link tuning enabled,
* if for any reason the link tuner must be reset, this will be
* handled by rt2x00lib_config().
*/ */
rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF); rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_OFF_LINK);
/* /*
* When we've just turned on the radio, we want to reprogram * When we've just turned on the radio, we want to reprogram
@ -367,7 +369,7 @@ int rt2x00mac_config(struct ieee80211_hw *hw, u32 changed)
rt2x00lib_config_antenna(rt2x00dev, rt2x00dev->default_ant); rt2x00lib_config_antenna(rt2x00dev, rt2x00dev->default_ant);
/* Turn RX back on */ /* Turn RX back on */
rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON); rt2x00lib_toggle_rx(rt2x00dev, STATE_RADIO_RX_ON_LINK);
return 0; return 0;
} }
@ -431,12 +433,36 @@ void rt2x00mac_configure_filter(struct ieee80211_hw *hw,
} }
EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter); EXPORT_SYMBOL_GPL(rt2x00mac_configure_filter);
static void rt2x00mac_set_tim_iter(void *data, u8 *mac,
struct ieee80211_vif *vif)
{
struct rt2x00_intf *intf = vif_to_intf(vif);
if (vif->type != NL80211_IFTYPE_AP &&
vif->type != NL80211_IFTYPE_ADHOC &&
vif->type != NL80211_IFTYPE_MESH_POINT &&
vif->type != NL80211_IFTYPE_WDS)
return;
spin_lock(&intf->lock);
intf->delayed_flags |= DELAYED_UPDATE_BEACON;
spin_unlock(&intf->lock);
}
int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
bool set) bool set)
{ {
struct rt2x00_dev *rt2x00dev = hw->priv; struct rt2x00_dev *rt2x00dev = hw->priv;
rt2x00lib_beacondone(rt2x00dev); if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return 0;
ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw,
rt2x00mac_set_tim_iter,
rt2x00dev);
/* queue work to upodate the beacon template */
ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(rt2x00mac_set_tim); EXPORT_SYMBOL_GPL(rt2x00mac_set_tim);
@ -540,6 +566,22 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
EXPORT_SYMBOL_GPL(rt2x00mac_set_key); EXPORT_SYMBOL_GPL(rt2x00mac_set_key);
#endif /* CONFIG_RT2X00_LIB_CRYPTO */ #endif /* CONFIG_RT2X00_LIB_CRYPTO */
void rt2x00mac_sw_scan_start(struct ieee80211_hw *hw)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
__set_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags);
rt2x00link_stop_tuner(rt2x00dev);
}
EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_start);
void rt2x00mac_sw_scan_complete(struct ieee80211_hw *hw)
{
struct rt2x00_dev *rt2x00dev = hw->priv;
__clear_bit(DEVICE_STATE_SCANNING, &rt2x00dev->flags);
rt2x00link_start_tuner(rt2x00dev);
}
EXPORT_SYMBOL_GPL(rt2x00mac_sw_scan_complete);
int rt2x00mac_get_stats(struct ieee80211_hw *hw, int rt2x00mac_get_stats(struct ieee80211_hw *hw,
struct ieee80211_low_level_stats *stats) struct ieee80211_low_level_stats *stats)
{ {

View File

@ -153,7 +153,9 @@ int rt2x00pci_initialize(struct rt2x00_dev *rt2x00dev)
/* /*
* Register interrupt handler. * Register interrupt handler.
*/ */
status = request_irq(rt2x00dev->irq, rt2x00dev->ops->lib->irq_handler, status = request_threaded_irq(rt2x00dev->irq,
rt2x00dev->ops->lib->irq_handler,
rt2x00dev->ops->lib->irq_handler_thread,
IRQF_SHARED, rt2x00dev->name, rt2x00dev); IRQF_SHARED, rt2x00dev->name, rt2x00dev);
if (status) { if (status) {
ERROR(rt2x00dev, "IRQ %d allocation failed (error %d).\n", ERROR(rt2x00dev, "IRQ %d allocation failed (error %d).\n",

View File

@ -688,9 +688,11 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
if (index == Q_INDEX) { if (index == Q_INDEX) {
queue->length++; queue->length++;
queue->last_index = jiffies;
} else if (index == Q_INDEX_DONE) { } else if (index == Q_INDEX_DONE) {
queue->length--; queue->length--;
queue->count++; queue->count++;
queue->last_index_done = jiffies;
} }
spin_unlock_irqrestore(&queue->lock, irqflags); spin_unlock_irqrestore(&queue->lock, irqflags);
@ -704,6 +706,8 @@ static void rt2x00queue_reset(struct data_queue *queue)
queue->count = 0; queue->count = 0;
queue->length = 0; queue->length = 0;
queue->last_index = jiffies;
queue->last_index_done = jiffies;
memset(queue->index, 0, sizeof(queue->index)); memset(queue->index, 0, sizeof(queue->index));
spin_unlock_irqrestore(&queue->lock, irqflags); spin_unlock_irqrestore(&queue->lock, irqflags);

View File

@ -446,6 +446,8 @@ struct data_queue {
enum data_queue_qid qid; enum data_queue_qid qid;
spinlock_t lock; spinlock_t lock;
unsigned long last_index;
unsigned long last_index_done;
unsigned int count; unsigned int count;
unsigned short limit; unsigned short limit;
unsigned short threshold; unsigned short threshold;
@ -598,6 +600,15 @@ static inline int rt2x00queue_threshold(struct data_queue *queue)
return rt2x00queue_available(queue) < queue->threshold; return rt2x00queue_available(queue) < queue->threshold;
} }
/**
* rt2x00queue_timeout - Check if a timeout occured for this queue
* @queue: Queue to check.
*/
static inline int rt2x00queue_timeout(struct data_queue *queue)
{
return time_after(queue->last_index, queue->last_index_done + (HZ / 10));
}
/** /**
* _rt2x00_desc_read - Read a word from the hardware descriptor. * _rt2x00_desc_read - Read a word from the hardware descriptor.
* @desc: Base descriptor address * @desc: Base descriptor address

View File

@ -63,7 +63,8 @@ enum led_mode {
enum tsf_sync { enum tsf_sync {
TSF_SYNC_NONE = 0, TSF_SYNC_NONE = 0,
TSF_SYNC_INFRA = 1, TSF_SYNC_INFRA = 1,
TSF_SYNC_BEACON = 2, TSF_SYNC_ADHOC = 2,
TSF_SYNC_AP_NONE = 3,
}; };
/* /*
@ -88,6 +89,8 @@ enum dev_state {
STATE_RADIO_RX_OFF_LINK, STATE_RADIO_RX_OFF_LINK,
STATE_RADIO_IRQ_ON, STATE_RADIO_IRQ_ON,
STATE_RADIO_IRQ_OFF, STATE_RADIO_IRQ_OFF,
STATE_RADIO_IRQ_ON_ISR,
STATE_RADIO_IRQ_OFF_ISR,
}; };
/* /*

View File

@ -292,6 +292,56 @@ void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
} }
EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue); EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);
static void rt2x00usb_watchdog_reset_tx(struct data_queue *queue)
{
struct queue_entry_priv_usb *entry_priv;
unsigned short threshold = queue->threshold;
WARNING(queue->rt2x00dev, "TX queue %d timed out, invoke reset", queue->qid);
/*
* Temporarily disable the TX queue, this will force mac80211
* to use the other queues until this queue has been restored.
*
* Set the queue threshold to the queue limit. This prevents the
* queue from being enabled during the txdone handler.
*/
queue->threshold = queue->limit;
ieee80211_stop_queue(queue->rt2x00dev->hw, queue->qid);
/*
* Reset all currently uploaded TX frames.
*/
while (!rt2x00queue_empty(queue)) {
entry_priv = rt2x00queue_get_entry(queue, Q_INDEX_DONE)->priv_data;
usb_kill_urb(entry_priv->urb);
/*
* We need a short delay here to wait for
* the URB to be canceled and invoked the tx_done handler.
*/
udelay(200);
}
/*
* The queue has been reset, and mac80211 is allowed to use the
* queue again.
*/
queue->threshold = threshold;
ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid);
}
void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev)
{
struct data_queue *queue;
tx_queue_for_each(rt2x00dev, queue) {
if (rt2x00queue_timeout(queue))
rt2x00usb_watchdog_reset_tx(queue);
}
}
EXPORT_SYMBOL_GPL(rt2x00usb_watchdog);
/* /*
* RX data handlers. * RX data handlers.
*/ */

View File

@ -399,6 +399,16 @@ void rt2x00usb_kick_tx_queue(struct rt2x00_dev *rt2x00dev,
void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev, void rt2x00usb_kill_tx_queue(struct rt2x00_dev *rt2x00dev,
const enum data_queue_qid qid); const enum data_queue_qid qid);
/**
* rt2x00usb_watchdog - Watchdog for USB communication
* @rt2x00dev: Pointer to &struct rt2x00_dev
*
* Check the health of the USB communication and determine
* if timeouts have occured. If this is the case, this function
* will reset all communication to restore functionality again.
*/
void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev);
/* /*
* Device initialization handlers. * Device initialization handlers.
*/ */

View File

@ -1622,7 +1622,8 @@ static void rt61pci_toggle_rx(struct rt2x00_dev *rt2x00dev,
static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev, static void rt61pci_toggle_irq(struct rt2x00_dev *rt2x00dev,
enum dev_state state) enum dev_state state)
{ {
int mask = (state == STATE_RADIO_IRQ_OFF); int mask = (state == STATE_RADIO_IRQ_OFF) ||
(state == STATE_RADIO_IRQ_OFF_ISR);
u32 reg; u32 reg;
/* /*
@ -1739,7 +1740,9 @@ static int rt61pci_set_device_state(struct rt2x00_dev *rt2x00dev,
rt61pci_toggle_rx(rt2x00dev, state); rt61pci_toggle_rx(rt2x00dev, state);
break; break;
case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_ON:
case STATE_RADIO_IRQ_ON_ISR:
case STATE_RADIO_IRQ_OFF: case STATE_RADIO_IRQ_OFF:
case STATE_RADIO_IRQ_OFF_ISR:
rt61pci_toggle_irq(rt2x00dev, state); rt61pci_toggle_irq(rt2x00dev, state);
break; break;
case STATE_DEEP_SLEEP: case STATE_DEEP_SLEEP:
@ -2147,27 +2150,11 @@ static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev)
rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS); rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
} }
static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance) static irqreturn_t rt61pci_interrupt_thread(int irq, void *dev_instance)
{ {
struct rt2x00_dev *rt2x00dev = dev_instance; struct rt2x00_dev *rt2x00dev = dev_instance;
u32 reg_mcu; u32 reg = rt2x00dev->irqvalue[0];
u32 reg; u32 reg_mcu = rt2x00dev->irqvalue[1];
/*
* Get the interrupt sources & saved to local variable.
* Write register value back to clear pending interrupts.
*/
rt2x00pci_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, &reg_mcu);
rt2x00pci_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg_mcu);
rt2x00pci_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
rt2x00pci_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
if (!reg && !reg_mcu)
return IRQ_NONE;
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return IRQ_HANDLED;
/* /*
* Handle interrupts, walk through all bits * Handle interrupts, walk through all bits
@ -2206,9 +2193,45 @@ static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance)
if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE)) if (rt2x00_get_field32(reg, INT_SOURCE_CSR_BEACON_DONE))
rt2x00lib_beacondone(rt2x00dev); rt2x00lib_beacondone(rt2x00dev);
/* Enable interrupts again. */
rt2x00dev->ops->lib->set_device_state(rt2x00dev,
STATE_RADIO_IRQ_ON_ISR);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t rt61pci_interrupt(int irq, void *dev_instance)
{
struct rt2x00_dev *rt2x00dev = dev_instance;
u32 reg_mcu;
u32 reg;
/*
* Get the interrupt sources & saved to local variable.
* Write register value back to clear pending interrupts.
*/
rt2x00pci_register_read(rt2x00dev, MCU_INT_SOURCE_CSR, &reg_mcu);
rt2x00pci_register_write(rt2x00dev, MCU_INT_SOURCE_CSR, reg_mcu);
rt2x00pci_register_read(rt2x00dev, INT_SOURCE_CSR, &reg);
rt2x00pci_register_write(rt2x00dev, INT_SOURCE_CSR, reg);
if (!reg && !reg_mcu)
return IRQ_NONE;
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return IRQ_HANDLED;
/* Store irqvalues for use in the interrupt thread. */
rt2x00dev->irqvalue[0] = reg;
rt2x00dev->irqvalue[1] = reg_mcu;
/* Disable interrupts, will be enabled again in the interrupt thread. */
rt2x00dev->ops->lib->set_device_state(rt2x00dev,
STATE_RADIO_IRQ_OFF_ISR);
return IRQ_WAKE_THREAD;
}
/* /*
* Device probe functions. * Device probe functions.
*/ */
@ -2690,6 +2713,7 @@ static int rt61pci_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags);
if (!modparam_nohwcrypt) if (!modparam_nohwcrypt)
__set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
/* /*
* Set the rssi offset. * Set the rssi offset.
@ -2781,8 +2805,9 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = {
.remove_interface = rt2x00mac_remove_interface, .remove_interface = rt2x00mac_remove_interface,
.config = rt2x00mac_config, .config = rt2x00mac_config,
.configure_filter = rt2x00mac_configure_filter, .configure_filter = rt2x00mac_configure_filter,
.set_tim = rt2x00mac_set_tim,
.set_key = rt2x00mac_set_key, .set_key = rt2x00mac_set_key,
.sw_scan_start = rt2x00mac_sw_scan_start,
.sw_scan_complete = rt2x00mac_sw_scan_complete,
.get_stats = rt2x00mac_get_stats, .get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed, .bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt61pci_conf_tx, .conf_tx = rt61pci_conf_tx,
@ -2792,6 +2817,7 @@ static const struct ieee80211_ops rt61pci_mac80211_ops = {
static const struct rt2x00lib_ops rt61pci_rt2x00_ops = { static const struct rt2x00lib_ops rt61pci_rt2x00_ops = {
.irq_handler = rt61pci_interrupt, .irq_handler = rt61pci_interrupt,
.irq_handler_thread = rt61pci_interrupt_thread,
.probe_hw = rt61pci_probe_hw, .probe_hw = rt61pci_probe_hw,
.get_firmware_name = rt61pci_get_firmware_name, .get_firmware_name = rt61pci_get_firmware_name,
.check_firmware = rt61pci_check_firmware, .check_firmware = rt61pci_check_firmware,

View File

@ -1400,7 +1400,9 @@ static int rt73usb_set_device_state(struct rt2x00_dev *rt2x00dev,
rt73usb_toggle_rx(rt2x00dev, state); rt73usb_toggle_rx(rt2x00dev, state);
break; break;
case STATE_RADIO_IRQ_ON: case STATE_RADIO_IRQ_ON:
case STATE_RADIO_IRQ_ON_ISR:
case STATE_RADIO_IRQ_OFF: case STATE_RADIO_IRQ_OFF:
case STATE_RADIO_IRQ_OFF_ISR:
/* No support, but no error either */ /* No support, but no error either */
break; break;
case STATE_DEEP_SLEEP: case STATE_DEEP_SLEEP:
@ -2135,6 +2137,8 @@ static int rt73usb_probe_hw(struct rt2x00_dev *rt2x00dev)
__set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags); __set_bit(DRIVER_REQUIRE_FIRMWARE, &rt2x00dev->flags);
if (!modparam_nohwcrypt) if (!modparam_nohwcrypt)
__set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags); __set_bit(CONFIG_SUPPORT_HW_CRYPTO, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_LINK_TUNING, &rt2x00dev->flags);
__set_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags);
/* /*
* Set the rssi offset. * Set the rssi offset.
@ -2228,6 +2232,8 @@ static const struct ieee80211_ops rt73usb_mac80211_ops = {
.configure_filter = rt2x00mac_configure_filter, .configure_filter = rt2x00mac_configure_filter,
.set_tim = rt2x00mac_set_tim, .set_tim = rt2x00mac_set_tim,
.set_key = rt2x00mac_set_key, .set_key = rt2x00mac_set_key,
.sw_scan_start = rt2x00mac_sw_scan_start,
.sw_scan_complete = rt2x00mac_sw_scan_complete,
.get_stats = rt2x00mac_get_stats, .get_stats = rt2x00mac_get_stats,
.bss_info_changed = rt2x00mac_bss_info_changed, .bss_info_changed = rt2x00mac_bss_info_changed,
.conf_tx = rt73usb_conf_tx, .conf_tx = rt73usb_conf_tx,
@ -2248,6 +2254,7 @@ static const struct rt2x00lib_ops rt73usb_rt2x00_ops = {
.link_stats = rt73usb_link_stats, .link_stats = rt73usb_link_stats,
.reset_tuner = rt73usb_reset_tuner, .reset_tuner = rt73usb_reset_tuner,
.link_tuner = rt73usb_link_tuner, .link_tuner = rt73usb_link_tuner,
.watchdog = rt2x00usb_watchdog,
.write_tx_desc = rt73usb_write_tx_desc, .write_tx_desc = rt73usb_write_tx_desc,
.write_beacon = rt73usb_write_beacon, .write_beacon = rt73usb_write_beacon,
.get_tx_data_len = rt73usb_get_tx_data_len, .get_tx_data_len = rt73usb_get_tx_data_len,

View File

@ -10,7 +10,7 @@ obj-$(CONFIG_WL1251_SDIO) += wl1251_sdio.o
wl1271-objs = wl1271_main.o wl1271_cmd.o wl1271_io.o \ wl1271-objs = wl1271_main.o wl1271_cmd.o wl1271_io.o \
wl1271_event.o wl1271_tx.o wl1271_rx.o \ wl1271_event.o wl1271_tx.o wl1271_rx.o \
wl1271_ps.o wl1271_acx.o wl1271_boot.o \ wl1271_ps.o wl1271_acx.o wl1271_boot.o \
wl1271_init.o wl1271_debugfs.o wl1271_init.o wl1271_debugfs.o wl1271_scan.o
wl1271-$(CONFIG_NL80211_TESTMODE) += wl1271_testmode.o wl1271-$(CONFIG_NL80211_TESTMODE) += wl1271_testmode.o
obj-$(CONFIG_WL1271) += wl1271.o obj-$(CONFIG_WL1271) += wl1271.o

View File

@ -1417,5 +1417,4 @@ EXPORT_SYMBOL_GPL(wl1251_free_hw);
MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core"); MODULE_DESCRIPTION("TI wl1251 Wireles LAN Driver Core");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>");
MODULE_ALIAS("spi:wl1251");
MODULE_FIRMWARE(WL1251_FW_NAME); MODULE_FIRMWARE(WL1251_FW_NAME);

View File

@ -345,3 +345,4 @@ module_exit(wl1251_spi_exit);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>"); MODULE_AUTHOR("Kalle Valo <kalle.valo@nokia.com>");
MODULE_ALIAS("spi:wl1251");

View File

@ -300,12 +300,10 @@ struct wl1271_rx_mem_pool_addr {
struct wl1271_scan { struct wl1271_scan {
struct cfg80211_scan_request *req; struct cfg80211_scan_request *req;
bool *scanned_ch;
u8 state; u8 state;
u8 ssid[IW_ESSID_MAX_SIZE+1]; u8 ssid[IW_ESSID_MAX_SIZE+1];
size_t ssid_len; size_t ssid_len;
u8 active;
u8 high_prio;
u8 probe_requests;
}; };
struct wl1271_if_operations { struct wl1271_if_operations {
@ -343,14 +341,14 @@ struct wl1271 {
#define WL1271_FLAG_JOINED (2) #define WL1271_FLAG_JOINED (2)
#define WL1271_FLAG_GPIO_POWER (3) #define WL1271_FLAG_GPIO_POWER (3)
#define WL1271_FLAG_TX_QUEUE_STOPPED (4) #define WL1271_FLAG_TX_QUEUE_STOPPED (4)
#define WL1271_FLAG_SCANNING (5) #define WL1271_FLAG_IN_ELP (5)
#define WL1271_FLAG_IN_ELP (6) #define WL1271_FLAG_PSM (6)
#define WL1271_FLAG_PSM (7) #define WL1271_FLAG_PSM_REQUESTED (7)
#define WL1271_FLAG_PSM_REQUESTED (8) #define WL1271_FLAG_IRQ_PENDING (8)
#define WL1271_FLAG_IRQ_PENDING (9) #define WL1271_FLAG_IRQ_RUNNING (9)
#define WL1271_FLAG_IRQ_RUNNING (10) #define WL1271_FLAG_IDLE (10)
#define WL1271_FLAG_IDLE (11) #define WL1271_FLAG_IDLE_REQUESTED (11)
#define WL1271_FLAG_IDLE_REQUESTED (12) #define WL1271_FLAG_PSPOLL_FAILURE (12)
unsigned long flags; unsigned long flags;
struct wl1271_partition_set part; struct wl1271_partition_set part;
@ -445,6 +443,10 @@ struct wl1271 {
struct completion *elp_compl; struct completion *elp_compl;
struct delayed_work elp_work; struct delayed_work elp_work;
struct delayed_work pspoll_work;
/* counter for ps-poll delivery failures */
int ps_poll_failures;
/* retry counter for PSM entries */ /* retry counter for PSM entries */
u8 psm_entry_retry; u8 psm_entry_retry;

View File

@ -1075,8 +1075,7 @@ out:
return ret; return ret;
} }
int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address, int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, __be32 address)
u8 version)
{ {
struct wl1271_acx_arp_filter *acx; struct wl1271_acx_arp_filter *acx;
int ret; int ret;
@ -1089,17 +1088,11 @@ int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address,
goto out; goto out;
} }
acx->version = version; acx->version = ACX_IPV4_VERSION;
acx->enable = enable; acx->enable = enable;
if (enable == true) { if (enable == true)
if (version == ACX_IPV4_VERSION) memcpy(acx->address, &address, ACX_IPV4_ADDR_SIZE);
memcpy(acx->address, address, ACX_IPV4_ADDR_SIZE);
else if (version == ACX_IPV6_VERSION)
memcpy(acx->address, address, sizeof(acx->address));
else
wl1271_error("Invalid IP version");
}
ret = wl1271_cmd_configure(wl, ACX_ARP_IP_FILTER, ret = wl1271_cmd_configure(wl, ACX_ARP_IP_FILTER,
acx, sizeof(*acx)); acx, sizeof(*acx));
@ -1266,3 +1259,29 @@ out:
kfree(acx); kfree(acx);
return ret; return ret;
} }
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime)
{
struct wl1271_acx_fw_tsf_information *tsf_info;
int ret;
tsf_info = kzalloc(sizeof(*tsf_info), GFP_KERNEL);
if (!tsf_info) {
ret = -ENOMEM;
goto out;
}
ret = wl1271_cmd_interrogate(wl, ACX_TSF_INFO,
tsf_info, sizeof(*tsf_info));
if (ret < 0) {
wl1271_warning("acx tsf info interrogate failed");
goto out;
}
*mactime = le32_to_cpu(tsf_info->current_tsf_low) |
((u64) le32_to_cpu(tsf_info->current_tsf_high) << 32);
out:
kfree(tsf_info);
return ret;
}

View File

@ -993,6 +993,17 @@ struct wl1271_acx_rssi_snr_avg_weights {
u8 snr_data; u8 snr_data;
}; };
struct wl1271_acx_fw_tsf_information {
struct acx_header header;
__le32 current_tsf_high;
__le32 current_tsf_low;
__le32 last_bttt_high;
__le32 last_tbtt_low;
u8 last_dtim_count;
u8 padding[3];
} __packed;
enum { enum {
ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_WAKE_UP_CONDITIONS = 0x0002,
ACX_MEM_CFG = 0x0003, ACX_MEM_CFG = 0x0003,
@ -1106,13 +1117,13 @@ int wl1271_acx_init_mem_config(struct wl1271 *wl);
int wl1271_acx_init_rx_interrupt(struct wl1271 *wl); int wl1271_acx_init_rx_interrupt(struct wl1271 *wl);
int wl1271_acx_smart_reflex(struct wl1271 *wl); int wl1271_acx_smart_reflex(struct wl1271 *wl);
int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable); int wl1271_acx_bet_enable(struct wl1271 *wl, bool enable);
int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, u8 *address, int wl1271_acx_arp_ip_filter(struct wl1271 *wl, bool enable, __be32 address);
u8 version);
int wl1271_acx_pm_config(struct wl1271 *wl); int wl1271_acx_pm_config(struct wl1271 *wl);
int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable); int wl1271_acx_keep_alive_mode(struct wl1271 *wl, bool enable);
int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid); int wl1271_acx_keep_alive_config(struct wl1271 *wl, u8 index, u8 tpl_valid);
int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable, int wl1271_acx_rssi_snr_trigger(struct wl1271 *wl, bool enable,
s16 thold, u8 hyst); s16 thold, u8 hyst);
int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl); int wl1271_acx_rssi_snr_avg_weights(struct wl1271 *wl);
int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime);
#endif /* __WL1271_ACX_H__ */ #endif /* __WL1271_ACX_H__ */

View File

@ -414,7 +414,9 @@ static int wl1271_boot_run_firmware(struct wl1271 *wl)
PS_REPORT_EVENT_ID | PS_REPORT_EVENT_ID |
JOIN_EVENT_COMPLETE_ID | JOIN_EVENT_COMPLETE_ID |
DISCONNECT_EVENT_COMPLETE_ID | DISCONNECT_EVENT_COMPLETE_ID |
RSSI_SNR_TRIGGER_0_EVENT_ID; RSSI_SNR_TRIGGER_0_EVENT_ID |
PSPOLL_DELIVERY_FAILURE_EVENT_ID |
SOFT_GEMINI_SENSE_EVENT_ID;
ret = wl1271_event_unmask(wl); ret = wl1271_event_unmask(wl);
if (ret < 0) { if (ret < 0) {

View File

@ -104,100 +104,6 @@ out:
return ret; return ret;
} }
static int wl1271_cmd_cal_channel_tune(struct wl1271 *wl)
{
struct wl1271_cmd_cal_channel_tune *cmd;
int ret = 0;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
cmd->test.id = TEST_CMD_CHANNEL_TUNE;
cmd->band = WL1271_CHANNEL_TUNE_BAND_2_4;
/* set up any channel, 7 is in the middle of the range */
cmd->channel = 7;
ret = wl1271_cmd_test(wl, cmd, sizeof(*cmd), 0);
if (ret < 0)
wl1271_warning("TEST_CMD_CHANNEL_TUNE failed");
kfree(cmd);
return ret;
}
static int wl1271_cmd_cal_update_ref_point(struct wl1271 *wl)
{
struct wl1271_cmd_cal_update_ref_point *cmd;
int ret = 0;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
cmd->test.id = TEST_CMD_UPDATE_PD_REFERENCE_POINT;
/* FIXME: still waiting for the correct values */
cmd->ref_power = 0;
cmd->ref_detector = 0;
cmd->sub_band = WL1271_PD_REFERENCE_POINT_BAND_B_G;
ret = wl1271_cmd_test(wl, cmd, sizeof(*cmd), 0);
if (ret < 0)
wl1271_warning("TEST_CMD_UPDATE_PD_REFERENCE_POINT failed");
kfree(cmd);
return ret;
}
static int wl1271_cmd_cal_p2g(struct wl1271 *wl)
{
struct wl1271_cmd_cal_p2g *cmd;
int ret = 0;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (!cmd)
return -ENOMEM;
cmd->test.id = TEST_CMD_P2G_CAL;
cmd->sub_band_mask = WL1271_CAL_P2G_BAND_B_G;
ret = wl1271_cmd_test(wl, cmd, sizeof(*cmd), 0);
if (ret < 0)
wl1271_warning("TEST_CMD_P2G_CAL failed");
kfree(cmd);
return ret;
}
static int wl1271_cmd_cal(struct wl1271 *wl)
{
/*
* FIXME: we must make sure that we're not sleeping when calibration
* is done
*/
int ret;
wl1271_notice("performing tx calibration");
ret = wl1271_cmd_cal_channel_tune(wl);
if (ret < 0)
return ret;
ret = wl1271_cmd_cal_update_ref_point(wl);
if (ret < 0)
return ret;
ret = wl1271_cmd_cal_p2g(wl);
if (ret < 0)
return ret;
return ret;
}
int wl1271_cmd_general_parms(struct wl1271 *wl) int wl1271_cmd_general_parms(struct wl1271 *wl)
{ {
struct wl1271_general_parms_cmd *gen_parms; struct wl1271_general_parms_cmd *gen_parms;
@ -226,7 +132,7 @@ int wl1271_cmd_general_parms(struct wl1271 *wl)
int wl1271_cmd_radio_parms(struct wl1271 *wl) int wl1271_cmd_radio_parms(struct wl1271 *wl)
{ {
struct wl1271_radio_parms_cmd *radio_parms; struct wl1271_radio_parms_cmd *radio_parms;
struct conf_radio_parms *rparam = &wl->conf.init.radioparam; struct wl1271_ini_general_params *gp = &wl->nvs->general_params;
int ret; int ret;
if (!wl->nvs) if (!wl->nvs)
@ -242,7 +148,7 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
memcpy(&radio_parms->static_params_2, &wl->nvs->stat_radio_params_2, memcpy(&radio_parms->static_params_2, &wl->nvs->stat_radio_params_2,
sizeof(struct wl1271_ini_band_params_2)); sizeof(struct wl1271_ini_band_params_2));
memcpy(&radio_parms->dyn_params_2, memcpy(&radio_parms->dyn_params_2,
&wl->nvs->dyn_radio_params_2[rparam->fem].params, &wl->nvs->dyn_radio_params_2[gp->tx_bip_fem_manufacturer].params,
sizeof(struct wl1271_ini_fem_params_2)); sizeof(struct wl1271_ini_fem_params_2));
/* 5GHz parameters */ /* 5GHz parameters */
@ -250,7 +156,7 @@ int wl1271_cmd_radio_parms(struct wl1271 *wl)
&wl->nvs->stat_radio_params_5, &wl->nvs->stat_radio_params_5,
sizeof(struct wl1271_ini_band_params_5)); sizeof(struct wl1271_ini_band_params_5));
memcpy(&radio_parms->dyn_params_5, memcpy(&radio_parms->dyn_params_5,
&wl->nvs->dyn_radio_params_5[rparam->fem].params, &wl->nvs->dyn_radio_params_5[gp->tx_bip_fem_manufacturer].params,
sizeof(struct wl1271_ini_fem_params_5)); sizeof(struct wl1271_ini_fem_params_5));
wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ", wl1271_dump(DEBUG_CMD, "TEST_CMD_INI_FILE_RADIO_PARAM: ",
@ -295,20 +201,10 @@ static int wl1271_cmd_wait_for_event(struct wl1271 *wl, u32 mask)
int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type) int wl1271_cmd_join(struct wl1271 *wl, u8 bss_type)
{ {
static bool do_cal = true;
struct wl1271_cmd_join *join; struct wl1271_cmd_join *join;
int ret, i; int ret, i;
u8 *bssid; u8 *bssid;
/* FIXME: remove when we get calibration from the factory */
if (do_cal) {
ret = wl1271_cmd_cal(wl);
if (ret < 0)
wl1271_warning("couldn't calibrate");
else
do_cal = false;
}
join = kzalloc(sizeof(*join), GFP_KERNEL); join = kzalloc(sizeof(*join), GFP_KERNEL);
if (!join) { if (!join) {
ret = -ENOMEM; ret = -ENOMEM;
@ -567,142 +463,6 @@ out:
return ret; return ret;
} }
int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
struct cfg80211_scan_request *req, u8 active_scan,
u8 high_prio, u8 band, u8 probe_requests)
{
struct wl1271_cmd_trigger_scan_to *trigger = NULL;
struct wl1271_cmd_scan *params = NULL;
struct ieee80211_channel *channels;
u32 rate;
int i, j, n_ch, ret;
u16 scan_options = 0;
u8 ieee_band;
if (band == WL1271_SCAN_BAND_2_4_GHZ) {
ieee_band = IEEE80211_BAND_2GHZ;
rate = wl->conf.tx.basic_rate;
} else if (band == WL1271_SCAN_BAND_DUAL && wl1271_11a_enabled()) {
ieee_band = IEEE80211_BAND_2GHZ;
rate = wl->conf.tx.basic_rate;
} else if (band == WL1271_SCAN_BAND_5_GHZ && wl1271_11a_enabled()) {
ieee_band = IEEE80211_BAND_5GHZ;
rate = wl->conf.tx.basic_rate_5;
} else
return -EINVAL;
if (wl->hw->wiphy->bands[ieee_band]->channels == NULL)
return -EINVAL;
channels = wl->hw->wiphy->bands[ieee_band]->channels;
n_ch = wl->hw->wiphy->bands[ieee_band]->n_channels;
if (test_bit(WL1271_FLAG_SCANNING, &wl->flags))
return -EINVAL;
params = kzalloc(sizeof(*params), GFP_KERNEL);
if (!params)
return -ENOMEM;
params->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD);
params->params.rx_filter_options =
cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN);
if (!active_scan)
scan_options |= WL1271_SCAN_OPT_PASSIVE;
if (high_prio)
scan_options |= WL1271_SCAN_OPT_PRIORITY_HIGH;
params->params.scan_options = cpu_to_le16(scan_options);
params->params.num_probe_requests = probe_requests;
params->params.tx_rate = cpu_to_le32(rate);
params->params.tid_trigger = 0;
params->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
if (band == WL1271_SCAN_BAND_DUAL)
params->params.band = WL1271_SCAN_BAND_2_4_GHZ;
else
params->params.band = band;
for (i = 0, j = 0; i < n_ch && i < WL1271_SCAN_MAX_CHANNELS; i++) {
if (!(channels[i].flags & IEEE80211_CHAN_DISABLED)) {
params->channels[j].min_duration =
cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION);
params->channels[j].max_duration =
cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION);
memset(&params->channels[j].bssid_lsb, 0xff, 4);
memset(&params->channels[j].bssid_msb, 0xff, 2);
params->channels[j].early_termination = 0;
params->channels[j].tx_power_att =
WL1271_SCAN_CURRENT_TX_PWR;
params->channels[j].channel = channels[i].hw_value;
j++;
}
}
params->params.num_channels = j;
if (ssid_len && ssid) {
params->params.ssid_len = ssid_len;
memcpy(params->params.ssid, ssid, ssid_len);
}
ret = wl1271_cmd_build_probe_req(wl, ssid, ssid_len,
req->ie, req->ie_len, ieee_band);
if (ret < 0) {
wl1271_error("PROBE request template failed");
goto out;
}
trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
if (!trigger) {
ret = -ENOMEM;
goto out;
}
/* disable the timeout */
trigger->timeout = 0;
ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
sizeof(*trigger), 0);
if (ret < 0) {
wl1271_error("trigger scan to failed for hw scan");
goto out;
}
wl1271_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params));
set_bit(WL1271_FLAG_SCANNING, &wl->flags);
if (wl1271_11a_enabled()) {
wl->scan.state = band;
if (band == WL1271_SCAN_BAND_DUAL) {
wl->scan.active = active_scan;
wl->scan.high_prio = high_prio;
wl->scan.probe_requests = probe_requests;
if (ssid_len && ssid) {
wl->scan.ssid_len = ssid_len;
memcpy(wl->scan.ssid, ssid, ssid_len);
} else
wl->scan.ssid_len = 0;
wl->scan.req = req;
} else
wl->scan.req = NULL;
}
ret = wl1271_cmd_send(wl, CMD_SCAN, params, sizeof(*params), 0);
if (ret < 0) {
wl1271_error("SCAN failed");
clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
goto out;
}
out:
kfree(params);
kfree(trigger);
return ret;
}
int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
void *buf, size_t buf_len, int index, u32 rates) void *buf, size_t buf_len, int index, u32 rates)
{ {
@ -807,7 +567,7 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, u16 aid)
goto out; goto out;
ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data, ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PS_POLL, skb->data,
skb->len, 0, wl->basic_rate); skb->len, 0, wl->basic_rate_set);
out: out:
dev_kfree_skb(skb); dev_kfree_skb(skb);

View File

@ -41,9 +41,6 @@ int wl1271_cmd_data_path(struct wl1271 *wl, bool enable);
int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send); int wl1271_cmd_ps_mode(struct wl1271 *wl, u8 ps_mode, bool send);
int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer, int wl1271_cmd_read_memory(struct wl1271 *wl, u32 addr, void *answer,
size_t len); size_t len);
int wl1271_cmd_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
struct cfg80211_scan_request *req, u8 active_scan,
u8 high_prio, u8 band, u8 probe_requests);
int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id, int wl1271_cmd_template_set(struct wl1271 *wl, u16 template_id,
void *buf, size_t buf_len, int index, u32 rates); void *buf, size_t buf_len, int index, u32 rates);
int wl1271_cmd_build_null_data(struct wl1271 *wl); int wl1271_cmd_build_null_data(struct wl1271 *wl);
@ -350,71 +347,6 @@ struct wl1271_cmd_set_keys {
__le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY]; __le32 ac_seq_num32[NUM_ACCESS_CATEGORIES_COPY];
} __packed; } __packed;
#define WL1271_SCAN_MAX_CHANNELS 24
#define WL1271_SCAN_DEFAULT_TAG 1
#define WL1271_SCAN_CURRENT_TX_PWR 0
#define WL1271_SCAN_OPT_ACTIVE 0
#define WL1271_SCAN_OPT_PASSIVE 1
#define WL1271_SCAN_OPT_PRIORITY_HIGH 4
#define WL1271_SCAN_CHAN_MIN_DURATION 30000 /* TU */
#define WL1271_SCAN_CHAN_MAX_DURATION 60000 /* TU */
#define WL1271_SCAN_BAND_2_4_GHZ 0
#define WL1271_SCAN_BAND_5_GHZ 1
#define WL1271_SCAN_BAND_DUAL 2
struct basic_scan_params {
__le32 rx_config_options;
__le32 rx_filter_options;
/* Scan option flags (WL1271_SCAN_OPT_*) */
__le16 scan_options;
/* Number of scan channels in the list (maximum 30) */
u8 num_channels;
/* This field indicates the number of probe requests to send
per channel for an active scan */
u8 num_probe_requests;
/* Rate bit field for sending the probes */
__le32 tx_rate;
u8 tid_trigger;
u8 ssid_len;
/* in order to align */
u8 padding1[2];
u8 ssid[IW_ESSID_MAX_SIZE];
/* Band to scan */
u8 band;
u8 use_ssid_list;
u8 scan_tag;
u8 padding2;
} __packed;
struct basic_scan_channel_params {
/* Duration in TU to wait for frames on a channel for active scan */
__le32 min_duration;
__le32 max_duration;
__le32 bssid_lsb;
__le16 bssid_msb;
u8 early_termination;
u8 tx_power_att;
u8 channel;
/* FW internal use only! */
u8 dfs_candidate;
u8 activity_detected;
u8 pad;
} __packed;
struct wl1271_cmd_scan {
struct wl1271_cmd_header header;
struct basic_scan_params params;
struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
} __packed;
struct wl1271_cmd_trigger_scan_to {
struct wl1271_cmd_header header;
__le32 timeout;
} __packed;
struct wl1271_cmd_test_header { struct wl1271_cmd_test_header {
u8 id; u8 id;
u8 padding[3]; u8 padding[3];

View File

@ -873,6 +873,13 @@ struct conf_conn_settings {
*/ */
u8 ps_poll_threshold; u8 ps_poll_threshold;
/*
* PS Poll failure recovery ACTIVE period length
*
* Range: u32 (ms)
*/
u32 ps_poll_recovery_period;
/* /*
* Configuration of signal average weights. * Configuration of signal average weights.
*/ */
@ -948,14 +955,6 @@ struct conf_radio_parms {
u8 fem; u8 fem;
}; };
struct conf_init_settings {
/*
* Configure radio parameters.
*/
struct conf_radio_parms radioparam;
};
struct conf_itrim_settings { struct conf_itrim_settings {
/* enable dco itrim */ /* enable dco itrim */
u8 enable; u8 enable;
@ -1022,7 +1021,6 @@ struct conf_drv_settings {
struct conf_rx_settings rx; struct conf_rx_settings rx;
struct conf_tx_settings tx; struct conf_tx_settings tx;
struct conf_conn_settings conn; struct conf_conn_settings conn;
struct conf_init_settings init;
struct conf_itrim_settings itrim; struct conf_itrim_settings itrim;
struct conf_pm_config_settings pm_config; struct conf_pm_config_settings pm_config;
struct conf_roam_trigger_settings roam_trigger; struct conf_roam_trigger_settings roam_trigger;

View File

@ -26,36 +26,64 @@
#include "wl1271_io.h" #include "wl1271_io.h"
#include "wl1271_event.h" #include "wl1271_event.h"
#include "wl1271_ps.h" #include "wl1271_ps.h"
#include "wl1271_scan.h"
#include "wl12xx_80211.h" #include "wl12xx_80211.h"
static int wl1271_event_scan_complete(struct wl1271 *wl, void wl1271_pspoll_work(struct work_struct *work)
struct event_mailbox *mbox)
{ {
wl1271_debug(DEBUG_EVENT, "status: 0x%x", struct delayed_work *dwork;
mbox->scheduled_scan_status); struct wl1271 *wl;
dwork = container_of(work, struct delayed_work, work);
wl = container_of(dwork, struct wl1271, pspoll_work);
wl1271_debug(DEBUG_EVENT, "pspoll work");
if (test_bit(WL1271_FLAG_SCANNING, &wl->flags)) {
if (wl->scan.state == WL1271_SCAN_BAND_DUAL) {
/* 2.4 GHz band scanned, scan 5 GHz band, pretend
* to the wl1271_cmd_scan function that we are not
* scanning as it checks that.
*/
clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
/* FIXME: ie missing! */
wl1271_cmd_scan(wl, wl->scan.ssid, wl->scan.ssid_len,
wl->scan.req,
wl->scan.active,
wl->scan.high_prio,
WL1271_SCAN_BAND_5_GHZ,
wl->scan.probe_requests);
} else {
mutex_unlock(&wl->mutex);
ieee80211_scan_completed(wl->hw, false);
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
clear_bit(WL1271_FLAG_SCANNING, &wl->flags);
if (!test_and_clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags))
goto out;
if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags))
goto out;
/*
* if we end up here, then we were in powersave when the pspoll
* delivery failure occurred, and no-one changed state since, so
* we should go back to powersave.
*/
wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, true);
out:
mutex_unlock(&wl->mutex);
};
static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl)
{
int delay = wl->conf.conn.ps_poll_recovery_period;
int ret;
wl->ps_poll_failures++;
if (wl->ps_poll_failures == 1)
wl1271_info("AP with dysfunctional ps-poll, "
"trying to work around it.");
/* force active mode receive data from the AP */
if (test_bit(WL1271_FLAG_PSM, &wl->flags)) {
ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, true);
if (ret < 0)
return;
set_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
ieee80211_queue_delayed_work(wl->hw, &wl->pspoll_work,
msecs_to_jiffies(delay));
} }
}
return 0; /*
* If already in active mode, lets we should be getting data from
* the AP right away. If we enter PSM too fast after this, and data
* remains on the AP, we will get another event like this, and we'll
* go into active once more.
*/
} }
static int wl1271_event_ps_report(struct wl1271 *wl, static int wl1271_event_ps_report(struct wl1271 *wl,
@ -163,9 +191,19 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector);
if (vector & SCAN_COMPLETE_EVENT_ID) { if (vector & SCAN_COMPLETE_EVENT_ID) {
ret = wl1271_event_scan_complete(wl, mbox); wl1271_debug(DEBUG_EVENT, "status: 0x%x",
if (ret < 0) mbox->scheduled_scan_status);
return ret;
wl1271_scan_stm(wl);
}
/* disable dynamic PS when requested by the firmware */
if (vector & SOFT_GEMINI_SENSE_EVENT_ID &&
wl->bss_type == BSS_TYPE_STA_BSS) {
if (mbox->soft_gemini_sense_info)
ieee80211_disable_dyn_ps(wl->vif);
else
ieee80211_enable_dyn_ps(wl->vif);
} }
/* /*
@ -191,6 +229,9 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox)
return ret; return ret;
} }
if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID)
wl1271_event_pspoll_delivery_fail(wl);
if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) {
wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT");
if (wl->vif) if (wl->vif)

View File

@ -121,5 +121,6 @@ struct event_mailbox {
int wl1271_event_unmask(struct wl1271 *wl); int wl1271_event_unmask(struct wl1271 *wl);
void wl1271_event_mbox_config(struct wl1271 *wl); void wl1271_event_mbox_config(struct wl1271 *wl);
int wl1271_event_handle(struct wl1271 *wl, u8 mbox); int wl1271_event_handle(struct wl1271 *wl, u8 mbox);
void wl1271_pspoll_work(struct work_struct *work);
#endif #endif

View File

@ -28,7 +28,6 @@
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/inetdevice.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/slab.h> #include <linux/slab.h>
@ -45,6 +44,7 @@
#include "wl1271_cmd.h" #include "wl1271_cmd.h"
#include "wl1271_boot.h" #include "wl1271_boot.h"
#include "wl1271_testmode.h" #include "wl1271_testmode.h"
#include "wl1271_scan.h"
#define WL1271_BOOT_RETRIES 3 #define WL1271_BOOT_RETRIES 3
@ -55,7 +55,7 @@ static struct conf_drv_settings default_conf = {
[CONF_SG_HV3_MAX_OVERRIDE] = 0, [CONF_SG_HV3_MAX_OVERRIDE] = 0,
[CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400, [CONF_SG_BT_NFS_SAMPLE_INTERVAL] = 400,
[CONF_SG_BT_LOAD_RATIO] = 50, [CONF_SG_BT_LOAD_RATIO] = 50,
[CONF_SG_AUTO_PS_MODE] = 0, [CONF_SG_AUTO_PS_MODE] = 1,
[CONF_SG_AUTO_SCAN_PROBE_REQ] = 170, [CONF_SG_AUTO_SCAN_PROBE_REQ] = 170,
[CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50, [CONF_SG_ACTIVE_SCAN_DURATION_FACTOR_HV3] = 50,
[CONF_SG_ANTENNA_CONFIGURATION] = 0, [CONF_SG_ANTENNA_CONFIGURATION] = 0,
@ -234,18 +234,14 @@ static struct conf_drv_settings default_conf = {
.beacon_rx_timeout = 10000, .beacon_rx_timeout = 10000,
.broadcast_timeout = 20000, .broadcast_timeout = 20000,
.rx_broadcast_in_ps = 1, .rx_broadcast_in_ps = 1,
.ps_poll_threshold = 20, .ps_poll_threshold = 10,
.ps_poll_recovery_period = 700,
.bet_enable = CONF_BET_MODE_ENABLE, .bet_enable = CONF_BET_MODE_ENABLE,
.bet_max_consecutive = 10, .bet_max_consecutive = 10,
.psm_entry_retries = 3, .psm_entry_retries = 3,
.keep_alive_interval = 55000, .keep_alive_interval = 55000,
.max_listen_interval = 20, .max_listen_interval = 20,
}, },
.init = {
.radioparam = {
.fem = 1,
}
},
.itrim = { .itrim = {
.enable = false, .enable = false,
.timeout = 50000, .timeout = 50000,
@ -818,93 +814,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
static int wl1271_dev_notify(struct notifier_block *me, unsigned long what,
void *arg)
{
struct net_device *dev;
struct wireless_dev *wdev;
struct wiphy *wiphy;
struct ieee80211_hw *hw;
struct wl1271 *wl;
struct wl1271 *wl_temp;
struct in_device *idev;
struct in_ifaddr *ifa = arg;
int ret = 0;
/* FIXME: this ugly function should probably be implemented in the
* mac80211, and here should only be a simple callback handling actual
* setting of the filters. Now we need to dig up references to
* various structures to gain access to what we need.
* Also, because of this, there is no "initial" setting of the filter
* in "op_start", because we don't want to dig up struct net_device
* there - the filter will be set upon first change of the interface
* IP address. */
dev = ifa->ifa_dev->dev;
wdev = dev->ieee80211_ptr;
if (wdev == NULL)
return NOTIFY_DONE;
wiphy = wdev->wiphy;
if (wiphy == NULL)
return NOTIFY_DONE;
hw = wiphy_priv(wiphy);
if (hw == NULL)
return NOTIFY_DONE;
/* Check that the interface is one supported by this driver. */
wl_temp = hw->priv;
list_for_each_entry(wl, &wl_list, list) {
if (wl == wl_temp)
break;
}
if (wl != wl_temp)
return NOTIFY_DONE;
/* Get the interface IP address for the device. "ifa" will become
NULL if:
- there is no IPV4 protocol address configured
- there are multiple (virtual) IPV4 addresses configured
When "ifa" is NULL, filtering will be disabled.
*/
ifa = NULL;
idev = dev->ip_ptr;
if (idev)
ifa = idev->ifa_list;
if (ifa && ifa->ifa_next)
ifa = NULL;
mutex_lock(&wl->mutex);
if (wl->state == WL1271_STATE_OFF)
goto out;
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
if (ifa)
ret = wl1271_acx_arp_ip_filter(wl, true,
(u8 *)&ifa->ifa_address,
ACX_IPV4_VERSION);
else
ret = wl1271_acx_arp_ip_filter(wl, false, NULL,
ACX_IPV4_VERSION);
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return NOTIFY_OK;
}
static struct notifier_block wl1271_dev_notifier = {
.notifier_call = wl1271_dev_notify,
};
static int wl1271_op_start(struct ieee80211_hw *hw) static int wl1271_op_start(struct ieee80211_hw *hw)
{ {
wl1271_debug(DEBUG_MAC80211, "mac80211 start"); wl1271_debug(DEBUG_MAC80211, "mac80211 start");
@ -1008,10 +917,8 @@ power_off:
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
if (!ret) { if (!ret)
list_add(&wl->list, &wl_list); list_add(&wl->list, &wl_list);
register_inetaddr_notifier(&wl1271_dev_notifier);
}
return ret; return ret;
} }
@ -1022,8 +929,6 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
int i; int i;
unregister_inetaddr_notifier(&wl1271_dev_notifier);
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface"); wl1271_debug(DEBUG_MAC80211, "mac80211 remove interface");
@ -1033,10 +938,17 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
WARN_ON(wl->state != WL1271_STATE_ON); WARN_ON(wl->state != WL1271_STATE_ON);
if (test_and_clear_bit(WL1271_FLAG_SCANNING, &wl->flags)) { /* enable dyn ps just in case (if left on due to fw crash etc) */
if (wl->bss_type == BSS_TYPE_STA_BSS)
ieee80211_enable_dyn_ps(wl->vif);
if (wl->scan.state != WL1271_SCAN_STATE_IDLE) {
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
ieee80211_scan_completed(wl->hw, true); ieee80211_scan_completed(wl->hw, true);
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
wl->scan.state = WL1271_SCAN_STATE_IDLE;
kfree(wl->scan.scanned_ch);
wl->scan.scanned_ch = NULL;
} }
wl->state = WL1271_STATE_OFF; wl->state = WL1271_STATE_OFF;
@ -1047,6 +959,7 @@ static void wl1271_op_remove_interface(struct ieee80211_hw *hw,
cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->irq_work);
cancel_work_sync(&wl->tx_work); cancel_work_sync(&wl->tx_work);
cancel_delayed_work_sync(&wl->pspoll_work);
mutex_lock(&wl->mutex); mutex_lock(&wl->mutex);
@ -1352,6 +1265,13 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed)
wl1271_warning("idle mode change failed %d", ret); wl1271_warning("idle mode change failed %d", ret);
} }
/*
* if mac80211 changes the PSM mode, make sure the mode is not
* incorrectly changed after the pspoll failure active window.
*/
if (changed & IEEE80211_CONF_CHANGE_PS)
clear_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags);
if (conf->flags & IEEE80211_CONF_PS && if (conf->flags & IEEE80211_CONF_PS &&
!test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) { !test_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags)) {
set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags); set_bit(WL1271_FLAG_PSM_REQUESTED, &wl->flags);
@ -1634,11 +1554,9 @@ static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
goto out; goto out;
if (wl1271_11a_enabled()) if (wl1271_11a_enabled())
ret = wl1271_cmd_scan(hw->priv, ssid, len, req, ret = wl1271_scan(hw->priv, ssid, len, req);
1, 0, WL1271_SCAN_BAND_DUAL, 3);
else else
ret = wl1271_cmd_scan(hw->priv, ssid, len, req, ret = wl1271_scan(hw->priv, ssid, len, req);
1, 0, WL1271_SCAN_BAND_2_4_GHZ, 3);
wl1271_ps_elp_sleep(wl); wl1271_ps_elp_sleep(wl);
@ -1811,6 +1729,8 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
wl->aid = bss_conf->aid; wl->aid = bss_conf->aid;
set_assoc = true; set_assoc = true;
wl->ps_poll_failures = 0;
/* /*
* use basic rates from AP, and determine lowest rate * use basic rates from AP, and determine lowest rate
* to use with control frames. * to use with control frames.
@ -1860,6 +1780,9 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags); clear_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags);
wl->aid = 0; wl->aid = 0;
/* re-enable dynamic ps - just in case */
ieee80211_enable_dyn_ps(wl->vif);
/* revert back to minimum rates for the current band */ /* revert back to minimum rates for the current band */
wl1271_set_band_rate(wl); wl1271_set_band_rate(wl);
wl->basic_rate = wl1271_min_rate_get(wl); wl->basic_rate = wl1271_min_rate_get(wl);
@ -1908,6 +1831,19 @@ static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw,
} }
} }
if (changed & BSS_CHANGED_ARP_FILTER) {
__be32 addr = bss_conf->arp_addr_list[0];
WARN_ON(wl->bss_type != BSS_TYPE_STA_BSS);
if (bss_conf->arp_addr_cnt == 1 && bss_conf->arp_filter_enabled)
ret = wl1271_acx_arp_ip_filter(wl, true, addr);
else
ret = wl1271_acx_arp_ip_filter(wl, false, addr);
if (ret < 0)
goto out_sleep;
}
if (do_join) { if (do_join) {
ret = wl1271_join(wl, set_assoc); ret = wl1271_join(wl, set_assoc);
if (ret < 0) { if (ret < 0) {
@ -1966,6 +1902,32 @@ out:
return ret; return ret;
} }
static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw)
{
struct wl1271 *wl = hw->priv;
u64 mactime = ULLONG_MAX;
int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 get tsf");
mutex_lock(&wl->mutex);
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
goto out;
ret = wl1271_acx_tsf_info(wl, &mactime);
if (ret < 0)
goto out_sleep;
out_sleep:
wl1271_ps_elp_sleep(wl);
out:
mutex_unlock(&wl->mutex);
return mactime;
}
/* can't be const, mac80211 writes to this */ /* can't be const, mac80211 writes to this */
static struct ieee80211_rate wl1271_rates[] = { static struct ieee80211_rate wl1271_rates[] = {
@ -2195,6 +2157,7 @@ static const struct ieee80211_ops wl1271_ops = {
.bss_info_changed = wl1271_op_bss_info_changed, .bss_info_changed = wl1271_op_bss_info_changed,
.set_rts_threshold = wl1271_op_set_rts_threshold, .set_rts_threshold = wl1271_op_set_rts_threshold,
.conf_tx = wl1271_op_conf_tx, .conf_tx = wl1271_op_conf_tx,
.get_tsf = wl1271_op_get_tsf,
CFG80211_TESTMODE_CMD(wl1271_tm_cmd) CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
}; };
@ -2407,6 +2370,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void)
skb_queue_head_init(&wl->tx_queue); skb_queue_head_init(&wl->tx_queue);
INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work);
INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work);
wl->channel = WL1271_DEFAULT_CHANNEL; wl->channel = WL1271_DEFAULT_CHANNEL;
wl->beacon_int = WL1271_DEFAULT_BEACON_INT; wl->beacon_int = WL1271_DEFAULT_BEACON_INT;
wl->default_key = 0; wl->default_key = 0;

View File

@ -53,12 +53,6 @@ static void wl1271_rx_status(struct wl1271 *wl,
status->band = wl->band; status->band = wl->band;
status->rate_idx = wl1271_rate_to_idx(wl, desc->rate); status->rate_idx = wl1271_rate_to_idx(wl, desc->rate);
/*
* FIXME: Add mactime handling. For IBSS (ad-hoc) we need to get the
* timestamp from the beacon (acx_tsf_info). In BSS mode (infra) we
* only need the mactime for monitor mode. For now the mactime is
* not valid, so RX_FLAG_TSFT should not be set
*/
status->signal = desc->rssi; status->signal = desc->rssi;
status->freq = ieee80211_channel_to_frequency(desc->channel); status->freq = ieee80211_channel_to_frequency(desc->channel);

View File

@ -0,0 +1,257 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/ieee80211.h>
#include "wl1271.h"
#include "wl1271_cmd.h"
#include "wl1271_scan.h"
#include "wl1271_acx.h"
static int wl1271_get_scan_channels(struct wl1271 *wl,
struct cfg80211_scan_request *req,
struct basic_scan_channel_params *channels,
enum ieee80211_band band, bool passive)
{
int i, j;
u32 flags;
for (i = 0, j = 0;
i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS;
i++) {
flags = req->channels[i]->flags;
if (!wl->scan.scanned_ch[i] &&
!(flags & IEEE80211_CHAN_DISABLED) &&
((!!(flags & IEEE80211_CHAN_PASSIVE_SCAN)) == passive) &&
(req->channels[i]->band == band)) {
wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ",
req->channels[i]->band,
req->channels[i]->center_freq);
wl1271_debug(DEBUG_SCAN, "hw_value %d, flags %X",
req->channels[i]->hw_value,
req->channels[i]->flags);
wl1271_debug(DEBUG_SCAN,
"max_antenna_gain %d, max_power %d",
req->channels[i]->max_antenna_gain,
req->channels[i]->max_power);
wl1271_debug(DEBUG_SCAN, "beacon_found %d",
req->channels[i]->beacon_found);
channels[j].min_duration =
cpu_to_le32(WL1271_SCAN_CHAN_MIN_DURATION);
channels[j].max_duration =
cpu_to_le32(WL1271_SCAN_CHAN_MAX_DURATION);
channels[j].early_termination = 0;
channels[j].tx_power_att = req->channels[i]->max_power;
channels[j].channel = req->channels[i]->hw_value;
memset(&channels[j].bssid_lsb, 0xff, 4);
memset(&channels[j].bssid_msb, 0xff, 2);
/* Mark the channels we already used */
wl->scan.scanned_ch[i] = true;
j++;
}
}
return j;
}
#define WL1271_NOTHING_TO_SCAN 1
static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band,
bool passive, u32 basic_rate)
{
struct wl1271_cmd_scan *cmd;
struct wl1271_cmd_trigger_scan_to *trigger;
int ret;
u16 scan_options = 0;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
trigger = kzalloc(sizeof(*trigger), GFP_KERNEL);
if (!cmd || !trigger) {
ret = -ENOMEM;
goto out;
}
/* We always use high priority scans */
scan_options = WL1271_SCAN_OPT_PRIORITY_HIGH;
if(passive)
scan_options |= WL1271_SCAN_OPT_PASSIVE;
cmd->params.scan_options = cpu_to_le16(scan_options);
cmd->params.n_ch = wl1271_get_scan_channels(wl, wl->scan.req,
cmd->channels,
band, passive);
if (cmd->params.n_ch == 0) {
ret = WL1271_NOTHING_TO_SCAN;
goto out;
}
cmd->params.tx_rate = cpu_to_le32(basic_rate);
cmd->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD);
cmd->params.rx_filter_options =
cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN);
cmd->params.n_probe_reqs = WL1271_SCAN_PROBE_REQS;
cmd->params.tx_rate = cpu_to_le32(basic_rate);
cmd->params.tid_trigger = 0;
cmd->params.scan_tag = WL1271_SCAN_DEFAULT_TAG;
if (band == IEEE80211_BAND_2GHZ)
cmd->params.band = WL1271_SCAN_BAND_2_4_GHZ;
else
cmd->params.band = WL1271_SCAN_BAND_5_GHZ;
if (wl->scan.ssid_len && wl->scan.ssid) {
cmd->params.ssid_len = wl->scan.ssid_len;
memcpy(cmd->params.ssid, wl->scan.ssid, wl->scan.ssid_len);
}
ret = wl1271_cmd_build_probe_req(wl, wl->scan.ssid, wl->scan.ssid_len,
wl->scan.req->ie, wl->scan.req->ie_len,
band);
if (ret < 0) {
wl1271_error("PROBE request template failed");
goto out;
}
/* disable the timeout */
trigger->timeout = 0;
ret = wl1271_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger,
sizeof(*trigger), 0);
if (ret < 0) {
wl1271_error("trigger scan to failed for hw scan");
goto out;
}
wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd));
ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0);
if (ret < 0) {
wl1271_error("SCAN failed");
goto out;
}
out:
kfree(cmd);
kfree(trigger);
return ret;
}
void wl1271_scan_stm(struct wl1271 *wl)
{
int ret;
switch (wl->scan.state) {
case WL1271_SCAN_STATE_IDLE:
break;
case WL1271_SCAN_STATE_2GHZ_ACTIVE:
ret = wl1271_scan_send(wl, IEEE80211_BAND_2GHZ, false,
wl->conf.tx.basic_rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE;
wl1271_scan_stm(wl);
}
break;
case WL1271_SCAN_STATE_2GHZ_PASSIVE:
ret = wl1271_scan_send(wl, IEEE80211_BAND_2GHZ, true,
wl->conf.tx.basic_rate);
if (ret == WL1271_NOTHING_TO_SCAN) {
if (wl1271_11a_enabled())
wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE;
else
wl->scan.state = WL1271_SCAN_STATE_DONE;
wl1271_scan_stm(wl);
}
break;
case WL1271_SCAN_STATE_5GHZ_ACTIVE:
ret = wl1271_scan_send(wl, IEEE80211_BAND_5GHZ, false,
wl->conf.tx.basic_rate_5);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE;
wl1271_scan_stm(wl);
}
break;
case WL1271_SCAN_STATE_5GHZ_PASSIVE:
ret = wl1271_scan_send(wl, IEEE80211_BAND_5GHZ, true,
wl->conf.tx.basic_rate_5);
if (ret == WL1271_NOTHING_TO_SCAN) {
wl->scan.state = WL1271_SCAN_STATE_DONE;
wl1271_scan_stm(wl);
}
break;
case WL1271_SCAN_STATE_DONE:
mutex_unlock(&wl->mutex);
ieee80211_scan_completed(wl->hw, false);
mutex_lock(&wl->mutex);
kfree(wl->scan.scanned_ch);
wl->scan.scanned_ch = NULL;
wl->scan.state = WL1271_SCAN_STATE_IDLE;
break;
default:
wl1271_error("invalid scan state");
break;
}
}
int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
struct cfg80211_scan_request *req)
{
if (wl->scan.state != WL1271_SCAN_STATE_IDLE)
return -EBUSY;
wl->scan.state = WL1271_SCAN_STATE_2GHZ_ACTIVE;
if (ssid_len && ssid) {
wl->scan.ssid_len = ssid_len;
memcpy(wl->scan.ssid, ssid, ssid_len);
} else {
wl->scan.ssid_len = 0;
}
wl->scan.req = req;
wl->scan.scanned_ch = kzalloc(req->n_channels *
sizeof(*wl->scan.scanned_ch),
GFP_KERNEL);
wl1271_scan_stm(wl);
return 0;
}

View File

@ -0,0 +1,109 @@
/*
* This file is part of wl1271
*
* Copyright (C) 2009-2010 Nokia Corporation
*
* Contact: Luciano Coelho <luciano.coelho@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#ifndef __WL1271_SCAN_H__
#define __WL1271_SCAN_H__
#include "wl1271.h"
int wl1271_scan(struct wl1271 *wl, const u8 *ssid, size_t ssid_len,
struct cfg80211_scan_request *req);
int wl1271_scan_build_probe_req(struct wl1271 *wl,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len, u8 band);
void wl1271_scan_stm(struct wl1271 *wl);
#define WL1271_SCAN_MAX_CHANNELS 24
#define WL1271_SCAN_DEFAULT_TAG 1
#define WL1271_SCAN_CURRENT_TX_PWR 0
#define WL1271_SCAN_OPT_ACTIVE 0
#define WL1271_SCAN_OPT_PASSIVE 1
#define WL1271_SCAN_OPT_PRIORITY_HIGH 4
#define WL1271_SCAN_CHAN_MIN_DURATION 30000 /* TU */
#define WL1271_SCAN_CHAN_MAX_DURATION 60000 /* TU */
#define WL1271_SCAN_BAND_2_4_GHZ 0
#define WL1271_SCAN_BAND_5_GHZ 1
#define WL1271_SCAN_PROBE_REQS 3
enum {
WL1271_SCAN_STATE_IDLE,
WL1271_SCAN_STATE_2GHZ_ACTIVE,
WL1271_SCAN_STATE_2GHZ_PASSIVE,
WL1271_SCAN_STATE_5GHZ_ACTIVE,
WL1271_SCAN_STATE_5GHZ_PASSIVE,
WL1271_SCAN_STATE_DONE
};
struct basic_scan_params {
__le32 rx_config_options;
__le32 rx_filter_options;
/* Scan option flags (WL1271_SCAN_OPT_*) */
__le16 scan_options;
/* Number of scan channels in the list (maximum 30) */
u8 n_ch;
/* This field indicates the number of probe requests to send
per channel for an active scan */
u8 n_probe_reqs;
/* Rate bit field for sending the probes */
__le32 tx_rate;
u8 tid_trigger;
u8 ssid_len;
/* in order to align */
u8 padding1[2];
u8 ssid[IW_ESSID_MAX_SIZE];
/* Band to scan */
u8 band;
u8 use_ssid_list;
u8 scan_tag;
u8 padding2;
} __packed;
struct basic_scan_channel_params {
/* Duration in TU to wait for frames on a channel for active scan */
__le32 min_duration;
__le32 max_duration;
__le32 bssid_lsb;
__le16 bssid_msb;
u8 early_termination;
u8 tx_power_att;
u8 channel;
/* FW internal use only! */
u8 dfs_candidate;
u8 activity_detected;
u8 pad;
} __packed;
struct wl1271_cmd_scan {
struct wl1271_cmd_header header;
struct basic_scan_params params;
struct basic_scan_channel_params channels[WL1271_SCAN_MAX_CHANNELS];
} __packed;
struct wl1271_cmd_trigger_scan_to {
struct wl1271_cmd_header header;
__le32 timeout;
} __packed;
#endif /* __WL1271_SCAN_H__ */

View File

@ -461,3 +461,4 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>");
MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>");
MODULE_FIRMWARE(WL1271_FW_NAME); MODULE_FIRMWARE(WL1271_FW_NAME);
MODULE_ALIAS("spi:wl1271");

View File

@ -143,6 +143,11 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
return -EINVAL; return -EINVAL;
} }
/* reject WEP and TKIP keys if WEP failed to initialize */
if ((alg == ALG_WEP || alg == ALG_TKIP) &&
IS_ERR(sdata->local->wep_tx_tfm))
return -EINVAL;
key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key, key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key,
params->seq_len, params->seq); params->seq_len, params->seq);
if (!key) if (!key)

View File

@ -637,11 +637,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
goto fail_sta_info; goto fail_sta_info;
result = ieee80211_wep_init(local); result = ieee80211_wep_init(local);
if (result < 0) { if (result < 0)
printk(KERN_DEBUG "%s: Failed to initialize wep: %d\n", printk(KERN_DEBUG "%s: Failed to initialize wep: %d\n",
wiphy_name(local->hw.wiphy), result); wiphy_name(local->hw.wiphy), result);
goto fail_wep;
}
rtnl_lock(); rtnl_lock();
@ -694,7 +692,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
fail_rate: fail_rate:
rtnl_unlock(); rtnl_unlock();
ieee80211_wep_free(local); ieee80211_wep_free(local);
fail_wep:
sta_info_stop(local); sta_info_stop(local);
fail_sta_info: fail_sta_info:
destroy_workqueue(local->workqueue); destroy_workqueue(local->workqueue);

View File

@ -240,6 +240,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
MINSTREL_FRAC(3, 4)) || mr->probability > cur_prob) { MINSTREL_FRAC(3, 4)) || mr->probability > cur_prob) {
mg->max_prob_rate = index; mg->max_prob_rate = index;
cur_prob = mr->probability; cur_prob = mr->probability;
cur_prob_tp = mr->cur_tp;
} }
if (mr->cur_tp > cur_tp) { if (mr->cur_tp > cur_tp) {
@ -275,6 +276,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
minstrel_mcs_groups[group].streams == 1) { minstrel_mcs_groups[group].streams == 1) {
mi->max_prob_rate = mg->max_prob_rate; mi->max_prob_rate = mg->max_prob_rate;
cur_prob = mr->cur_prob; cur_prob = mr->cur_prob;
cur_prob_tp = mr->cur_tp;
} }
mr = minstrel_get_ratestats(mi, mg->max_tp_rate); mr = minstrel_get_ratestats(mi, mg->max_tp_rate);
@ -441,8 +443,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
minstrel_downgrade_rate(mi, &mi->max_tp_rate, true); minstrel_downgrade_rate(mi, &mi->max_tp_rate, true);
rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate2); rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate2);
if (rate->attempts > 30 && if (rate2->attempts > 30 &&
MINSTREL_FRAC(rate->success, rate->attempts) < MINSTREL_FRAC(rate2->success, rate2->attempts) <
MINSTREL_FRAC(20, 100)) MINSTREL_FRAC(20, 100))
minstrel_downgrade_rate(mi, &mi->max_tp_rate2, false); minstrel_downgrade_rate(mi, &mi->max_tp_rate2, false);

View File

@ -202,7 +202,7 @@ EXPORT_SYMBOL(ieee80211_get_tkip_key);
* @payload_len is the length of payload (_not_ including IV/ICV length). * @payload_len is the length of payload (_not_ including IV/ICV length).
* @ta is the transmitter addresses. * @ta is the transmitter addresses.
*/ */
void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm, int ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm,
struct ieee80211_key *key, struct ieee80211_key *key,
u8 *pos, size_t payload_len, u8 *ta) u8 *pos, size_t payload_len, u8 *ta)
{ {
@ -216,7 +216,7 @@ void ieee80211_tkip_encrypt_data(struct crypto_blkcipher *tfm,
tkip_mixing_phase2(tk, ctx, ctx->iv16, rc4key); tkip_mixing_phase2(tk, ctx, ctx->iv16, rc4key);
ieee80211_wep_encrypt_data(tfm, rc4key, 16, pos, payload_len); return ieee80211_wep_encrypt_data(tfm, rc4key, 16, pos, payload_len);
} }
/* Decrypt packet payload with TKIP using @key. @pos is a pointer to the /* Decrypt packet payload with TKIP using @key. @pos is a pointer to the

Some files were not shown because too many files have changed in this diff Show More