diff options
186 files changed, 8572 insertions, 4414 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 5faf685..65ca7ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3426,7 +3426,7 @@ M: Wey-Yi Guy <wey-yi.w.guy@intel.com> M: Intel Linux Wireless <ilw@linux.intel.com> L: linux-wireless@vger.kernel.org W: http://intellinuxwireless.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-2.6.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi.git S: Supported F: drivers/net/wireless/iwlwifi/ @@ -4528,7 +4528,7 @@ L: linux-wireless@vger.kernel.org S: Maintained F: net/nfc/ F: include/linux/nfc.h -F: include/net/nfc.h +F: include/net/nfc/ F: drivers/nfc/ NFS, SUNRPC, AND LOCKD CLIENTS diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index 4bc10aa..2968d80 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -18,20 +18,40 @@ static u32 bcma_chipco_pll_read(struct bcma_drv_cc *cc, u32 offset) return bcma_cc_read32(cc, BCMA_CC_PLLCTL_DATA); } -static void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, - u32 offset, u32 mask, u32 set) +void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, u32 value) { - u32 value; + bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); + bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); + bcma_cc_write32(cc, BCMA_CC_PLLCTL_DATA, value); +} +EXPORT_SYMBOL_GPL(bcma_chipco_pll_write); - bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR); +void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, + u32 set) +{ + bcma_cc_write32(cc, BCMA_CC_PLLCTL_ADDR, offset); + bcma_cc_read32(cc, BCMA_CC_PLLCTL_ADDR); + bcma_cc_maskset32(cc, BCMA_CC_PLLCTL_DATA, mask, set); +} +EXPORT_SYMBOL_GPL(bcma_chipco_pll_maskset); + +void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, + u32 offset, u32 mask, u32 set) +{ bcma_cc_write32(cc, BCMA_CC_CHIPCTL_ADDR, offset); bcma_cc_read32(cc, BCMA_CC_CHIPCTL_ADDR); - value = bcma_cc_read32(cc, BCMA_CC_CHIPCTL_DATA); - value &= mask; - value |= set; - bcma_cc_write32(cc, BCMA_CC_CHIPCTL_DATA, value); - bcma_cc_read32(cc, BCMA_CC_CHIPCTL_DATA); + bcma_cc_maskset32(cc, BCMA_CC_CHIPCTL_DATA, mask, set); +} +EXPORT_SYMBOL_GPL(bcma_chipco_chipctl_maskset); + +void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, u32 offset, u32 mask, + u32 set) +{ + bcma_cc_write32(cc, BCMA_CC_REGCTL_ADDR, offset); + bcma_cc_read32(cc, BCMA_CC_REGCTL_ADDR); + bcma_cc_maskset32(cc, BCMA_CC_REGCTL_DATA, mask, set); } +EXPORT_SYMBOL_GPL(bcma_chipco_regctl_maskset); static void bcma_pmu_pll_init(struct bcma_drv_cc *cc) { diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 43ebc44..3b752d9 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1249,7 +1249,8 @@ static int adm8211_hw_reset(struct ieee80211_hw *dev) return 0; } -static u64 adm8211_get_tsft(struct ieee80211_hw *dev) +static u64 adm8211_get_tsft(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) { struct adm8211_priv *priv = dev->priv; u32 tsftl; diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 0560234..bba4f6f 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -602,7 +602,7 @@ ath5k_conf_tx(struct ieee80211_hw *hw, u16 queue, static u64 -ath5k_get_tsf(struct ieee80211_hw *hw) +ath5k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath5k_hw *ah = hw->priv; @@ -611,7 +611,7 @@ ath5k_get_tsf(struct ieee80211_hw *hw) static void -ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +ath5k_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u64 tsf) { struct ath5k_hw *ah = hw->priv; @@ -620,7 +620,7 @@ ath5k_set_tsf(struct ieee80211_hw *hw, u64 tsf) static void -ath5k_reset_tsf(struct ieee80211_hw *hw) +ath5k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath5k_hw *ah = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index d969a11..01240d6 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -273,7 +273,8 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel) immunityLevel, aniState->noiseFloor, aniState->rssiThrLow, aniState->rssiThrHigh); - aniState->ofdmNoiseImmunityLevel = immunityLevel; + if (aniState->update_ani) + aniState->ofdmNoiseImmunityLevel = immunityLevel; entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel]; entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel]; @@ -346,7 +347,8 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel) immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI) immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI; - aniState->cckNoiseImmunityLevel = immunityLevel; + if (aniState->update_ani) + aniState->cckNoiseImmunityLevel = immunityLevel; entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel]; entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel]; @@ -593,6 +595,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) aniState->ofdmNoiseImmunityLevel, aniState->cckNoiseImmunityLevel); + aniState->update_ani = false; ath9k_hw_set_ofdm_nil(ah, ATH9K_ANI_OFDM_DEF_LEVEL); ath9k_hw_set_cck_nil(ah, ATH9K_ANI_CCK_DEF_LEVEL); } @@ -609,6 +612,7 @@ void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning) aniState->ofdmNoiseImmunityLevel, aniState->cckNoiseImmunityLevel); + aniState->update_ani = true; ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel); ath9k_hw_set_cck_nil(ah, @@ -892,6 +896,8 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ani->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; + ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL; + ani->update_ani = false; } /* diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h index a547005..83029d6 100644 --- a/drivers/net/wireless/ath/ath9k/ani.h +++ b/drivers/net/wireless/ath/ath9k/ani.h @@ -122,6 +122,7 @@ struct ar5416AniState { u8 firstepLevel; u8 ofdmWeakSigDetectOff; u8 cckWeakSigThreshold; + bool update_ani; u32 listenTime; int32_t rssiThrLow; int32_t rssiThrHigh; diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index fac2c6d..0a749c8 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -704,8 +704,10 @@ static void ar5008_hw_override_ini(struct ath_hw *ah, REG_WRITE(ah, AR_PCU_MISC_MODE2, val); } - if (!AR_SREV_5416_20_OR_LATER(ah) || - AR_SREV_9280_20_OR_LATER(ah)) + REG_SET_BIT(ah, AR_PHY_CCK_DETECT, + AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + + if (AR_SREV_9280_20_OR_LATER(ah)) return; /* * Disable BB clock gating @@ -802,7 +804,8 @@ static int ar5008_hw_process_ini(struct ath_hw *ah, /* Write ADDAC shifts */ REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_EXTERNAL_RADIO); - ah->eep_ops->set_addac(ah, chan); + if (ah->eep_ops->set_addac) + ah->eep_ops->set_addac(ah, chan); if (AR_SREV_5416_22_OR_LATER(ah)) { REG_WRITE_ARRAY(&ah->iniAddac, 1, regWrites); @@ -1007,24 +1010,6 @@ static void ar5008_restore_chainmask(struct ath_hw *ah) } } -static void ar5008_set_diversity(struct ath_hw *ah, bool value) -{ - u32 v = REG_READ(ah, AR_PHY_CCK_DETECT); - if (value) - v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; - else - v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; - REG_WRITE(ah, AR_PHY_CCK_DETECT, v); -} - -static u32 ar9100_hw_compute_pll_control(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - if (chan && IS_CHAN_5GHZ(chan)) - return 0x1450; - return 0x1458; -} - static u32 ar9160_hw_compute_pll_control(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -1654,7 +1639,6 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah) priv_ops->rfbus_req = ar5008_hw_rfbus_req; priv_ops->rfbus_done = ar5008_hw_rfbus_done; priv_ops->restore_chainmask = ar5008_restore_chainmask; - priv_ops->set_diversity = ar5008_set_diversity; priv_ops->do_getnf = ar5008_hw_do_getnf; priv_ops->set_radar_params = ar5008_hw_set_radar_params; @@ -1664,9 +1648,7 @@ void ar5008_hw_attach_phy_ops(struct ath_hw *ah) } else priv_ops->ani_control = ar5008_hw_ani_control_old; - if (AR_SREV_9100(ah)) - priv_ops->compute_pll_control = ar9100_hw_compute_pll_control; - else if (AR_SREV_9160_10_OR_LATER(ah)) + if (AR_SREV_9100(ah) || AR_SREV_9160_10_OR_LATER(ah)) priv_ops->compute_pll_control = ar9160_hw_compute_pll_control; else priv_ops->compute_pll_control = ar5008_hw_compute_pll_control; diff --git a/drivers/net/wireless/ath/ath9k/ar9002_calib.c b/drivers/net/wireless/ath/ath9k/ar9002_calib.c index 2d394af..e0ab065 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_calib.c @@ -869,6 +869,7 @@ static bool ar9002_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) ar9002_hw_pa_cal(ah, true); /* Do NF Calibration after DC offset and other calibrations */ + ath9k_hw_loadnf(ah, chan); ath9k_hw_start_nfcal(ah, true); if (ah->caldata) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h index f2c6f23..08e9341 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_2p2_initvals.h @@ -1514,7 +1514,7 @@ static const u32 ar9300_2p2_mac_core[][2] = { {0x00008258, 0x00000000}, {0x0000825c, 0x40000000}, {0x00008260, 0x00080922}, - {0x00008264, 0x9bc00010}, + {0x00008264, 0x9d400010}, {0x00008268, 0xffffffff}, {0x0000826c, 0x0000ffff}, {0x00008270, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 3319a67..e4b1a83 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -652,8 +652,9 @@ static void ar9003_hw_detect_outlier(int *mp_coeff, int nmeasurement, outlier_idx = max_idx; else outlier_idx = min_idx; + + mp_coeff[outlier_idx] = mp_avg; } - mp_coeff[outlier_idx] = mp_avg; } static void ar9003_hw_tx_iqcal_load_avg_2_passes(struct ath_hw *ah, @@ -884,6 +885,7 @@ static bool ar9003_hw_init_cal(struct ath_hw *ah, if (txiqcal_done) ar9003_hw_tx_iq_cal_post_proc(ah); + ath9k_hw_loadnf(ah, chan); ath9k_hw_start_nfcal(ah, true); /* Initialize list pointers */ diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 4956d09..7db6e86 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -595,6 +595,9 @@ static void ar9003_hw_override_ini(struct ath_hw *ah) val = REG_READ(ah, AR_PCU_MISC_MODE2) & (~AR_ADHOC_MCAST_KEYID_ENABLE); REG_WRITE(ah, AR_PCU_MISC_MODE2, val | AR_AGG_WEP_ENABLE_FIX | AR_AGG_WEP_ENABLE); + + REG_SET_BIT(ah, AR_PHY_CCK_DETECT, + AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); } static void ar9003_hw_prog_ini(struct ath_hw *ah, @@ -795,16 +798,6 @@ static void ar9003_hw_rfbus_done(struct ath_hw *ah) REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0); } -static void ar9003_hw_set_diversity(struct ath_hw *ah, bool value) -{ - u32 v = REG_READ(ah, AR_PHY_CCK_DETECT); - if (value) - v |= AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; - else - v &= ~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV; - REG_WRITE(ah, AR_PHY_CCK_DETECT, v); -} - static bool ar9003_hw_ani_control(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param) { @@ -1287,7 +1280,6 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) priv_ops->set_delta_slope = ar9003_hw_set_delta_slope; priv_ops->rfbus_req = ar9003_hw_rfbus_req; priv_ops->rfbus_done = ar9003_hw_rfbus_done; - priv_ops->set_diversity = ar9003_hw_set_diversity; priv_ops->ani_control = ar9003_hw_ani_control; priv_ops->do_getnf = ar9003_hw_do_getnf; priv_ops->ani_cache_ini_regs = ar9003_hw_ani_cache_ini_regs; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 179da20..a5329c9 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -876,6 +876,15 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, TX_SAMP_DBG(rssi) = ts->ts_rssi; TX_SAMP_DBG(tid) = ts->tid; TX_SAMP_DBG(qid) = ts->qid; + + if (ts->ts_flags & ATH9K_TX_BA) { + TX_SAMP_DBG(ba_low) = ts->ba_low; + TX_SAMP_DBG(ba_high) = ts->ba_high; + } else { + TX_SAMP_DBG(ba_low) = 0; + TX_SAMP_DBG(ba_high) = 0; + } + sc->debug.tsidx = (sc->debug.tsidx + 1) % ATH_DBG_MAX_SAMPLES; spin_unlock(&sc->debug.samp_lock); @@ -1516,14 +1525,15 @@ static int open_file_bb_mac_samps(struct inode *inode, struct file *file) len += snprintf(buf + len, size - len, "Tx status Dump :\n"); len += snprintf(buf + len, size - len, "Sample rssi:- ctl0 ctl1 ctl2 ext0 ext1 ext2 comb " - "isok rts_fail data_fail rate tid qid tx_before(ms)\n"); + "isok rts_fail data_fail rate tid qid " + "ba_low ba_high tx_before(ms)\n"); for (sampidx = 0; sampidx < ATH_DBG_MAX_SAMPLES; sampidx++) { for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) { if (!ATH_SAMP_DBG(ts[i].jiffies)) continue; - len += snprintf(buf + len, size - len, "%4d \t" - "%8d %4d %4d %4d %4d %4d %4d %4d %4d " - "%4d %4d %2d %2d %d\n", + len += snprintf(buf + len, size - len, "%-14d" + "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-4d %-8d " + "%-9d %-4d %-3d %-3d %08x %08x %-11d\n", sampidx, ATH_SAMP_DBG(ts[i].rssi_ctl0), ATH_SAMP_DBG(ts[i].rssi_ctl1), @@ -1538,6 +1548,8 @@ static int open_file_bb_mac_samps(struct inode *inode, struct file *file) ATH_SAMP_DBG(ts[i].rateindex), ATH_SAMP_DBG(ts[i].tid), ATH_SAMP_DBG(ts[i].qid), + ATH_SAMP_DBG(ts[i].ba_low), + ATH_SAMP_DBG(ts[i].ba_high), jiffies_to_msecs(jiffies - ATH_SAMP_DBG(ts[i].jiffies))); } @@ -1550,8 +1562,8 @@ static int open_file_bb_mac_samps(struct inode *inode, struct file *file) for (i = 0; i < ATH_DBG_MAX_SAMPLES; i++) { if (!ATH_SAMP_DBG(rs[i].jiffies)) continue; - len += snprintf(buf + len, size - len, "%4d \t" - "%8d %4d %4d %4d %4d %4d %4d %s %4d %02x %d\n", + len += snprintf(buf + len, size - len, "%-14d" + "%-4d %-4d %-4d %-4d %-4d %-4d %-4d %-9s %-2d %02x %-13d\n", sampidx, ATH_SAMP_DBG(rs[i].rssi_ctl0), ATH_SAMP_DBG(rs[i].rssi_ctl1), diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 39f89bc..b93e88b 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -196,6 +196,8 @@ struct ath_dbg_bb_mac_samp { u8 rateindex; u8 qid; u8 tid; + u32 ba_low; + u32 ba_high; } ts[ATH_DBG_MAX_SAMPLES]; struct { u64 jiffies; diff --git a/drivers/net/wireless/ath/ath9k/eeprom.c b/drivers/net/wireless/ath/ath9k/eeprom.c index e61404d..e46f751 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom.c +++ b/drivers/net/wireless/ath/ath9k/eeprom.c @@ -456,12 +456,7 @@ void ath9k_hw_get_gain_boundaries_pdadcs(struct ath_hw *ah, pPdGainBoundaries[i] = min((u16)MAX_RATE_POWER, pPdGainBoundaries[i]); - if ((i == 0) && !AR_SREV_5416_20_OR_LATER(ah)) { - minDelta = pPdGainBoundaries[0] - 23; - pPdGainBoundaries[0] = 23; - } else { - minDelta = 0; - } + minDelta = 0; if (i == 0) { if (AR_SREV_9280_20_OR_LATER(ah)) diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index ea658e7..303560e 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -405,12 +405,7 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_GAIN_3, 0); for (i = 0; i < AR5416_EEP4K_MAX_CHAINS; i++) { - if (AR_SREV_5416_20_OR_LATER(ah) && - (ah->rxchainmask == 5 || ah->txchainmask == 5) && - (i != 0)) { - regChainOffset = (i == 1) ? 0x2000 : 0x1000; - } else - regChainOffset = i * 0x1000; + regChainOffset = i * 0x1000; if (pEepData->baseEepHeader.txMask & (1 << i)) { pRawDataset = pEepData->calPierData2G[i]; @@ -423,19 +418,17 @@ static void ath9k_hw_set_4k_power_cal_table(struct ath_hw *ah, ENABLE_REGWRITE_BUFFER(ah); - if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) { - REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, - SM(pdGainOverlap_t2, - AR_PHY_TPCRG5_PD_GAIN_OVERLAP) - | SM(gainBoundaries[0], - AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) - | SM(gainBoundaries[1], - AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) - | SM(gainBoundaries[2], - AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) - | SM(gainBoundaries[3], - AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); - } + REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, + SM(pdGainOverlap_t2, + AR_PHY_TPCRG5_PD_GAIN_OVERLAP) + | SM(gainBoundaries[0], + AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) + | SM(gainBoundaries[1], + AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) + | SM(gainBoundaries[2], + AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) + | SM(gainBoundaries[3], + AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset; for (j = 0; j < 32; j++) { @@ -715,10 +708,8 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah, if (test) return; - if (AR_SREV_9280_20_OR_LATER(ah)) { - for (i = 0; i < Ar5416RateSize; i++) - ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; - } + for (i = 0; i < Ar5416RateSize; i++) + ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; ENABLE_REGWRITE_BUFFER(ah); @@ -788,28 +779,6 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah, REGWRITE_BUFFER_FLUSH(ah); } -static void ath9k_hw_4k_set_addac(struct ath_hw *ah, - struct ath9k_channel *chan) -{ - struct modal_eep_4k_header *pModal; - struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k; - u8 biaslevel; - - if (ah->hw_version.macVersion != AR_SREV_VERSION_9160) - return; - - if (ah->eep_ops->get_eeprom_rev(ah) < AR5416_EEP_MINOR_VER_7) - return; - - pModal = &eep->modalHeader; - - if (pModal->xpaBiasLvl != 0xff) { - biaslevel = pModal->xpaBiasLvl; - INI_RA(&ah->iniAddac, 7, 1) = - (INI_RA(&ah->iniAddac, 7, 1) & (~0x18)) | biaslevel << 3; - } -} - static void ath9k_hw_4k_set_gain(struct ath_hw *ah, struct modal_eep_4k_header *pModal, struct ar5416_eeprom_4k *eep, @@ -877,6 +846,7 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, u8 txRxAttenLocal; u8 ob[5], db1[5], db2[5]; u8 ant_div_control1, ant_div_control2; + u8 bb_desired_scale; u32 regVal; pModal = &eep->modalHeader; @@ -1096,30 +1066,29 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); } - if (AR_SREV_9271(ah) || AR_SREV_9285(ah)) { - u8 bb_desired_scale = (pModal->bb_scale_smrt_antenna & - EEP_4K_BB_DESIRED_SCALE_MASK); - if ((pBase->txGainType == 0) && (bb_desired_scale != 0)) { - u32 pwrctrl, mask, clr; - - mask = BIT(0)|BIT(5)|BIT(10)|BIT(15)|BIT(20)|BIT(25); - pwrctrl = mask * bb_desired_scale; - clr = mask * 0x1f; - REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr); - REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr); - REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr); - - mask = BIT(0)|BIT(5)|BIT(15); - pwrctrl = mask * bb_desired_scale; - clr = mask * 0x1f; - REG_RMW(ah, AR_PHY_TX_PWRCTRL9, pwrctrl, clr); - - mask = BIT(0)|BIT(5); - pwrctrl = mask * bb_desired_scale; - clr = mask * 0x1f; - REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr); - REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr); - } + + bb_desired_scale = (pModal->bb_scale_smrt_antenna & + EEP_4K_BB_DESIRED_SCALE_MASK); + if ((pBase->txGainType == 0) && (bb_desired_scale != 0)) { + u32 pwrctrl, mask, clr; + + mask = BIT(0)|BIT(5)|BIT(10)|BIT(15)|BIT(20)|BIT(25); + pwrctrl = mask * bb_desired_scale; + clr = mask * 0x1f; + REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr); + REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr); + REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr); + + mask = BIT(0)|BIT(5)|BIT(15); + pwrctrl = mask * bb_desired_scale; + clr = mask * 0x1f; + REG_RMW(ah, AR_PHY_TX_PWRCTRL9, pwrctrl, clr); + + mask = BIT(0)|BIT(5); + pwrctrl = mask * bb_desired_scale; + clr = mask * 0x1f; + REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr); + REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr); } } @@ -1161,7 +1130,6 @@ const struct eeprom_ops eep_4k_ops = { .get_eeprom_ver = ath9k_hw_4k_get_eeprom_ver, .get_eeprom_rev = ath9k_hw_4k_get_eeprom_rev, .set_board_values = ath9k_hw_4k_set_board_values, - .set_addac = ath9k_hw_4k_set_addac, .set_txpower = ath9k_hw_4k_set_txpower, .get_spur_channel = ath9k_hw_4k_get_spur_channel }; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index 21f180d..6698b72 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -851,10 +851,8 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah, if (test) return; - if (AR_SREV_9280_20_OR_LATER(ah)) { - for (i = 0; i < Ar5416RateSize; i++) - ratesArray[i] -= AR9287_PWR_TABLE_OFFSET_DB * 2; - } + for (i = 0; i < Ar5416RateSize; i++) + ratesArray[i] -= AR9287_PWR_TABLE_OFFSET_DB * 2; ENABLE_REGWRITE_BUFFER(ah); @@ -944,11 +942,6 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah, REGWRITE_BUFFER_FLUSH(ah); } -static void ath9k_hw_ar9287_set_addac(struct ath_hw *ah, - struct ath9k_channel *chan) -{ -} - static void ath9k_hw_ar9287_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -1100,7 +1093,6 @@ const struct eeprom_ops eep_ar9287_ops = { .get_eeprom_ver = ath9k_hw_ar9287_get_eeprom_ver, .get_eeprom_rev = ath9k_hw_ar9287_get_eeprom_rev, .set_board_values = ath9k_hw_ar9287_set_board_values, - .set_addac = ath9k_hw_ar9287_set_addac, .set_txpower = ath9k_hw_ar9287_set_txpower, .get_spur_channel = ath9k_hw_ar9287_get_spur_channel }; diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index e7e84be..eda681f 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -547,8 +547,7 @@ static void ath9k_hw_def_set_board_values(struct ath_hw *ah, break; } - if (AR_SREV_5416_20_OR_LATER(ah) && - (ah->rxchainmask == 5 || ah->txchainmask == 5) && (i != 0)) + if ((ah->rxchainmask == 5 || ah->txchainmask == 5) && (i != 0)) regChainOffset = (i == 1) ? 0x2000 : 0x1000; else regChainOffset = i * 0x1000; @@ -565,9 +564,8 @@ static void ath9k_hw_def_set_board_values(struct ath_hw *ah, SM(pModal->iqCalQCh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); - if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) - ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal, - regChainOffset, i); + ath9k_hw_def_set_gain(ah, pModal, eep, txRxAttenLocal, + regChainOffset, i); } if (AR_SREV_9280_20_OR_LATER(ah)) { @@ -893,8 +891,7 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, xpdGainValues[2]); for (i = 0; i < AR5416_MAX_CHAINS; i++) { - if (AR_SREV_5416_20_OR_LATER(ah) && - (ah->rxchainmask == 5 || ah->txchainmask == 5) && + if ((ah->rxchainmask == 5 || ah->txchainmask == 5) && (i != 0)) { regChainOffset = (i == 1) ? 0x2000 : 0x1000; } else @@ -935,27 +932,24 @@ static void ath9k_hw_set_def_power_cal_table(struct ath_hw *ah, ENABLE_REGWRITE_BUFFER(ah); - if ((i == 0) || AR_SREV_5416_20_OR_LATER(ah)) { - if (OLC_FOR_AR9280_20_LATER) { - REG_WRITE(ah, - AR_PHY_TPCRG5 + regChainOffset, - SM(0x6, - AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | - SM_PD_GAIN(1) | SM_PD_GAIN(2) | - SM_PD_GAIN(3) | SM_PD_GAIN(4)); - } else { - REG_WRITE(ah, - AR_PHY_TPCRG5 + regChainOffset, - SM(pdGainOverlap_t2, - AR_PHY_TPCRG5_PD_GAIN_OVERLAP)| - SM_PDGAIN_B(0, 1) | - SM_PDGAIN_B(1, 2) | - SM_PDGAIN_B(2, 3) | - SM_PDGAIN_B(3, 4)); - } + if (OLC_FOR_AR9280_20_LATER) { + REG_WRITE(ah, + AR_PHY_TPCRG5 + regChainOffset, + SM(0x6, + AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | + SM_PD_GAIN(1) | SM_PD_GAIN(2) | + SM_PD_GAIN(3) | SM_PD_GAIN(4)); + } else { + REG_WRITE(ah, + AR_PHY_TPCRG5 + regChainOffset, + SM(pdGainOverlap_t2, + AR_PHY_TPCRG5_PD_GAIN_OVERLAP)| + SM_PDGAIN_B(0, 1) | + SM_PDGAIN_B(1, 2) | + SM_PDGAIN_B(2, 3) | + SM_PDGAIN_B(3, 4)); } - ath9k_adjust_pdadc_values(ah, pwr_table_offset, diff, pdadcValues); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 495fdf6..17dbbd9 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -1563,7 +1563,8 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, mutex_unlock(&priv->mutex); } -static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw) +static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; u64 tsf; @@ -1577,7 +1578,8 @@ static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw) return tsf; } -static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf) +static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf) { struct ath9k_htc_priv *priv = hw->priv; @@ -1588,7 +1590,8 @@ static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf) mutex_unlock(&priv->mutex); } -static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw) +static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct ath9k_htc_priv *priv = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 41f4bf3..e9782d1 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -181,11 +181,6 @@ static inline void ath9k_hw_restore_chainmask(struct ath_hw *ah) return ath9k_hw_private_ops(ah)->restore_chainmask(ah); } -static inline void ath9k_hw_set_diversity(struct ath_hw *ah, bool value) -{ - return ath9k_hw_private_ops(ah)->set_diversity(ah, value); -} - static inline bool ath9k_hw_ani_control(struct ath_hw *ah, enum ath9k_ani_cmd cmd, int param) { diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index bf38e2f..24889f7 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -584,7 +584,6 @@ struct ath_hw_private_ops { bool (*rfbus_req)(struct ath_hw *ah); void (*rfbus_done)(struct ath_hw *ah); void (*restore_chainmask)(struct ath_hw *ah); - void (*set_diversity)(struct ath_hw *ah, bool value); u32 (*compute_pll_control)(struct ath_hw *ah, struct ath9k_channel *chan); bool (*ani_control)(struct ath_hw *ah, enum ath9k_ani_cmd cmd, diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 9b34c4b..39514de 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -506,7 +506,6 @@ static void ath9k_init_misc(struct ath_softc *sc) sc->sc_flags |= SC_OP_RXAGGR; } - ath9k_hw_set_diversity(sc->sc_ah, true); sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah); memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 786587a..22f23ea 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -584,7 +584,7 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, else rs->rs_keyix = ATH9K_RXKEYIX_INVALID; - rs->rs_rate = RXSTATUS_RATE(ah, (&ads)); + rs->rs_rate = MS(ads.ds_rxstatus0, AR_RxRate); rs->rs_more = (ads.ds_rxstatus1 & AR_RxMore) ? 1 : 0; rs->rs_isaggr = (ads.ds_rxstatus8 & AR_RxAggr) ? 1 : 0; diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index ac5a1d2..91c9654 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -17,10 +17,6 @@ #ifndef MAC_H #define MAC_H -#define RXSTATUS_RATE(ah, ads) (AR_SREV_5416_20_OR_LATER(ah) ? \ - MS(ads->ds_rxstatus0, AR_RxRate) : \ - (ads->ds_rxstatus3 >> 2) & 0xFF) - #define set11nTries(_series, _index) \ (SM((_series)[_index].Tries, AR_XmitDataTries##_index)) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a16f539..edaa784 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -133,7 +133,7 @@ void ath9k_ps_restore(struct ath_softc *sc) ath_hw_cycle_counters_update(common); spin_unlock(&common->cc_lock); - ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); + ath9k_hw_setpower(sc->sc_ah, mode); unlock: spin_unlock_irqrestore(&sc->sc_pm_lock, flags); @@ -2021,6 +2021,7 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif) /* Stop ANI */ sc->sc_flags &= ~SC_OP_ANI_RUN; del_timer_sync(&common->ani.timer); + memset(&sc->caldata, 0, sizeof(sc->caldata)); } } @@ -2142,7 +2143,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath9k_ps_restore(sc); } -static u64 ath9k_get_tsf(struct ieee80211_hw *hw) +static u64 ath9k_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; u64 tsf; @@ -2156,7 +2157,9 @@ static u64 ath9k_get_tsf(struct ieee80211_hw *hw) return tsf; } -static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf) +static void ath9k_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u64 tsf) { struct ath_softc *sc = hw->priv; @@ -2167,7 +2170,7 @@ static void ath9k_set_tsf(struct ieee80211_hw *hw, u64 tsf) mutex_unlock(&sc->mutex); } -static void ath9k_reset_tsf(struct ieee80211_hw *hw) +static void ath9k_reset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct ath_softc *sc = hw->priv; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index bcc0b22..4984350 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -205,14 +205,22 @@ static void ath_rx_remove_buffer(struct ath_softc *sc, static void ath_rx_edma_cleanup(struct ath_softc *sc) { + struct ath_hw *ah = sc->sc_ah; + struct ath_common *common = ath9k_hw_common(ah); struct ath_buf *bf; ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_LP); ath_rx_remove_buffer(sc, ATH9K_RX_QUEUE_HP); list_for_each_entry(bf, &sc->rx.rxbuf, list) { - if (bf->bf_mpdu) + if (bf->bf_mpdu) { + dma_unmap_single(sc->dev, bf->bf_buf_addr, + common->rx_bufsize, + DMA_BIDIRECTIONAL); dev_kfree_skb_any(bf->bf_mpdu); + bf->bf_buf_addr = 0; + bf->bf_mpdu = NULL; + } } INIT_LIST_HEAD(&sc->rx.rxbuf); diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 0846654..b76c49d 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -803,10 +803,6 @@ #define AR_SREV_5416(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \ ((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCIE)) -#define AR_SREV_5416_20_OR_LATER(_ah) \ - (((AR_SREV_5416(_ah)) && \ - ((_ah)->hw_version.macRev >= AR_SREV_REVISION_5416_20)) || \ - ((_ah)->hw_version.macVersion >= AR_SREV_VERSION_9100)) #define AR_SREV_5416_22_OR_LATER(_ah) \ (((AR_SREV_5416(_ah)) && \ ((_ah)->hw_version.macRev >= AR_SREV_REVISION_5416_22)) || \ diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 2c6aefa..fa3dcfd 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -262,6 +262,7 @@ static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq, struct sk_buff *skb) { struct ath_frame_info *fi = get_frame_info(skb); + struct ath_buf *bf = fi->bf; struct ieee80211_hdr *hdr; TX_STAT_INC(txq->axq_qnum, a_retries); @@ -270,6 +271,8 @@ static void ath_tx_set_retry(struct ath_softc *sc, struct ath_txq *txq, hdr = (struct ieee80211_hdr *)skb->data; hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_RETRY); + dma_sync_single_for_device(sc->dev, bf->bf_buf_addr, + sizeof(*hdr), DMA_TO_DEVICE); } static struct ath_buf *ath_tx_get_buffer(struct ath_softc *sc) @@ -1017,8 +1020,6 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, while (bf) { struct sk_buff *skb = bf->bf_mpdu; struct ath_frame_info *fi = get_frame_info(skb); - struct ieee80211_hdr *hdr; - int padpos, padsize; info.type = get_hw_packet_type(skb); if (bf->bf_next) @@ -1026,20 +1027,8 @@ static void ath_tx_fill_desc(struct ath_softc *sc, struct ath_buf *bf, else info.link = 0; - if (ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) { - hdr = (struct ieee80211_hdr *)skb->data; - padpos = ath9k_cmn_padpos(hdr->frame_control); - padsize = padpos & 3; - - info.buf_addr[0] = bf->bf_buf_addr; - info.buf_len[0] = padpos + padsize; - info.buf_addr[1] = info.buf_addr[0] + padpos; - info.buf_len[1] = skb->len - padpos; - } else { - info.buf_addr[0] = bf->bf_buf_addr; - info.buf_len[0] = skb->len; - } - + info.buf_addr[0] = bf->bf_buf_addr; + info.buf_len[0] = skb->len; info.pkt_len = fi->framelen; info.keyix = fi->keyix; info.keytype = fi->keytype; @@ -1892,17 +1881,16 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no); } - if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)) { - /* Add the padding after the header if this is not already done */ - padpos = ath9k_cmn_padpos(hdr->frame_control); - padsize = padpos & 3; - if (padsize && skb->len > padpos) { - if (skb_headroom(skb) < padsize) - return -ENOMEM; + /* Add the padding after the header if this is not already done */ + padpos = ath9k_cmn_padpos(hdr->frame_control); + padsize = padpos & 3; + if (padsize && skb->len > padpos) { + if (skb_headroom(skb) < padsize) + return -ENOMEM; - skb_push(skb, padsize); - memmove(skb->data, skb->data + padsize, padpos); - } + skb_push(skb, padsize); + memmove(skb->data, skb->data + padsize, padpos); + hdr = (struct ieee80211_hdr *) skb->data; } if ((vif && vif->type != NL80211_IFTYPE_AP && @@ -1952,17 +1940,15 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, /* Frame was ACKed */ tx_info->flags |= IEEE80211_TX_STAT_ACK; - if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)) { - padpos = ath9k_cmn_padpos(hdr->frame_control); - padsize = padpos & 3; - if (padsize && skb->len>padpos+padsize) { - /* - * Remove MAC header padding before giving the frame back to - * mac80211. - */ - memmove(skb->data + padsize, skb->data, padpos); - skb_pull(skb, padsize); - } + padpos = ath9k_cmn_padpos(hdr->frame_control); + padsize = padpos & 3; + if (padsize && skb->len>padpos+padsize) { + /* + * Remove MAC header padding before giving the frame back to + * mac80211. + */ + memmove(skb->data + padsize, skb->data, padpos); + skb_pull(skb, padsize); } if (sc->ps_flags & PS_WAIT_FOR_TX_ACK) { diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index af351ec..8b780d6 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1078,7 +1078,8 @@ out: mutex_unlock(&ar->mutex); } -static u64 carl9170_op_get_tsf(struct ieee80211_hw *hw) +static u64 carl9170_op_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct ar9170 *ar = hw->priv; struct carl9170_tsf_rsp tsf; diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index f8615cd..447a230 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -107,6 +107,40 @@ #define B43_MMIO_RADIO_HWENABLED_LO 0x49A #define B43_MMIO_GPIO_CONTROL 0x49C #define B43_MMIO_GPIO_MASK 0x49E +#define B43_MMIO_TXE0_CTL 0x500 +#define B43_MMIO_TXE0_AUX 0x502 +#define B43_MMIO_TXE0_TS_LOC 0x504 +#define B43_MMIO_TXE0_TIME_OUT 0x506 +#define B43_MMIO_TXE0_WM_0 0x508 +#define B43_MMIO_TXE0_WM_1 0x50A +#define B43_MMIO_TXE0_PHYCTL 0x50C +#define B43_MMIO_TXE0_STATUS 0x50E +#define B43_MMIO_TXE0_MMPLCP0 0x510 +#define B43_MMIO_TXE0_MMPLCP1 0x512 +#define B43_MMIO_TXE0_PHYCTL1 0x514 +#define B43_MMIO_XMTFIFODEF 0x520 +#define B43_MMIO_XMTFIFO_FRAME_CNT 0x522 /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFO_BYTE_CNT 0x524 /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFO_HEAD 0x526 /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFO_RD_PTR 0x528 /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFO_WR_PTR 0x52A /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFODEF1 0x52C /* core rev>= 16 only */ +#define B43_MMIO_XMTFIFOCMD 0x540 +#define B43_MMIO_XMTFIFOFLUSH 0x542 +#define B43_MMIO_XMTFIFOTHRESH 0x544 +#define B43_MMIO_XMTFIFORDY 0x546 +#define B43_MMIO_XMTFIFOPRIRDY 0x548 +#define B43_MMIO_XMTFIFORQPRI 0x54A +#define B43_MMIO_XMTTPLATETXPTR 0x54C +#define B43_MMIO_XMTTPLATEPTR 0x550 +#define B43_MMIO_SMPL_CLCT_STRPTR 0x552 /* core rev>= 22 only */ +#define B43_MMIO_SMPL_CLCT_STPPTR 0x554 /* core rev>= 22 only */ +#define B43_MMIO_SMPL_CLCT_CURPTR 0x556 /* core rev>= 22 only */ +#define B43_MMIO_XMTTPLATEDATALO 0x560 +#define B43_MMIO_XMTTPLATEDATAHI 0x562 +#define B43_MMIO_XMTSEL 0x568 +#define B43_MMIO_XMTTXCNT 0x56A +#define B43_MMIO_XMTTXSHMADDR 0x56C #define B43_MMIO_TSF_CFP_START_LOW 0x604 #define B43_MMIO_TSF_CFP_START_HIGH 0x606 #define B43_MMIO_TSF_CFP_PRETBTT 0x612 @@ -118,12 +152,16 @@ #define B43_MMIO_TSF_3 0x638 /* core rev < 3 only */ #define B43_MMIO_RNG 0x65A #define B43_MMIO_IFSSLOT 0x684 /* Interframe slot time */ -#define B43_MMIO_IFSCTL 0x688 /* Interframe space control */ +#define B43_MMIO_IFSCTL 0x688 /* Interframe space control */ +#define B43_MMIO_IFSSTAT 0x690 +#define B43_MMIO_IFSMEDBUSYCTL 0x692 +#define B43_MMIO_IFTXDUR 0x694 #define B43_MMIO_IFSCTL_USE_EDCF 0x0004 #define B43_MMIO_POWERUP_DELAY 0x6A8 #define B43_MMIO_BTCOEX_CTL 0x6B4 /* Bluetooth Coexistence Control */ #define B43_MMIO_BTCOEX_STAT 0x6B6 /* Bluetooth Coexistence Status */ #define B43_MMIO_BTCOEX_TXCTL 0x6B8 /* Bluetooth Coexistence Transmit Control */ +#define B43_MMIO_WEPCTL 0x7C0 /* SPROM boardflags_lo values */ #define B43_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 56fa3a3..43400fb 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -729,52 +729,59 @@ void b43_dummy_transmission(struct b43_wldev *dev, bool ofdm, bool pa_on) for (i = 0; i < 5; i++) b43_ram_write(dev, i * 4, buffer[i]); - b43_write16(dev, 0x0568, 0x0000); + b43_write16(dev, B43_MMIO_XMTSEL, 0x0000); + if (dev->dev->core_rev < 11) - b43_write16(dev, 0x07C0, 0x0000); + b43_write16(dev, B43_MMIO_WEPCTL, 0x0000); else - b43_write16(dev, 0x07C0, 0x0100); + b43_write16(dev, B43_MMIO_WEPCTL, 0x0100); + value = (ofdm ? 0x41 : 0x40); - b43_write16(dev, 0x050C, value); - if ((phy->type == B43_PHYTYPE_N) || (phy->type == B43_PHYTYPE_LP)) - b43_write16(dev, 0x0514, 0x1A02); - b43_write16(dev, 0x0508, 0x0000); - b43_write16(dev, 0x050A, 0x0000); - b43_write16(dev, 0x054C, 0x0000); - b43_write16(dev, 0x056A, 0x0014); - b43_write16(dev, 0x0568, 0x0826); - b43_write16(dev, 0x0500, 0x0000); - if (!pa_on && (phy->type == B43_PHYTYPE_N)) { - //SPEC TODO - } + b43_write16(dev, B43_MMIO_TXE0_PHYCTL, value); + if (phy->type == B43_PHYTYPE_N || phy->type == B43_PHYTYPE_LP || + phy->type == B43_PHYTYPE_LCN) + b43_write16(dev, B43_MMIO_TXE0_PHYCTL1, 0x1A02); + + b43_write16(dev, B43_MMIO_TXE0_WM_0, 0x0000); + b43_write16(dev, B43_MMIO_TXE0_WM_1, 0x0000); + + b43_write16(dev, B43_MMIO_XMTTPLATETXPTR, 0x0000); + b43_write16(dev, B43_MMIO_XMTTXCNT, 0x0014); + b43_write16(dev, B43_MMIO_XMTSEL, 0x0826); + b43_write16(dev, B43_MMIO_TXE0_CTL, 0x0000); + + if (!pa_on && phy->type == B43_PHYTYPE_N) + ; /*b43_nphy_pa_override(dev, false) */ switch (phy->type) { case B43_PHYTYPE_N: - b43_write16(dev, 0x0502, 0x00D0); + case B43_PHYTYPE_LCN: + b43_write16(dev, B43_MMIO_TXE0_AUX, 0x00D0); break; case B43_PHYTYPE_LP: - b43_write16(dev, 0x0502, 0x0050); + b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0050); break; default: - b43_write16(dev, 0x0502, 0x0030); + b43_write16(dev, B43_MMIO_TXE0_AUX, 0x0030); } + b43_read16(dev, B43_MMIO_TXE0_AUX); if (phy->radio_ver == 0x2050 && phy->radio_rev <= 0x5) b43_radio_write16(dev, 0x0051, 0x0017); for (i = 0x00; i < max_loop; i++) { - value = b43_read16(dev, 0x050E); + value = b43_read16(dev, B43_MMIO_TXE0_STATUS); if (value & 0x0080) break; udelay(10); } for (i = 0x00; i < 0x0A; i++) { - value = b43_read16(dev, 0x050E); + value = b43_read16(dev, B43_MMIO_TXE0_STATUS); if (value & 0x0400) break; udelay(10); } for (i = 0x00; i < 0x19; i++) { - value = b43_read16(dev, 0x0690); + value = b43_read16(dev, B43_MMIO_IFSSTAT); if (!(value & 0x0100)) break; udelay(10); @@ -3599,7 +3606,7 @@ static int b43_op_get_stats(struct ieee80211_hw *hw, return 0; } -static u64 b43_op_get_tsf(struct ieee80211_hw *hw) +static u64 b43_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; @@ -3618,7 +3625,8 @@ static u64 b43_op_get_tsf(struct ieee80211_hw *hw) return tsf; } -static void b43_op_set_tsf(struct ieee80211_hw *hw, u64 tsf) +static void b43_op_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf) { struct b43_wl *wl = hw_to_b43_wl(hw); struct b43_wldev *dev; diff --git a/drivers/net/wireless/b43/phy_lcn.c b/drivers/net/wireless/b43/phy_lcn.c index d1dfeec..a13e28e 100644 --- a/drivers/net/wireless/b43/phy_lcn.c +++ b/drivers/net/wireless/b43/phy_lcn.c @@ -20,6 +20,14 @@ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. + This file incorporates work covered by the following copyright and + permission notice: + + Copyright (c) 2010 Broadcom Corporation + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. */ #include <linux/slab.h> @@ -29,6 +37,56 @@ #include "tables_phy_lcn.h" #include "main.h" +struct lcn_tx_gains { + u16 gm_gain; + u16 pga_gain; + u16 pad_gain; + u16 dac_gain; +}; + +struct lcn_tx_iir_filter { + u8 type; + u16 values[16]; +}; + +enum lcn_sense_type { + B43_SENSE_TEMP, + B43_SENSE_VBAT, +}; + +/* In theory it's PHY common function, move if needed */ +/* brcms_b_switch_macfreq */ +static void b43_phy_switch_macfreq(struct b43_wldev *dev, u8 spurmode) +{ + if (dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) { + switch (spurmode) { + case 2: /* 126 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + case 1: /* 123 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + default: /* 120 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + } + } else if (dev->phy.type == B43_PHYTYPE_LCN) { + switch (spurmode) { + case 1: /* 82 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); + break; + default: /* 80 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); + break; + } + } +} + /************************************************** * Radio 2064. **************************************************/ @@ -152,6 +210,29 @@ static void b43_phy_lcn_afe_set_unset(struct b43_wldev *dev) b43_phy_write(dev, B43_PHY_LCN_AFE_CTL1, afe_ctl1); } +/* wlc_lcnphy_get_pa_gain */ +static u16 b43_phy_lcn_get_pa_gain(struct b43_wldev *dev) +{ + return (b43_phy_read(dev, 0x4fb) & 0x7f00) >> 8; +} + +/* wlc_lcnphy_set_dac_gain */ +static void b43_phy_lcn_set_dac_gain(struct b43_wldev *dev, u16 dac_gain) +{ + u16 dac_ctrl; + + dac_ctrl = b43_phy_read(dev, 0x439); + dac_ctrl = dac_ctrl & 0xc7f; + dac_ctrl = dac_ctrl | (dac_gain << 7); + b43_phy_maskset(dev, 0x439, ~0xfff, dac_ctrl); +} + +/* wlc_lcnphy_set_bbmult */ +static void b43_phy_lcn_set_bbmult(struct b43_wldev *dev, u8 m0) +{ + b43_lcntab_write(dev, B43_LCNTAB16(0x00, 0x57), m0 << 8); +} + /* wlc_lcnphy_clear_tx_power_offsets */ static void b43_phy_lcn_clear_tx_power_offsets(struct b43_wldev *dev) { @@ -231,18 +312,40 @@ static void b43_phy_lcn_bu_tweaks(struct b43_wldev *dev) b43_phy_write(dev, 0x7d6, 0x0902); - /* TODO: more ops */ + b43_phy_maskset(dev, 0x429, ~0xf, 0x9); + b43_phy_maskset(dev, 0x429, ~(0x3f << 4), 0xe << 4); if (dev->phy.rev == 1) { - /* TODO: more ops */ + b43_phy_maskset(dev, 0x423, ~0xff, 0x46); + b43_phy_maskset(dev, 0x411, ~0xff, 1); + b43_phy_set(dev, 0x434, 0xff); /* FIXME: update to wl */ + + /* TODO: wl operates on PHY 0x416, brcmsmac is outdated here */ + + b43_phy_maskset(dev, 0x656, ~0xf, 2); + b43_phy_set(dev, 0x44d, 4); + + b43_radio_set(dev, 0x0f7, 0x4); + b43_radio_mask(dev, 0x0f1, ~0x3); + b43_radio_maskset(dev, 0x0f2, ~0xf8, 0x90); + b43_radio_maskset(dev, 0x0f3, ~0x3, 0x2); + b43_radio_maskset(dev, 0x0f3, ~0xf0, 0xa0); + + b43_radio_set(dev, 0x11f, 0x2); b43_phy_lcn_clear_tx_power_offsets(dev); + + /* TODO: something more? */ } } /* wlc_lcnphy_vbat_temp_sense_setup */ -static void b43_phy_lcn_sense_setup(struct b43_wldev *dev) +static void b43_phy_lcn_sense_setup(struct b43_wldev *dev, + enum lcn_sense_type sense_type) { + u8 auxpga_vmidcourse, auxpga_vmidfine, auxpga_gain; + u16 auxpga_vmid; + u8 tx_pwr_idx; u8 i; u16 save_radio_regs[6][2] = { @@ -257,35 +360,312 @@ static void b43_phy_lcn_sense_setup(struct b43_wldev *dev) }; u16 save_radio_4a4; + msleep(1); + + /* Save */ for (i = 0; i < 6; i++) save_radio_regs[i][1] = b43_radio_read(dev, save_radio_regs[i][0]); for (i = 0; i < 14; i++) save_phy_regs[i][1] = b43_phy_read(dev, save_phy_regs[i][0]); + b43_mac_suspend(dev); save_radio_4a4 = b43_radio_read(dev, 0x4a4); + /* wlc_lcnphy_set_tx_pwr_ctrl(pi, LCNPHY_TX_PWR_CTRL_OFF); */ + tx_pwr_idx = dev->phy.lcn->tx_pwr_curr_idx; + + /* Setup */ + /* TODO: wlc_lcnphy_set_tx_pwr_by_index(pi, 127); */ + b43_radio_set(dev, 0x007, 0x1); + b43_radio_set(dev, 0x0ff, 0x10); + b43_radio_set(dev, 0x11f, 0x4); + + b43_phy_mask(dev, 0x503, ~0x1); + b43_phy_mask(dev, 0x503, ~0x4); + b43_phy_mask(dev, 0x4a4, ~0x4000); + b43_phy_mask(dev, 0x4a4, (u16) ~0x8000); + b43_phy_mask(dev, 0x4d0, ~0x20); + b43_phy_set(dev, 0x4a5, 0xff); + b43_phy_maskset(dev, 0x4a5, ~0x7000, 0x5000); + b43_phy_mask(dev, 0x4a5, ~0x700); + b43_phy_maskset(dev, 0x40d, ~0xff, 64); + b43_phy_maskset(dev, 0x40d, ~0x700, 0x600); + b43_phy_maskset(dev, 0x4a2, ~0xff, 64); + b43_phy_maskset(dev, 0x4a2, ~0x700, 0x600); + b43_phy_maskset(dev, 0x4d9, ~0x70, 0x20); + b43_phy_maskset(dev, 0x4d9, ~0x700, 0x300); + b43_phy_maskset(dev, 0x4d9, ~0x7000, 0x1000); + b43_phy_mask(dev, 0x4da, ~0x1000); + b43_phy_set(dev, 0x4da, 0x2000); + b43_phy_set(dev, 0x4a6, 0x8000); + + b43_radio_write(dev, 0x025, 0xc); + b43_radio_set(dev, 0x005, 0x8); + b43_phy_set(dev, 0x938, 0x4); + b43_phy_set(dev, 0x939, 0x4); + b43_phy_set(dev, 0x4a4, 0x1000); + + /* FIXME: don't hardcode */ + b43_lcntab_write(dev, B43_LCNTAB16(0x8, 0x6), 0x640); + + switch (sense_type) { + case B43_SENSE_TEMP: + b43_phy_set(dev, 0x4d7, 0x8); + b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x1000); + auxpga_vmidcourse = 8; + auxpga_vmidfine = 0x4; + auxpga_gain = 2; + b43_radio_set(dev, 0x082, 0x20); + break; + case B43_SENSE_VBAT: + b43_phy_set(dev, 0x4d7, 0x8); + b43_phy_maskset(dev, 0x4d7, ~0x7000, 0x3000); + auxpga_vmidcourse = 7; + auxpga_vmidfine = 0xa; + auxpga_gain = 2; + break; + } + auxpga_vmid = (0x200 | (auxpga_vmidcourse << 4) | auxpga_vmidfine); + + b43_phy_set(dev, 0x4d8, 0x1); + b43_phy_maskset(dev, 0x4d8, ~(0x3ff << 2), auxpga_vmid << 2); + b43_phy_set(dev, 0x4d8, 0x2); + b43_phy_maskset(dev, 0x4d8, ~(0x7 << 12), auxpga_gain << 12); + b43_phy_set(dev, 0x4d0, 0x20); + b43_radio_write(dev, 0x112, 0x6); - /* TODO: config sth */ + b43_dummy_transmission(dev, true, false); + /* Wait if not done */ + if (!(b43_phy_read(dev, 0x476) & 0x8000)) + udelay(10); + /* Restore */ for (i = 0; i < 6; i++) b43_radio_write(dev, save_radio_regs[i][0], save_radio_regs[i][1]); for (i = 0; i < 14; i++) b43_phy_write(dev, save_phy_regs[i][0], save_phy_regs[i][1]); + /* TODO: wlc_lcnphy_set_tx_pwr_by_index(tx_pwr_idx) */ b43_radio_write(dev, 0x4a4, save_radio_4a4); + + b43_mac_enable(dev); + + msleep(1); +} + +static bool b43_phy_lcn_load_tx_iir_cck_filter(struct b43_wldev *dev, + u8 filter_type) +{ + int i, j; + u16 phy_regs[] = { 0x910, 0x91e, 0x91f, 0x924, 0x925, 0x926, 0x920, + 0x921, 0x927, 0x928, 0x929, 0x922, 0x923, 0x930, + 0x931, 0x932 }; + /* Table is from brcmsmac, values for type 25 were outdated, probably + * others need updating too */ + struct lcn_tx_iir_filter tx_iir_filters_cck[] = { + { 0, { 1, 415, 1874, 64, 128, 64, 792, 1656, 64, 128, 64, 778, + 1582, 64, 128, 64 } }, + { 1, { 1, 402, 1847, 259, 59, 259, 671, 1794, 68, 54, 68, 608, + 1863, 93, 167, 93 } }, + { 2, { 1, 415, 1874, 64, 128, 64, 792, 1656, 192, 384, 192, + 778, 1582, 64, 128, 64 } }, + { 3, { 1, 302, 1841, 129, 258, 129, 658, 1720, 205, 410, 205, + 754, 1760, 170, 340, 170 } }, + { 20, { 1, 360, 1884, 242, 1734, 242, 752, 1720, 205, 1845, 205, + 767, 1760, 256, 185, 256 } }, + { 21, { 1, 360, 1884, 149, 1874, 149, 752, 1720, 205, 1883, 205, + 767, 1760, 256, 273, 256 } }, + { 22, { 1, 360, 1884, 98, 1948, 98, 752, 1720, 205, 1924, 205, + 767, 1760, 256, 352, 256 } }, + { 23, { 1, 350, 1884, 116, 1966, 116, 752, 1720, 205, 2008, 205, + 767, 1760, 128, 233, 128 } }, + { 24, { 1, 325, 1884, 32, 40, 32, 756, 1720, 256, 471, 256, 766, + 1760, 256, 1881, 256 } }, + { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, 256, 471, 256, 765, + 1760, 262, 1878, 262 } }, + /* brcmsmac version { 25, { 1, 299, 1884, 51, 64, 51, 736, 1720, + * 256, 471, 256, 765, 1760, 256, 1881, 256 } }, */ + { 26, { 1, 277, 1943, 39, 117, 88, 637, 1838, 64, 192, 144, 614, + 1864, 128, 384, 288 } }, + { 27, { 1, 245, 1943, 49, 147, 110, 626, 1838, 256, 768, 576, + 613, 1864, 128, 384, 288 } }, + { 30, { 1, 302, 1841, 61, 122, 61, 658, 1720, 205, 410, 205, + 754, 1760, 170, 340, 170 } }, + }; + + for (i = 0; i < ARRAY_SIZE(tx_iir_filters_cck); i++) { + if (tx_iir_filters_cck[i].type == filter_type) { + for (j = 0; j < 16; j++) + b43_phy_write(dev, phy_regs[j], + tx_iir_filters_cck[i].values[j]); + return true; + } + } + + return false; +} + +static bool b43_phy_lcn_load_tx_iir_ofdm_filter(struct b43_wldev *dev, + u8 filter_type) +{ + int i, j; + u16 phy_regs[] = { 0x90f, 0x900, 0x901, 0x906, 0x907, 0x908, 0x902, + 0x903, 0x909, 0x90a, 0x90b, 0x904, 0x905, 0x90c, + 0x90d, 0x90e }; + struct lcn_tx_iir_filter tx_iir_filters_ofdm[] = { + { 0, { 0, 0xa2, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x100, 0x0, + 0x0, 0x278, 0xfea0, 0x80, 0x100, 0x80 } }, + { 1, { 0, 374, 0xFF79, 16, 32, 16, 799, 0xFE74, 50, 32, 50, 750, + 0xFE2B, 212, 0xFFCE, 212 } }, + { 2, { 0, 375, 0xFF16, 37, 76, 37, 799, 0xFE74, 32, 20, 32, 748, + 0xFEF2, 128, 0xFFE2, 128 } }, + }; + + for (i = 0; i < ARRAY_SIZE(tx_iir_filters_ofdm); i++) { + if (tx_iir_filters_ofdm[i].type == filter_type) { + for (j = 0; j < 16; j++) + b43_phy_write(dev, phy_regs[j], + tx_iir_filters_ofdm[i].values[j]); + return true; + } + } + + return false; +} + +/* wlc_lcnphy_set_tx_gain_override */ +static void b43_phy_lcn_set_tx_gain_override(struct b43_wldev *dev, bool enable) +{ + b43_phy_maskset(dev, 0x4b0, ~(0x1 << 7), enable << 7); + b43_phy_maskset(dev, 0x4b0, ~(0x1 << 14), enable << 14); + b43_phy_maskset(dev, 0x43b, ~(0x1 << 6), enable << 6); +} + +/* wlc_lcnphy_set_tx_gain */ +static void b43_phy_lcn_set_tx_gain(struct b43_wldev *dev, + struct lcn_tx_gains *target_gains) +{ + u16 pa_gain = b43_phy_lcn_get_pa_gain(dev); + + b43_phy_write(dev, 0x4b5, + (target_gains->gm_gain | (target_gains->pga_gain << 8))); + b43_phy_maskset(dev, 0x4fb, ~0x7fff, + (target_gains->pad_gain | (pa_gain << 8))); + b43_phy_write(dev, 0x4fc, + (target_gains->gm_gain | (target_gains->pga_gain << 8))); + b43_phy_maskset(dev, 0x4fd, ~0x7fff, + (target_gains->pad_gain | (pa_gain << 8))); + + b43_phy_lcn_set_dac_gain(dev, target_gains->dac_gain); + b43_phy_lcn_set_tx_gain_override(dev, true); +} + +/* wlc_lcnphy_tx_pwr_ctrl_init */ +static void b43_phy_lcn_tx_pwr_ctl_init(struct b43_wldev *dev) +{ + struct lcn_tx_gains tx_gains; + u8 bbmult; + + b43_mac_suspend(dev); + + if (!dev->phy.lcn->hw_pwr_ctl_capable) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + tx_gains.gm_gain = 4; + tx_gains.pga_gain = 12; + tx_gains.pad_gain = 12; + tx_gains.dac_gain = 0; + bbmult = 150; + } else { + tx_gains.gm_gain = 7; + tx_gains.pga_gain = 15; + tx_gains.pad_gain = 14; + tx_gains.dac_gain = 0; + bbmult = 150; + } + b43_phy_lcn_set_tx_gain(dev, &tx_gains); + b43_phy_lcn_set_bbmult(dev, bbmult); + b43_phy_lcn_sense_setup(dev, B43_SENSE_TEMP); + } else { + b43err(dev->wl, "TX power control not supported for this HW\n"); + } + + b43_mac_enable(dev); +} + +/* wlc_lcnphy_txrx_spur_avoidance_mode */ +static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev, + bool enable) +{ + if (enable) { + b43_phy_write(dev, 0x942, 0x7); + b43_phy_write(dev, 0x93b, ((1 << 13) + 23)); + b43_phy_write(dev, 0x93c, ((1 << 13) + 1989)); + + b43_phy_write(dev, 0x44a, 0x084); + b43_phy_write(dev, 0x44a, 0x080); + b43_phy_write(dev, 0x6d3, 0x2222); + b43_phy_write(dev, 0x6d3, 0x2220); + } else { + b43_phy_write(dev, 0x942, 0x0); + b43_phy_write(dev, 0x93b, ((0 << 13) + 23)); + b43_phy_write(dev, 0x93c, ((0 << 13) + 1989)); + } + b43_phy_switch_macfreq(dev, enable); } /************************************************** * Channel switching ops. **************************************************/ +/* wlc_lcnphy_set_chanspec_tweaks */ +static void b43_phy_lcn_set_channel_tweaks(struct b43_wldev *dev, int channel) +{ + struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc; + + b43_phy_maskset(dev, 0x448, ~0x300, (channel == 14) ? 0x200 : 0x100); + + if (channel == 1 || channel == 2 || channel == 3 || channel == 4 || + channel == 9 || channel == 10 || channel == 11 || channel == 12) { + bcma_chipco_pll_write(cc, 0x2, 0x03000c04); + bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x0); + bcma_chipco_pll_write(cc, 0x4, 0x200005c0); + + bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400); + + b43_phy_write(dev, 0x942, 0); + + b43_phy_lcn_txrx_spur_avoidance_mode(dev, false); + b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1b00); + b43_phy_write(dev, 0x425, 0x5907); + } else { + bcma_chipco_pll_write(cc, 0x2, 0x03140c04); + bcma_chipco_pll_maskset(cc, 0x3, 0x00ffffff, 0x333333); + bcma_chipco_pll_write(cc, 0x4, 0x202c2820); + + bcma_cc_set32(cc, BCMA_CC_PMU_CTL, 0x400); + + b43_phy_write(dev, 0x942, 0); + + b43_phy_lcn_txrx_spur_avoidance_mode(dev, true); + b43_phy_maskset(dev, 0x424, (u16) ~0xff00, 0x1f00); + b43_phy_write(dev, 0x425, 0x590a); + } + + b43_phy_set(dev, 0x44a, 0x44); + b43_phy_write(dev, 0x44a, 0x80); +} + +/* wlc_phy_chanspec_set_lcnphy */ static int b43_phy_lcn_set_channel(struct b43_wldev *dev, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type) { - /* TODO: PLL and PHY ops */ + static const u16 sfo_cfg[14][2] = { + {965, 1087}, {967, 1085}, {969, 1082}, {971, 1080}, {973, 1078}, + {975, 1076}, {977, 1073}, {979, 1071}, {981, 1069}, {983, 1067}, + {985, 1065}, {987, 1063}, {989, 1060}, {994, 1055}, + }; - b43_phy_set(dev, 0x44a, 0x44); - b43_phy_write(dev, 0x44a, 0x80); + b43_phy_lcn_set_channel_tweaks(dev, channel->hw_value); b43_phy_set(dev, 0x44a, 0x44); b43_phy_write(dev, 0x44a, 0x80); @@ -295,7 +675,21 @@ static int b43_phy_lcn_set_channel(struct b43_wldev *dev, b43_phy_lcn_afe_set_unset(dev); - /* TODO */ + b43_phy_write(dev, 0x657, sfo_cfg[channel->hw_value - 1][0]); + b43_phy_write(dev, 0x658, sfo_cfg[channel->hw_value - 1][1]); + + if (channel->hw_value == 14) { + b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (2) << 8); + b43_phy_lcn_load_tx_iir_cck_filter(dev, 3); + } else { + b43_phy_maskset(dev, 0x448, ~(0x3 << 8), (1) << 8); + /* brcmsmac uses filter_type 2, we follow wl with 25 */ + b43_phy_lcn_load_tx_iir_cck_filter(dev, 25); + } + /* brcmsmac uses filter_type 2, we follow wl with 0 */ + b43_phy_lcn_load_tx_iir_ofdm_filter(dev, 0); + + b43_phy_maskset(dev, 0x4eb, ~(0x7 << 3), 0x1 << 3); return 0; } @@ -336,6 +730,8 @@ static void b43_phy_lcn_op_prepare_structs(struct b43_wldev *dev) /* wlc_phy_init_lcnphy */ static int b43_phy_lcn_op_init(struct b43_wldev *dev) { + struct bcma_drv_cc *cc = &dev->dev->bdev->bus->drv_cc; + b43_phy_set(dev, 0x44a, 0x80); b43_phy_mask(dev, 0x44a, 0x7f); b43_phy_set(dev, 0x6d1, 0x80); @@ -357,7 +753,21 @@ static int b43_phy_lcn_op_init(struct b43_wldev *dev) else B43_WARN_ON(1); - b43_phy_lcn_sense_setup(dev); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_phy_lcn_tx_pwr_ctl_init(dev); + + b43_switch_channel(dev, dev->phy.channel); + + bcma_chipco_regctl_maskset(cc, 0, 0xf, 0x9); + bcma_chipco_chipctl_maskset(cc, 0, 0, 0x03cddddd); + + /* TODO */ + + b43_phy_set(dev, 0x448, 0x4000); + udelay(100); + b43_phy_mask(dev, 0x448, ~0x4000); + + /* TODO */ return 0; } diff --git a/drivers/net/wireless/b43/phy_lcn.h b/drivers/net/wireless/b43/phy_lcn.h index 25f06e8..6a7092e 100644 --- a/drivers/net/wireless/b43/phy_lcn.h +++ b/drivers/net/wireless/b43/phy_lcn.h @@ -19,6 +19,9 @@ struct b43_phy_lcn { + bool hw_pwr_ctl; + bool hw_pwr_ctl_capable; + u8 tx_pwr_curr_idx; }; diff --git a/drivers/net/wireless/b43/tables_phy_lcn.c b/drivers/net/wireless/b43/tables_phy_lcn.c index 9d484e2..5176363 100644 --- a/drivers/net/wireless/b43/tables_phy_lcn.c +++ b/drivers/net/wireless/b43/tables_phy_lcn.c @@ -657,8 +657,25 @@ void b43_phy_lcn_load_tx_gain_tab(struct b43_wldev *dev, } } +/* wlc_lcnphy_load_rfpower */ +static void b43_phy_lcn_load_rfpower(struct b43_wldev *dev) +{ + u32 bbmult, rfgain; + u8 i; + + for (i = 0; i < 128; i++) { + bbmult = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0x140 + i)); + bbmult >>= 20; + rfgain = b43_lcntab_read(dev, B43_LCNTAB32(0x7, 0xc0 + i)); + + /* TODO: calculate value for 0x240 + i table offset + * b43_lcntab_write(dev, B43_LCNTAB32(0x7, 0x240 + i), val); + */ + } +} + /* Not implemented in brcmsmac, noticed in wl in MMIO dump */ -static void b43_phy_lcn_rewrite_tables(struct b43_wldev *dev) +static void b43_phy_lcn_rewrite_rfpower_table(struct b43_wldev *dev) { int i; u32 tmp; @@ -685,7 +702,7 @@ void b43_phy_lcn_tables_init(struct b43_wldev *dev) b43_phy_lcn_upload_static_tables(dev); if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if (sprom->boardflags_lo & B43_BFL_EXTLNA) + if (sprom->boardflags_lo & B43_BFL_FEM) b43_phy_lcn_load_tx_gain_tab(dev, b43_lcntab_tx_gain_tbl_2ghz_ext_pa_rev0); else @@ -701,7 +718,7 @@ void b43_phy_lcn_tables_init(struct b43_wldev *dev) else b43err(dev->wl, "SW ctl table is unknown for this card\n"); - /* TODO: various tables ops here */ - b43_phy_lcn_rewrite_tables(dev); + b43_phy_lcn_load_rfpower(dev); + b43_phy_lcn_rewrite_rfpower_table(dev); b43_phy_lcn_clean_papd_comp_table(dev); } diff --git a/drivers/net/wireless/iwlegacy/iwl-core.c b/drivers/net/wireless/iwlegacy/iwl-core.c index 35cd2537..8928d47 100644 --- a/drivers/net/wireless/iwlegacy/iwl-core.c +++ b/drivers/net/wireless/iwlegacy/iwl-core.c @@ -937,7 +937,7 @@ void iwl_legacy_irq_handle_error(struct iwl_priv *priv) &priv->contexts[IWL_RXON_CTX_BSS]); #endif - wake_up_interruptible(&priv->wait_command_queue); + wake_up(&priv->wait_command_queue); /* Keep the restart process from trying to send host * commands by clearing the INIT status bit */ @@ -1746,7 +1746,7 @@ int iwl_legacy_force_reset(struct iwl_priv *priv, bool external) /* Set the FW error flag -- cleared on iwl_down */ set_bit(STATUS_FW_ERROR, &priv->status); - wake_up_interruptible(&priv->wait_command_queue); + wake_up(&priv->wait_command_queue); /* * Keep the restart process from trying to send host * commands by clearing the INIT status bit @@ -2220,7 +2220,8 @@ out: } EXPORT_SYMBOL(iwl_legacy_mac_config); -void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw) +void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct iwl_priv *priv = hw->priv; unsigned long flags; diff --git a/drivers/net/wireless/iwlegacy/iwl-core.h b/drivers/net/wireless/iwlegacy/iwl-core.h index 84da793..b2df01c 100644 --- a/drivers/net/wireless/iwlegacy/iwl-core.h +++ b/drivers/net/wireless/iwlegacy/iwl-core.h @@ -620,7 +620,8 @@ static inline const struct ieee80211_supported_band *iwl_get_hw_mode( /* mac80211 handlers */ int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed); -void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw); +void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); void iwl_legacy_mac_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, diff --git a/drivers/net/wireless/iwlegacy/iwl-hcmd.c b/drivers/net/wireless/iwlegacy/iwl-hcmd.c index 62b4b09..ce1fc9f 100644 --- a/drivers/net/wireless/iwlegacy/iwl-hcmd.c +++ b/drivers/net/wireless/iwlegacy/iwl-hcmd.c @@ -167,7 +167,7 @@ int iwl_legacy_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) goto out; } - ret = wait_event_interruptible_timeout(priv->wait_command_queue, + ret = wait_event_timeout(priv->wait_command_queue, !test_bit(STATUS_HCMD_ACTIVE, &priv->status), HOST_COMPLETE_TIMEOUT); if (!ret) { diff --git a/drivers/net/wireless/iwlegacy/iwl-tx.c b/drivers/net/wireless/iwlegacy/iwl-tx.c index 4fff995..ef9e268 100644 --- a/drivers/net/wireless/iwlegacy/iwl-tx.c +++ b/drivers/net/wireless/iwlegacy/iwl-tx.c @@ -625,6 +625,8 @@ iwl_legacy_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) cmd = txq->cmd[cmd_index]; meta = &txq->meta[cmd_index]; + txq->time_stamp = jiffies; + pci_unmap_single(priv->pci_dev, dma_unmap_addr(meta, mapping), dma_unmap_len(meta, len), @@ -645,7 +647,7 @@ iwl_legacy_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) clear_bit(STATUS_HCMD_ACTIVE, &priv->status); IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s\n", iwl_legacy_get_cmd_string(cmd->hdr.cmd)); - wake_up_interruptible(&priv->wait_command_queue); + wake_up(&priv->wait_command_queue); } /* Mark as unmapped */ diff --git a/drivers/net/wireless/iwlegacy/iwl3945-base.c b/drivers/net/wireless/iwlegacy/iwl3945-base.c index 015739d..b282d86 100644 --- a/drivers/net/wireless/iwlegacy/iwl3945-base.c +++ b/drivers/net/wireless/iwlegacy/iwl3945-base.c @@ -840,7 +840,7 @@ static void iwl3945_rx_card_state_notif(struct iwl_priv *priv, wiphy_rfkill_set_hw_state(priv->hw->wiphy, test_bit(STATUS_RF_KILL_HW, &priv->status)); else - wake_up_interruptible(&priv->wait_command_queue); + wake_up(&priv->wait_command_queue); } /** @@ -2268,7 +2268,7 @@ static void iwl3945_alive_start(struct iwl_priv *priv) iwl3945_reg_txpower_periodic(priv); IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n"); - wake_up_interruptible(&priv->wait_command_queue); + wake_up(&priv->wait_command_queue); return; @@ -2299,7 +2299,7 @@ static void __iwl3945_down(struct iwl_priv *priv) iwl_legacy_clear_driver_stations(priv); /* Unblock any waiting calls */ - wake_up_interruptible_all(&priv->wait_command_queue); + wake_up_all(&priv->wait_command_queue); /* Wipe out the EXIT_PENDING status bit if we are not actually * exiting the module */ @@ -2852,7 +2852,7 @@ static int iwl3945_mac_start(struct ieee80211_hw *hw) /* Wait for START_ALIVE from ucode. Otherwise callbacks from * mac80211 will not be run successfully. */ - ret = wait_event_interruptible_timeout(priv->wait_command_queue, + ret = wait_event_timeout(priv->wait_command_queue, test_bit(STATUS_READY, &priv->status), UCODE_READY_TIMEOUT); if (!ret) { diff --git a/drivers/net/wireless/iwlegacy/iwl4965-base.c b/drivers/net/wireless/iwlegacy/iwl4965-base.c index 6bc5575..d2fba9e 100644 --- a/drivers/net/wireless/iwlegacy/iwl4965-base.c +++ b/drivers/net/wireless/iwlegacy/iwl4965-base.c @@ -575,7 +575,7 @@ static void iwl4965_rx_card_state_notif(struct iwl_priv *priv, wiphy_rfkill_set_hw_state(priv->hw->wiphy, test_bit(STATUS_RF_KILL_HW, &priv->status)); else - wake_up_interruptible(&priv->wait_command_queue); + wake_up(&priv->wait_command_queue); } /** @@ -925,7 +925,7 @@ static void iwl4965_irq_tasklet(struct iwl_priv *priv) handled |= CSR_INT_BIT_FH_TX; /* Wake up uCode load routine, now that load is complete */ priv->ucode_write_complete = 1; - wake_up_interruptible(&priv->wait_command_queue); + wake_up(&priv->wait_command_queue); } if (inta & ~handled) { @@ -1794,7 +1794,7 @@ static void iwl4965_alive_start(struct iwl_priv *priv) iwl4965_rf_kill_ct_config(priv); IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n"); - wake_up_interruptible(&priv->wait_command_queue); + wake_up(&priv->wait_command_queue); iwl_legacy_power_update_mode(priv, true); IWL_DEBUG_INFO(priv, "Updated power mode\n"); @@ -1827,7 +1827,7 @@ static void __iwl4965_down(struct iwl_priv *priv) iwl_legacy_clear_driver_stations(priv); /* Unblock any waiting calls */ - wake_up_interruptible_all(&priv->wait_command_queue); + wake_up_all(&priv->wait_command_queue); /* Wipe out the EXIT_PENDING status bit if we are not actually * exiting the module */ @@ -2265,7 +2265,7 @@ int iwl4965_mac_start(struct ieee80211_hw *hw) /* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from * mac80211 will not be run successfully. */ - ret = wait_event_interruptible_timeout(priv->wait_command_queue, + ret = wait_event_timeout(priv->wait_command_queue, test_bit(STATUS_READY, &priv->status), UCODE_READY_TIMEOUT); if (!ret) { diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 48ab914..8fa59cd 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -3,18 +3,19 @@ obj-$(CONFIG_IWLAGN) += iwlagn.o iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwlagn-objs += iwl-agn-ucode.o iwl-agn-tx.o iwlagn-objs += iwl-agn-lib.o iwl-agn-calib.o iwl-io.o -iwlagn-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-eeprom.o +iwlagn-objs += iwl-agn-tt.o iwl-agn-sta.o -iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-power.o -iwlagn-objs += iwl-rx.o iwl-sta.o -iwlagn-objs += iwl-scan.o iwl-led.o -iwlagn-objs += iwl-agn-rxon.o -iwlagn-objs += iwl-5000.o -iwlagn-objs += iwl-6000.o -iwlagn-objs += iwl-1000.o -iwlagn-objs += iwl-2000.o -iwlagn-objs += iwl-pci.o -iwlagn-objs += iwl-trans.o iwl-trans-rx-pcie.o iwl-trans-tx-pcie.o +iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-power.o +iwlagn-objs += iwl-rx.o iwl-sta.o +iwlagn-objs += iwl-scan.o iwl-led.o +iwlagn-objs += iwl-agn-rxon.o +iwlagn-objs += iwl-5000.o +iwlagn-objs += iwl-6000.o +iwlagn-objs += iwl-1000.o +iwlagn-objs += iwl-2000.o +iwlagn-objs += iwl-pci.o +iwlagn-objs += iwl-trans.o +iwlagn-objs += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o iwlagn-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o iwlagn-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index 4766c3a..887f9ac 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -44,7 +44,7 @@ #include "iwl-helpers.h" #include "iwl-agn-hw.h" #include "iwl-shared.h" -#include "iwl-pci.h" +#include "iwl-cfg.h" /* Highest firmware API version supported */ #define IWL1000_UCODE_API_MAX 6 @@ -161,8 +161,6 @@ static int iwl1000_hw_set_hw_params(struct iwl_priv *priv) if (priv->cfg->need_dc_calib) hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_DC); - hw_params(priv).beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS; - return 0; } @@ -191,7 +189,6 @@ static struct iwl_base_params iwl1000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_1000, .shadow_ram_support = false, .led_compensation = 51, - .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index 913f2a2..db88958 100644 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -45,7 +45,7 @@ #include "iwl-agn-hw.h" #include "iwl-6000-hw.h" #include "iwl-shared.h" -#include "iwl-pci.h" +#include "iwl-cfg.h" /* Highest firmware API version supported */ #define IWL2030_UCODE_API_MAX 6 @@ -75,7 +75,7 @@ #define IWL105_MODULE_FIRMWARE(api) IWL105_FW_PRE __stringify(api) ".ucode" #define IWL135_FW_PRE "iwlwifi-135-" -#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE #api ".ucode" +#define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE __stringify(api) ".ucode" static void iwl2000_set_ct_threshold(struct iwl_priv *priv) { @@ -159,8 +159,6 @@ static int iwl2000_hw_set_hw_params(struct iwl_priv *priv) if (priv->cfg->need_temp_offset_calib) hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_TEMP_OFFSET); - hw_params(priv).beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS; - return 0; } @@ -211,7 +209,6 @@ static struct iwl_base_params iwl2000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_2x00, .shadow_ram_support = true, .led_compensation = 51, - .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .adv_thermal_throttle = true, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, @@ -231,7 +228,6 @@ static struct iwl_base_params iwl2030_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_2x00, .shadow_ram_support = true, .led_compensation = 57, - .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .adv_thermal_throttle = true, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, @@ -268,6 +264,7 @@ static struct iwl_bt_params iwl2030_bt_params = { .base_params = &iwl2000_base_params, \ .need_dc_calib = true, \ .need_temp_offset_calib = true, \ + .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ .iq_invert = true \ @@ -300,6 +297,7 @@ struct iwl_cfg iwl2000_2bgn_d_cfg = { .bt_params = &iwl2030_bt_params, \ .need_dc_calib = true, \ .need_temp_offset_calib = true, \ + .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true, \ .iq_invert = true \ @@ -326,6 +324,7 @@ struct iwl_cfg iwl2030_2bg_cfg = { .base_params = &iwl2000_base_params, \ .need_dc_calib = true, \ .need_temp_offset_calib = true, \ + .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true, \ .rx_with_siso_diversity = true, \ @@ -354,6 +353,7 @@ struct iwl_cfg iwl105_bgn_cfg = { .bt_params = &iwl2030_bt_params, \ .need_dc_calib = true, \ .need_temp_offset_calib = true, \ + .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true, \ .rx_with_siso_diversity = true, \ diff --git a/drivers/net/wireless/iwlwifi/iwl-5000-hw.h b/drivers/net/wireless/iwlwifi/iwl-5000-hw.h index f9630a3..c013598 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000-hw.h +++ b/drivers/net/wireless/iwlwifi/iwl-5000-hw.h @@ -74,8 +74,8 @@ static inline s32 iwl_temp_calib_to_offset(struct iwl_priv *priv) { u16 temperature, voltage; - __le16 *temp_calib = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_TEMPERATURE); + __le16 *temp_calib = (__le16 *)iwl_eeprom_query_addr(priv, + EEPROM_KELVIN_TEMPERATURE); temperature = le16_to_cpu(temp_calib[0]); voltage = le16_to_cpu(temp_calib[1]); diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 7cb4d69..2907016 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -47,7 +47,7 @@ #include "iwl-5000-hw.h" #include "iwl-trans.h" #include "iwl-shared.h" -#include "iwl-pci.h" +#include "iwl-cfg.h" /* Highest firmware API version supported */ #define IWL5000_UCODE_API_MAX 5 @@ -184,8 +184,6 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) BIT(IWL_CALIB_TX_IQ_PERD) | BIT(IWL_CALIB_BASE_BAND); - hw_params(priv).beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS; - return 0; } @@ -223,8 +221,6 @@ static int iwl5150_hw_set_hw_params(struct iwl_priv *priv) if (priv->cfg->need_dc_calib) hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_DC); - hw_params(priv).beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS; - return 0; } @@ -353,7 +349,6 @@ static struct iwl_base_params iwl5000_base_params = { .num_of_ampdu_queues = IWLAGN_NUM_AMPDU_QUEUES, .pll_cfg_val = CSR50_ANA_PLL_CFG_VAL, .led_compensation = 51, - .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .chain_noise_scale = 1000, .wd_timeout = IWL_LONG_WD_TIMEOUT, diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index 2a98e65..37837f7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -46,7 +46,7 @@ #include "iwl-6000-hw.h" #include "iwl-trans.h" #include "iwl-shared.h" -#include "iwl-pci.h" +#include "iwl-cfg.h" /* Highest firmware API version supported */ #define IWL6000_UCODE_API_MAX 4 @@ -180,8 +180,6 @@ static int iwl6000_hw_set_hw_params(struct iwl_priv *priv) if (priv->cfg->need_temp_offset_calib) hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_TEMP_OFFSET); - hw_params(priv).beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS; - return 0; } @@ -305,7 +303,6 @@ static struct iwl_base_params iwl6000_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .led_compensation = 51, - .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .adv_thermal_throttle = true, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, @@ -323,7 +320,6 @@ static struct iwl_base_params iwl6050_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x50, .shadow_ram_support = true, .led_compensation = 51, - .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .adv_thermal_throttle = true, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, @@ -340,7 +336,6 @@ static struct iwl_base_params iwl6000_g2_base_params = { .max_ll_items = OTP_MAX_LL_ITEMS_6x00, .shadow_ram_support = true, .led_compensation = 57, - .chain_noise_num_beacons = IWL_CAL_NUM_BEACONS, .adv_thermal_throttle = true, .support_ct_kill_exit = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-calib.c b/drivers/net/wireless/iwlwifi/iwl-agn-calib.c index b725f69..03bac48 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-calib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-calib.c @@ -766,12 +766,9 @@ static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, u8 first_chain; u16 i = 0; - average_sig[0] = data->chain_signal_a / - priv->cfg->base_params->chain_noise_num_beacons; - average_sig[1] = data->chain_signal_b / - priv->cfg->base_params->chain_noise_num_beacons; - average_sig[2] = data->chain_signal_c / - priv->cfg->base_params->chain_noise_num_beacons; + average_sig[0] = data->chain_signal_a / IWL_CAL_NUM_BEACONS; + average_sig[1] = data->chain_signal_b / IWL_CAL_NUM_BEACONS; + average_sig[2] = data->chain_signal_c / IWL_CAL_NUM_BEACONS; if (average_sig[0] >= average_sig[1]) { max_average_sig = average_sig[0]; @@ -1038,8 +1035,7 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv) /* If this is the "chain_noise_num_beacons", determine: * 1) Disconnected antennas (using signal strengths) * 2) Differential gain (using silence noise) to balance receivers */ - if (data->beacon_count != - priv->cfg->base_params->chain_noise_num_beacons) + if (data->beacon_count != IWL_CAL_NUM_BEACONS) return; /* Analyze signal for disconnected antenna */ @@ -1055,12 +1051,9 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv) iwl_find_disconn_antenna(priv, average_sig, data); /* Analyze noise for rx balance */ - average_noise[0] = data->chain_noise_a / - priv->cfg->base_params->chain_noise_num_beacons; - average_noise[1] = data->chain_noise_b / - priv->cfg->base_params->chain_noise_num_beacons; - average_noise[2] = data->chain_noise_c / - priv->cfg->base_params->chain_noise_num_beacons; + average_noise[0] = data->chain_noise_a / IWL_CAL_NUM_BEACONS; + average_noise[1] = data->chain_noise_b / IWL_CAL_NUM_BEACONS; + average_noise[2] = data->chain_noise_c / IWL_CAL_NUM_BEACONS; for (i = 0; i < NUM_RX_CHAINS; i++) { if (!(data->disconn_array[i]) && diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c deleted file mode 100644 index c62ddc2..0000000 --- a/drivers/net/wireless/iwlwifi/iwl-agn-eeprom.c +++ /dev/null @@ -1,299 +0,0 @@ -/****************************************************************************** - * - * This file is provided under a dual BSD/GPLv2 license. When using or - * redistributing this file, you may do so under either license. - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License 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 Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless <ilw@linux.intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - * - * BSD LICENSE - * - * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * * Neither the name Intel Corporation nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/init.h> - -#include <net/mac80211.h> - -#include "iwl-commands.h" -#include "iwl-dev.h" -#include "iwl-core.h" -#include "iwl-debug.h" -#include "iwl-agn.h" -#include "iwl-io.h" - -/****************************************************************************** - * - * EEPROM related functions - * -******************************************************************************/ - -int iwl_eeprom_check_version(struct iwl_priv *priv) -{ - u16 eeprom_ver; - u16 calib_ver; - - eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); - calib_ver = iwlagn_eeprom_calib_version(priv); - - if (eeprom_ver < priv->cfg->eeprom_ver || - calib_ver < priv->cfg->eeprom_calib_ver) - goto err; - - IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n", - eeprom_ver, calib_ver); - - return 0; -err: - IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x " - "CALIB=0x%x < 0x%x\n", - eeprom_ver, priv->cfg->eeprom_ver, - calib_ver, priv->cfg->eeprom_calib_ver); - return -EINVAL; - -} - -int iwl_eeprom_check_sku(struct iwl_priv *priv) -{ - u16 radio_cfg; - - if (!priv->cfg->sku) { - /* not using sku overwrite */ - priv->cfg->sku = iwl_eeprom_query16(priv, EEPROM_SKU_CAP); - if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE && - !priv->cfg->ht_params) { - IWL_ERR(priv, "Invalid 11n configuration\n"); - return -EINVAL; - } - } - if (!priv->cfg->sku) { - IWL_ERR(priv, "Invalid device sku\n"); - return -EINVAL; - } - - IWL_INFO(priv, "Device SKU: 0X%x\n", priv->cfg->sku); - - if (!priv->cfg->valid_tx_ant && !priv->cfg->valid_rx_ant) { - /* not using .cfg overwrite */ - radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); - priv->cfg->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg); - priv->cfg->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg); - if (!priv->cfg->valid_tx_ant || !priv->cfg->valid_rx_ant) { - IWL_ERR(priv, "Invalid chain (0X%x, 0X%x)\n", - priv->cfg->valid_tx_ant, - priv->cfg->valid_rx_ant); - return -EINVAL; - } - IWL_INFO(priv, "Valid Tx ant: 0X%x, Valid Rx ant: 0X%x\n", - priv->cfg->valid_tx_ant, priv->cfg->valid_rx_ant); - } - /* - * for some special cases, - * EEPROM did not reflect the correct antenna setting - * so overwrite the valid tx/rx antenna from .cfg - */ - return 0; -} - -void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac) -{ - const u8 *addr = iwl_eeprom_query_addr(priv, - EEPROM_MAC_ADDRESS); - memcpy(mac, addr, ETH_ALEN); -} - -/** - * iwl_get_max_txpower_avg - get the highest tx power from all chains. - * find the highest tx power from all chains for the channel - */ -static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv, - struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, - int element, s8 *max_txpower_in_half_dbm) -{ - s8 max_txpower_avg = 0; /* (dBm) */ - - /* Take the highest tx power from any valid chains */ - if ((priv->cfg->valid_tx_ant & ANT_A) && - (enhanced_txpower[element].chain_a_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].chain_a_max; - if ((priv->cfg->valid_tx_ant & ANT_B) && - (enhanced_txpower[element].chain_b_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].chain_b_max; - if ((priv->cfg->valid_tx_ant & ANT_C) && - (enhanced_txpower[element].chain_c_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].chain_c_max; - if (((priv->cfg->valid_tx_ant == ANT_AB) | - (priv->cfg->valid_tx_ant == ANT_BC) | - (priv->cfg->valid_tx_ant == ANT_AC)) && - (enhanced_txpower[element].mimo2_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].mimo2_max; - if ((priv->cfg->valid_tx_ant == ANT_ABC) && - (enhanced_txpower[element].mimo3_max > max_txpower_avg)) - max_txpower_avg = enhanced_txpower[element].mimo3_max; - - /* - * max. tx power in EEPROM is in 1/2 dBm format - * convert from 1/2 dBm to dBm (round-up convert) - * but we also do not want to loss 1/2 dBm resolution which - * will impact performance - */ - *max_txpower_in_half_dbm = max_txpower_avg; - return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1); -} - -static void -iwl_eeprom_enh_txp_read_element(struct iwl_priv *priv, - struct iwl_eeprom_enhanced_txpwr *txp, - s8 max_txpower_avg) -{ - int ch_idx; - bool is_ht40 = txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ; - enum ieee80211_band band; - - band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ? - IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; - - for (ch_idx = 0; ch_idx < priv->channel_count; ch_idx++) { - struct iwl_channel_info *ch_info = &priv->channel_info[ch_idx]; - - /* update matching channel or from common data only */ - if (txp->channel != 0 && ch_info->channel != txp->channel) - continue; - - /* update matching band only */ - if (band != ch_info->band) - continue; - - if (ch_info->max_power_avg < max_txpower_avg && !is_ht40) { - ch_info->max_power_avg = max_txpower_avg; - ch_info->curr_txpow = max_txpower_avg; - ch_info->scan_power = max_txpower_avg; - } - - if (is_ht40 && ch_info->ht40_max_power_avg < max_txpower_avg) - ch_info->ht40_max_power_avg = max_txpower_avg; - } -} - -#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT) -#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr) -#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE) - -#define TXP_CHECK_AND_PRINT(x) ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) \ - ? # x " " : "") - -void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv) -{ - struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; - int idx, entries; - __le16 *txp_len; - s8 max_txp_avg, max_txp_avg_halfdbm; - - BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); - - /* the length is in 16-bit words, but we want entries */ - txp_len = (__le16 *) iwl_eeprom_query_addr(priv, EEPROM_TXP_SZ_OFFS); - entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; - - txp_array = (void *) iwl_eeprom_query_addr(priv, EEPROM_TXP_OFFS); - - for (idx = 0; idx < entries; idx++) { - txp = &txp_array[idx]; - /* skip invalid entries */ - if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID)) - continue; - - IWL_DEBUG_EEPROM(priv, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n", - (txp->channel && (txp->flags & - IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ? - "Common " : (txp->channel) ? - "Channel" : "Common", - (txp->channel), - TXP_CHECK_AND_PRINT(VALID), - TXP_CHECK_AND_PRINT(BAND_52G), - TXP_CHECK_AND_PRINT(OFDM), - TXP_CHECK_AND_PRINT(40MHZ), - TXP_CHECK_AND_PRINT(HT_AP), - TXP_CHECK_AND_PRINT(RES1), - TXP_CHECK_AND_PRINT(RES2), - TXP_CHECK_AND_PRINT(COMMON_TYPE), - txp->flags); - IWL_DEBUG_EEPROM(priv, "\t\t chain_A: 0x%02x " - "chain_B: 0X%02x chain_C: 0X%02x\n", - txp->chain_a_max, txp->chain_b_max, - txp->chain_c_max); - IWL_DEBUG_EEPROM(priv, "\t\t MIMO2: 0x%02x " - "MIMO3: 0x%02x High 20_on_40: 0x%02x " - "Low 20_on_40: 0x%02x\n", - txp->mimo2_max, txp->mimo3_max, - ((txp->delta_20_in_40 & 0xf0) >> 4), - (txp->delta_20_in_40 & 0x0f)); - - max_txp_avg = iwl_get_max_txpower_avg(priv, txp_array, idx, - &max_txp_avg_halfdbm); - - /* - * Update the user limit values values to the highest - * power supported by any channel - */ - if (max_txp_avg > priv->tx_power_user_lmt) - priv->tx_power_user_lmt = max_txp_avg; - if (max_txp_avg_halfdbm > priv->tx_power_lmt_in_half_dbm) - priv->tx_power_lmt_in_half_dbm = max_txp_avg_halfdbm; - - iwl_eeprom_enh_txp_read_element(priv, txp, max_txp_avg); - } -} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 1301887..d30714b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -96,11 +96,7 @@ void iwlagn_temperature(struct iwl_priv *priv) u16 iwlagn_eeprom_calib_version(struct iwl_priv *priv) { - struct iwl_eeprom_calib_hdr { - u8 version; - u8 pa_type; - u16 voltage; - } *hdr; + struct iwl_eeprom_calib_hdr *hdr; hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv, EEPROM_CALIB_ALL); @@ -194,433 +190,6 @@ int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band) return -1; } -static int iwl_get_single_channel_for_scan(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum ieee80211_band band, - struct iwl_scan_channel *scan_ch) -{ - const struct ieee80211_supported_band *sband; - u16 passive_dwell = 0; - u16 active_dwell = 0; - int added = 0; - u16 channel = 0; - - sband = iwl_get_hw_mode(priv, band); - if (!sband) { - IWL_ERR(priv, "invalid band\n"); - return added; - } - - active_dwell = iwl_get_active_dwell_time(priv, band, 0); - passive_dwell = iwl_get_passive_dwell_time(priv, band, vif); - - if (passive_dwell <= active_dwell) - passive_dwell = active_dwell + 1; - - channel = iwl_get_single_channel_number(priv, band); - if (channel) { - scan_ch->channel = cpu_to_le16(channel); - scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - scan_ch->active_dwell = cpu_to_le16(active_dwell); - scan_ch->passive_dwell = cpu_to_le16(passive_dwell); - /* Set txpower levels to defaults */ - scan_ch->dsp_atten = 110; - if (band == IEEE80211_BAND_5GHZ) - scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; - else - scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - added++; - } else - IWL_ERR(priv, "no valid channel found\n"); - return added; -} - -static int iwl_get_channels_for_scan(struct iwl_priv *priv, - struct ieee80211_vif *vif, - enum ieee80211_band band, - u8 is_active, u8 n_probes, - struct iwl_scan_channel *scan_ch) -{ - struct ieee80211_channel *chan; - const struct ieee80211_supported_band *sband; - const struct iwl_channel_info *ch_info; - u16 passive_dwell = 0; - u16 active_dwell = 0; - int added, i; - u16 channel; - - sband = iwl_get_hw_mode(priv, band); - if (!sband) - return 0; - - active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); - passive_dwell = iwl_get_passive_dwell_time(priv, band, vif); - - if (passive_dwell <= active_dwell) - passive_dwell = active_dwell + 1; - - for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { - chan = priv->scan_request->channels[i]; - - if (chan->band != band) - continue; - - channel = chan->hw_value; - scan_ch->channel = cpu_to_le16(channel); - - ch_info = iwl_get_channel_info(priv, band, channel); - if (!is_channel_valid(ch_info)) { - IWL_DEBUG_SCAN(priv, "Channel %d is INVALID for this band.\n", - channel); - continue; - } - - if (!is_active || is_channel_passive(ch_info) || - (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) - scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - else - scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; - - if (n_probes) - scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes); - - scan_ch->active_dwell = cpu_to_le16(active_dwell); - scan_ch->passive_dwell = cpu_to_le16(passive_dwell); - - /* Set txpower levels to defaults */ - scan_ch->dsp_atten = 110; - - /* NOTE: if we were doing 6Mb OFDM for scans we'd use - * power level: - * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; - */ - if (band == IEEE80211_BAND_5GHZ) - scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; - else - scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - - IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n", - channel, le32_to_cpu(scan_ch->type), - (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? - "ACTIVE" : "PASSIVE", - (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? - active_dwell : passive_dwell); - - scan_ch++; - added++; - } - - IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added); - return added; -} - -int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) -{ - struct iwl_host_cmd cmd = { - .id = REPLY_SCAN_CMD, - .len = { sizeof(struct iwl_scan_cmd), }, - .flags = CMD_SYNC, - }; - struct iwl_scan_cmd *scan; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - u32 rate_flags = 0; - u16 cmd_len; - u16 rx_chain = 0; - enum ieee80211_band band; - u8 n_probes = 0; - u8 rx_ant = hw_params(priv).valid_rx_ant; - u8 rate; - bool is_active = false; - int chan_mod; - u8 active_chains; - u8 scan_tx_antennas = hw_params(priv).valid_tx_ant; - int ret; - - lockdep_assert_held(&priv->shrd->mutex); - - if (vif) - ctx = iwl_rxon_ctx_from_vif(vif); - - if (!priv->scan_cmd) { - priv->scan_cmd = kmalloc(sizeof(struct iwl_scan_cmd) + - IWL_MAX_SCAN_SIZE, GFP_KERNEL); - if (!priv->scan_cmd) { - IWL_DEBUG_SCAN(priv, - "fail to allocate memory for scan\n"); - return -ENOMEM; - } - } - scan = priv->scan_cmd; - memset(scan, 0, sizeof(struct iwl_scan_cmd) + IWL_MAX_SCAN_SIZE); - - scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; - scan->quiet_time = IWL_ACTIVE_QUIET_TIME; - - if (priv->scan_type != IWL_SCAN_ROC && - iwl_is_any_associated(priv)) { - u16 interval = 0; - u32 extra; - u32 suspend_time = 100; - u32 scan_suspend_time = 100; - - IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); - switch (priv->scan_type) { - case IWL_SCAN_ROC: - WARN_ON(1); - break; - case IWL_SCAN_RADIO_RESET: - interval = 0; - break; - case IWL_SCAN_NORMAL: - interval = vif->bss_conf.beacon_int; - break; - } - - scan->suspend_time = 0; - scan->max_out_time = cpu_to_le32(200 * 1024); - if (!interval) - interval = suspend_time; - - extra = (suspend_time / interval) << 22; - scan_suspend_time = (extra | - ((suspend_time % interval) * 1024)); - scan->suspend_time = cpu_to_le32(scan_suspend_time); - IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", - scan_suspend_time, interval); - } else if (priv->scan_type == IWL_SCAN_ROC) { - scan->suspend_time = 0; - scan->max_out_time = 0; - scan->quiet_time = 0; - scan->quiet_plcp_th = 0; - } - - switch (priv->scan_type) { - case IWL_SCAN_RADIO_RESET: - IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); - break; - case IWL_SCAN_NORMAL: - if (priv->scan_request->n_ssids) { - int i, p = 0; - IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); - for (i = 0; i < priv->scan_request->n_ssids; i++) { - /* always does wildcard anyway */ - if (!priv->scan_request->ssids[i].ssid_len) - continue; - scan->direct_scan[p].id = WLAN_EID_SSID; - scan->direct_scan[p].len = - priv->scan_request->ssids[i].ssid_len; - memcpy(scan->direct_scan[p].ssid, - priv->scan_request->ssids[i].ssid, - priv->scan_request->ssids[i].ssid_len); - n_probes++; - p++; - } - is_active = true; - } else - IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); - break; - case IWL_SCAN_ROC: - IWL_DEBUG_SCAN(priv, "Start ROC scan.\n"); - break; - } - - scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; - scan->tx_cmd.sta_id = ctx->bcast_sta_id; - scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; - - switch (priv->scan_band) { - case IEEE80211_BAND_2GHZ: - scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; - chan_mod = le32_to_cpu( - priv->contexts[IWL_RXON_CTX_BSS].active.flags & - RXON_FLG_CHANNEL_MODE_MSK) - >> RXON_FLG_CHANNEL_MODE_POS; - if (chan_mod == CHANNEL_MODE_PURE_40) { - rate = IWL_RATE_6M_PLCP; - } else { - rate = IWL_RATE_1M_PLCP; - rate_flags = RATE_MCS_CCK_MSK; - } - /* - * Internal scans are passive, so we can indiscriminately set - * the BT ignore flag on 2.4 GHz since it applies to TX only. - */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) - scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; - break; - case IEEE80211_BAND_5GHZ: - rate = IWL_RATE_6M_PLCP; - break; - default: - IWL_WARN(priv, "Invalid scan band\n"); - return -EIO; - } - - /* - * If active scanning is requested but a certain channel is - * marked passive, we can do active scanning if we detect - * transmissions. - * - * There is an issue with some firmware versions that triggers - * a sysassert on a "good CRC threshold" of zero (== disabled), - * on a radar channel even though this means that we should NOT - * send probes. - * - * The "good CRC threshold" is the number of frames that we - * need to receive during our dwell time on a channel before - * sending out probes -- setting this to a huge value will - * mean we never reach it, but at the same time work around - * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER - * here instead of IWL_GOOD_CRC_TH_DISABLED. - * - * This was fixed in later versions along with some other - * scan changes, and the threshold behaves as a flag in those - * versions. - */ - if (priv->new_scan_threshold_behaviour) - scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : - IWL_GOOD_CRC_TH_DISABLED; - else - scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : - IWL_GOOD_CRC_TH_NEVER; - - band = priv->scan_band; - - if (priv->cfg->scan_rx_antennas[band]) - rx_ant = priv->cfg->scan_rx_antennas[band]; - - if (band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { - /* transmit 2.4 GHz probes only on first antenna */ - scan_tx_antennas = first_antenna(scan_tx_antennas); - } - - priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, priv->scan_tx_ant[band], - scan_tx_antennas); - rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]); - scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); - - /* In power save mode use one chain, otherwise use all chains */ - if (test_bit(STATUS_POWER_PMI, &priv->shrd->status)) { - /* rx_ant has been set to all valid chains previously */ - active_chains = rx_ant & - ((u8)(priv->chain_noise_data.active_chains)); - if (!active_chains) - active_chains = rx_ant; - - IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n", - priv->chain_noise_data.active_chains); - - rx_ant = first_antenna(active_chains); - } - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && - priv->bt_full_concurrent) { - /* operated as 1x1 in full concurrency mode */ - rx_ant = first_antenna(rx_ant); - } - - /* MIMO is not used here, but value is required */ - rx_chain |= - hw_params(priv).valid_rx_ant << RXON_RX_CHAIN_VALID_POS; - rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; - rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; - rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; - scan->rx_chain = cpu_to_le16(rx_chain); - switch (priv->scan_type) { - case IWL_SCAN_NORMAL: - cmd_len = iwl_fill_probe_req(priv, - (struct ieee80211_mgmt *)scan->data, - vif->addr, - priv->scan_request->ie, - priv->scan_request->ie_len, - IWL_MAX_SCAN_SIZE - sizeof(*scan)); - break; - case IWL_SCAN_RADIO_RESET: - case IWL_SCAN_ROC: - /* use bcast addr, will not be transmitted but must be valid */ - cmd_len = iwl_fill_probe_req(priv, - (struct ieee80211_mgmt *)scan->data, - iwl_bcast_addr, NULL, 0, - IWL_MAX_SCAN_SIZE - sizeof(*scan)); - break; - default: - BUG(); - } - scan->tx_cmd.len = cpu_to_le16(cmd_len); - - scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | - RXON_FILTER_BCON_AWARE_MSK); - - switch (priv->scan_type) { - case IWL_SCAN_RADIO_RESET: - scan->channel_count = - iwl_get_single_channel_for_scan(priv, vif, band, - (void *)&scan->data[cmd_len]); - break; - case IWL_SCAN_NORMAL: - scan->channel_count = - iwl_get_channels_for_scan(priv, vif, band, - is_active, n_probes, - (void *)&scan->data[cmd_len]); - break; - case IWL_SCAN_ROC: { - struct iwl_scan_channel *scan_ch; - - scan->channel_count = 1; - - scan_ch = (void *)&scan->data[cmd_len]; - scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; - scan_ch->channel = - cpu_to_le16(priv->hw_roc_channel->hw_value); - scan_ch->active_dwell = - scan_ch->passive_dwell = - cpu_to_le16(priv->hw_roc_duration); - - /* Set txpower levels to defaults */ - scan_ch->dsp_atten = 110; - - /* NOTE: if we were doing 6Mb OFDM for scans we'd use - * power level: - * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; - */ - if (priv->hw_roc_channel->band == IEEE80211_BAND_5GHZ) - scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; - else - scan_ch->tx_gain = ((1 << 5) | (5 << 3)); - } - break; - } - - if (scan->channel_count == 0) { - IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); - return -EIO; - } - - cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) + - scan->channel_count * sizeof(struct iwl_scan_channel); - cmd.data[0] = scan; - cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - scan->len = cpu_to_le16(cmd.len[0]); - - /* set scan bit here for PAN params */ - set_bit(STATUS_SCAN_HW, &priv->shrd->status); - - ret = iwlagn_set_pan_params(priv); - if (ret) - return ret; - - ret = iwl_trans_send_cmd(trans(priv), &cmd); - if (ret) { - clear_bit(STATUS_SCAN_HW, &priv->shrd->status); - iwlagn_set_pan_params(priv); - } - - return ret; -} - int iwlagn_manage_ibss_station(struct iwl_priv *priv, struct ieee80211_vif *vif, bool add) { @@ -1136,8 +705,9 @@ static void iwlagn_set_kill_msk(struct iwl_priv *priv, } } -void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { unsigned long flags; struct iwl_rx_packet *pkt = rxb_addr(rxb); @@ -1146,7 +716,7 @@ void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { /* bt coex disabled */ - return; + return 0; } IWL_DEBUG_COEX(priv, "BT Coex notification:\n"); @@ -1188,6 +758,7 @@ void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, spin_lock_irqsave(&priv->shrd->lock, flags); priv->bt_ci_compliance = coex->bt_ci_compliance; spin_unlock_irqrestore(&priv->shrd->lock, flags); + return 0; } void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index ffee15b..c14f8d6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -346,7 +346,7 @@ static void rs_program_fix_rate(struct iwl_priv *priv, { struct iwl_station_priv *sta_priv = container_of(lq_sta, struct iwl_station_priv, lq_sta); - struct iwl_rxon_context *ctx = sta_priv->common.ctx; + struct iwl_rxon_context *ctx = sta_priv->ctx; lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ @@ -710,7 +710,7 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, static bool rs_use_green(struct ieee80211_sta *sta) { struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->common.ctx; + struct iwl_rxon_context *ctx = sta_priv->ctx; return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && !(ctx->ht.non_gf_sta_present); @@ -917,7 +917,7 @@ static void rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, struct iwl_scale_tbl_info tbl_type; struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->common.ctx; + struct iwl_rxon_context *ctx = sta_priv->ctx; IWL_DEBUG_RATE_LIMIT(priv, "get frame ack response, update rate scale window\n"); @@ -1283,7 +1283,7 @@ static int rs_switch_to_mimo2(struct iwl_priv *priv, s32 rate; s8 is_green = lq_sta->is_green; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->common.ctx; + struct iwl_rxon_context *ctx = sta_priv->ctx; if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; @@ -1339,7 +1339,7 @@ static int rs_switch_to_mimo3(struct iwl_priv *priv, s32 rate; s8 is_green = lq_sta->is_green; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->common.ctx; + struct iwl_rxon_context *ctx = sta_priv->ctx; if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; @@ -1396,7 +1396,7 @@ static int rs_switch_to_siso(struct iwl_priv *priv, u8 is_green = lq_sta->is_green; s32 rate; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->common.ctx; + struct iwl_rxon_context *ctx = sta_priv->ctx; if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) return -1; @@ -2263,7 +2263,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, u8 tid = IWL_MAX_TID_COUNT; struct iwl_tid_data *tid_data; struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_rxon_context *ctx = sta_priv->common.ctx; + struct iwl_rxon_context *ctx = sta_priv->ctx; IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n"); @@ -2706,7 +2706,7 @@ static void rs_initialize_lq(struct iwl_priv *priv, return; sta_priv = (void *)sta->drv_priv; - ctx = sta_priv->common.ctx; + ctx = sta_priv->ctx; i = lq_sta->last_txrate_idx; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index 00e6fc59..ca632f9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -370,7 +370,7 @@ int iwlagn_set_pan_params(struct iwl_priv *priv) slot1 = IWL_MIN_SLOT_TIME; } else if (!ctx_pan->vif->bss_conf.idle && !ctx_pan->vif->bss_conf.assoc) { - slot1 = bcnint * 3 - IWL_MIN_SLOT_TIME; + slot1 = dtim * bcnint * 3 - IWL_MIN_SLOT_TIME; slot0 = IWL_MIN_SLOT_TIME; } } else if (ctx_pan->vif) { diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tt.c b/drivers/net/wireless/iwlwifi/iwl-agn-tt.c index 92ba8cd..c27180a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tt.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tt.c @@ -114,9 +114,6 @@ static bool iwl_within_ct_kill_margin(struct iwl_priv *priv) s32 temp = priv->temperature; /* degrees CELSIUS except specified */ bool within_margin = false; - if (priv->cfg->base_params->temperature_kelvin) - temp = KELVIN_TO_CELSIUS(priv->temperature); - if (!priv->thermal_throttle.advanced_tt) within_margin = ((temp + IWL_TT_CT_KILL_MARGIN) >= CT_KILL_THRESHOLD_LEGACY) ? true : false; @@ -209,7 +206,7 @@ static void iwl_perform_ct_kill_task(struct iwl_priv *priv, { if (stop) { IWL_DEBUG_TEMP(priv, "Stop all queues\n"); - if (priv->shrd->mac80211_registered) + if (priv->mac80211_registered) ieee80211_stop_queues(priv->hw); IWL_DEBUG_TEMP(priv, "Schedule 5 seconds CT_KILL Timer\n"); @@ -217,7 +214,7 @@ static void iwl_perform_ct_kill_task(struct iwl_priv *priv, jiffies + CT_KILL_EXIT_DURATION * HZ); } else { IWL_DEBUG_TEMP(priv, "Wake all queues\n"); - if (priv->shrd->mac80211_registered) + if (priv->mac80211_registered) ieee80211_wake_queues(priv->hw); } } @@ -591,9 +588,6 @@ static void iwl_bg_tt_work(struct work_struct *work) if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) return; - if (priv->cfg->base_params->temperature_kelvin) - temp = KELVIN_TO_CELSIUS(priv->temperature); - if (!priv->thermal_throttle.advanced_tt) iwl_legacy_tt_handler(priv, temp, false); else @@ -641,11 +635,13 @@ void iwl_tt_initialize(struct iwl_priv *priv) if (priv->cfg->base_params->adv_thermal_throttle) { IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); - tt->restriction = kzalloc(sizeof(struct iwl_tt_restriction) * - IWL_TI_STATE_MAX, GFP_KERNEL); - tt->transaction = kzalloc(sizeof(struct iwl_tt_trans) * - IWL_TI_STATE_MAX * (IWL_TI_STATE_MAX - 1), - GFP_KERNEL); + tt->restriction = kcalloc(IWL_TI_STATE_MAX, + sizeof(struct iwl_tt_restriction), + GFP_KERNEL); + tt->transaction = kcalloc(IWL_TI_STATE_MAX * + (IWL_TI_STATE_MAX - 1), + sizeof(struct iwl_tt_trans), + GFP_KERNEL); if (!tt->restriction || !tt->transaction) { IWL_ERR(priv, "Fallback to Legacy Throttling\n"); priv->thermal_throttle.advanced_tt = false; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index ba5c514..0e5d649 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -313,6 +313,9 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) iwl_sta_modify_sleep_tx_count(priv, sta_id, 1); } + if (info->flags & IEEE80211_TX_CTL_AMPDU) + is_agg = true; + /* irqs already disabled/saved above when locking priv->shrd->lock */ spin_lock(&priv->shrd->sta_lock); @@ -322,10 +325,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) goto drop_unlock_sta; memset(dev_cmd, 0, sizeof(*dev_cmd)); - tx_cmd = &dev_cmd->cmd.tx; - - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdr_len); + tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; /* Total # bytes to be transmitted */ len = (u16)skb->len; @@ -342,6 +342,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) iwl_update_stats(priv, true, fc, len); + memset(&info->status, 0, sizeof(info->status)); + info->driver_data[0] = ctx; info->driver_data[1] = dev_cmd; @@ -580,6 +582,9 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); } + if (tx_resp->frame_count == 1) + return; + /* Construct bit-map of pending frames within Tx window */ for (i = 0; i < tx_resp->frame_count; i++) { u16 fstatus = le16_to_cpu(frame_status[i].status); @@ -736,7 +741,8 @@ static void iwl_check_abort_status(struct iwl_priv *priv, } } -void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) +int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); @@ -824,6 +830,7 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) iwl_check_abort_status(priv, tx_resp->frame_count, status); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); + return 0; } /** @@ -832,8 +839,9 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) * Handles block-acknowledge notification from device, which reports success * of frames sent via aggregation. */ -void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; @@ -857,7 +865,7 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, if (scd_flow >= hw_params(priv).max_txq_num) { IWL_ERR(priv, "BUG_ON scd_flow is bigger than number of queues\n"); - return; + return 0; } sta_id = ba_resp->sta_id; @@ -877,14 +885,14 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, "BA scd_flow %d does not match txq_id %d\n", scd_flow, agg->txq_id); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); - return; + return 0; } if (unlikely(!agg->wait_for_ba)) { if (unlikely(ba_resp->bitmap)) IWL_ERR(priv, "Received BA when not expected\n"); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); - return; + return 0; } IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " @@ -901,7 +909,7 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, ba_resp->scd_ssn); /* Mark that the expected block-ack response arrived */ - agg->wait_for_ba = 0; + agg->wait_for_ba = false; /* Sanity check values reported by uCode */ if (ba_resp->txed_2_done > ba_resp->txed) { @@ -935,7 +943,10 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, else WARN_ON_ONCE(1); - if (freed == 0) { + info = IEEE80211_SKB_CB(skb); + kmem_cache_free(priv->tx_cmd_pool, (info->driver_data[1])); + + if (freed == 1) { /* this is the first skb we deliver in this batch */ /* put the rate scaling data there */ info = IEEE80211_SKB_CB(skb); @@ -948,11 +959,9 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, info); } - info = IEEE80211_SKB_CB(skb); - kmem_cache_free(priv->tx_cmd_pool, (info->driver_data[1])); - ieee80211_tx_status_irqsafe(priv->hw, skb); } spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); + return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c b/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c index a7b8914..b4e1e7c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c @@ -114,13 +114,8 @@ static int iwlagn_load_section(struct iwl_priv *priv, const char *name, FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); IWL_DEBUG_FW(priv, "%s uCode section being loaded...\n", name); - ret = wait_event_interruptible_timeout(priv->shrd->wait_command_queue, - priv->ucode_write_complete, 5 * HZ); - if (ret == -ERESTARTSYS) { - IWL_ERR(priv, "Could not load the %s uCode section due " - "to interrupt\n", name); - return ret; - } + ret = wait_event_timeout(priv->shrd->wait_command_queue, + priv->ucode_write_complete, 5 * HZ); if (!ret) { IWL_ERR(priv, "Could not load the %s uCode section\n", name); @@ -164,7 +159,7 @@ static int iwlagn_set_temperature_offset_calib(struct iwl_priv *priv) { struct iwl_calib_temperature_offset_cmd cmd; __le16 *offset_calib = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_TEMPERATURE); + (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE); memset(&cmd, 0, sizeof(cmd)); iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); @@ -178,6 +173,42 @@ static int iwlagn_set_temperature_offset_calib(struct iwl_priv *priv) (u8 *)&cmd, sizeof(cmd)); } +static int iwlagn_set_temperature_offset_calib_v2(struct iwl_priv *priv) +{ + struct iwl_calib_temperature_offset_v2_cmd cmd; + __le16 *offset_calib_high = (__le16 *)iwl_eeprom_query_addr(priv, + EEPROM_KELVIN_TEMPERATURE); + __le16 *offset_calib_low = + (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE); + struct iwl_eeprom_calib_hdr *hdr; + + memset(&cmd, 0, sizeof(cmd)); + iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); + hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv, + EEPROM_CALIB_ALL); + memcpy(&cmd.radio_sensor_offset_high, offset_calib_high, + sizeof(*offset_calib_high)); + memcpy(&cmd.radio_sensor_offset_low, offset_calib_low, + sizeof(*offset_calib_low)); + if (!(cmd.radio_sensor_offset_low)) { + IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n"); + cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET; + cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET; + } + memcpy(&cmd.burntVoltageRef, &hdr->voltage, + sizeof(hdr->voltage)); + + IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n", + le16_to_cpu(cmd.radio_sensor_offset_high)); + IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n", + le16_to_cpu(cmd.radio_sensor_offset_low)); + IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n", + le16_to_cpu(cmd.burntVoltageRef)); + + return iwl_calib_set(&priv->calib_results[IWL_CALIB_TEMP_OFFSET], + (u8 *)&cmd, sizeof(cmd)); +} + static int iwlagn_send_calib_cfg(struct iwl_priv *priv) { struct iwl_calib_cfg_cmd calib_cfg_cmd; @@ -197,8 +228,9 @@ static int iwlagn_send_calib_cfg(struct iwl_priv *priv) return iwl_trans_send_cmd(trans(priv), &cmd); } -void iwlagn_rx_calib_result(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +int iwlagn_rx_calib_result(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->u.raw; @@ -231,9 +263,10 @@ void iwlagn_rx_calib_result(struct iwl_priv *priv, default: IWL_ERR(priv, "Unknown calibration notification %d\n", hdr->op_code); - return; + return -1; } iwl_calib_set(&priv->calib_results[index], pkt->u.raw, len); + return 0; } int iwlagn_init_alive_start(struct iwl_priv *priv) @@ -263,8 +296,12 @@ int iwlagn_init_alive_start(struct iwl_priv *priv) * temperature offset calibration is only needed for runtime ucode, * so prepare the value now. */ - if (priv->cfg->need_temp_offset_calib) - return iwlagn_set_temperature_offset_calib(priv); + if (priv->cfg->need_temp_offset_calib) { + if (priv->cfg->temp_offset_v2) + return iwlagn_set_temperature_offset_calib_v2(priv); + else + return iwlagn_set_temperature_offset_calib(priv); + } return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 6057e18..baaf486 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -453,122 +453,6 @@ static void iwl_bg_tx_flush(struct work_struct *work) iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); } -/***************************************************************************** - * - * sysfs attributes - * - *****************************************************************************/ - -#ifdef CONFIG_IWLWIFI_DEBUG - -/* - * The following adds a new attribute to the sysfs representation - * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/) - * used for controlling the debug level. - * - * See the level definitions in iwl for details. - * - * The debug_level being managed using sysfs below is a per device debug - * level that is used instead of the global debug level if it (the per - * device debug level) is set. - */ -static ssize_t show_debug_level(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_shared *shrd = dev_get_drvdata(d); - return sprintf(buf, "0x%08X\n", iwl_get_debug_level(shrd)); -} -static ssize_t store_debug_level(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_shared *shrd = dev_get_drvdata(d); - struct iwl_priv *priv = shrd->priv; - unsigned long val; - int ret; - - ret = strict_strtoul(buf, 0, &val); - if (ret) - IWL_ERR(priv, "%s is not in hex or decimal form.\n", buf); - else { - shrd->dbg_level_dev = val; - if (iwl_alloc_traffic_mem(priv)) - IWL_ERR(shrd->priv, - "Not enough memory to generate traffic log\n"); - } - return strnlen(buf, count); -} - -static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, - show_debug_level, store_debug_level); - - -#endif /* CONFIG_IWLWIFI_DEBUG */ - - -static ssize_t show_temperature(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_shared *shrd = dev_get_drvdata(d); - struct iwl_priv *priv = shrd->priv; - - if (!iwl_is_alive(priv->shrd)) - return -EAGAIN; - - return sprintf(buf, "%d\n", priv->temperature); -} - -static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL); - -static ssize_t show_tx_power(struct device *d, - struct device_attribute *attr, char *buf) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - - if (!iwl_is_ready_rf(priv->shrd)) - return sprintf(buf, "off\n"); - else - return sprintf(buf, "%d\n", priv->tx_power_user_lmt); -} - -static ssize_t store_tx_power(struct device *d, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct iwl_priv *priv = dev_get_drvdata(d); - unsigned long val; - int ret; - - ret = strict_strtoul(buf, 10, &val); - if (ret) - IWL_INFO(priv, "%s is not in decimal form.\n", buf); - else { - ret = iwl_set_tx_power(priv, val, false); - if (ret) - IWL_ERR(priv, "failed setting tx power (0x%d).\n", - ret); - else - ret = count; - } - return ret; -} - -static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power); - -static struct attribute *iwl_sysfs_entries[] = { - &dev_attr_temperature.attr, - &dev_attr_tx_power.attr, -#ifdef CONFIG_IWLWIFI_DEBUG - &dev_attr_debug_level.attr, -#endif - NULL -}; - -static struct attribute_group iwl_attribute_group = { - .name = NULL, /* put in device directory */ - .attrs = iwl_sysfs_entries, -}; - /****************************************************************************** * * uCode download functions @@ -1259,13 +1143,6 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) if (err) IWL_ERR(priv, "failed to create debugfs files. Ignoring error: %d\n", err); - err = sysfs_create_group(&(priv->bus->dev->kobj), - &iwl_attribute_group); - if (err) { - IWL_ERR(priv, "failed to create sysfs device attributes\n"); - goto out_unbind; - } - /* We have our copies now, allow OS release its copies */ release_firmware(ucode_raw); complete(&priv->firmware_loading_complete); @@ -1519,9 +1396,11 @@ static void __iwl_down(struct iwl_priv *priv) if (!exit_pending) clear_bit(STATUS_EXIT_PENDING, &priv->shrd->status); - if (priv->shrd->mac80211_registered) + if (priv->mac80211_registered) ieee80211_stop_queues(priv->hw); + iwl_trans_stop_device(trans(priv)); + /* Clear out all status bits but a few that are stable across reset */ priv->shrd->status &= test_bit(STATUS_RF_KILL_HW, &priv->shrd->status) << @@ -1533,8 +1412,6 @@ static void __iwl_down(struct iwl_priv *priv) test_bit(STATUS_EXIT_PENDING, &priv->shrd->status) << STATUS_EXIT_PENDING; - iwl_trans_stop_device(trans(priv)); - dev_kfree_skb(priv->beacon_skb); priv->beacon_skb = NULL; } @@ -1868,7 +1745,7 @@ static int iwl_mac_setup_register(struct iwl_priv *priv, IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); return ret; } - priv->shrd->mac80211_registered = 1; + priv->mac80211_registered = 1; return 0; } @@ -1924,7 +1801,7 @@ static void iwlagn_mac_stop(struct ieee80211_hw *hw) IWL_DEBUG_MAC80211(priv, "leave\n"); } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int iwlagn_send_patterns(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) { @@ -1999,7 +1876,7 @@ struct wowlan_key_data { bool error, use_rsc_tsc, use_tkip; }; -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static void iwlagn_convert_p1k(u16 *p1k, __le16 *out) { int i; @@ -2636,7 +2513,7 @@ static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, mutex_lock(&priv->shrd->mutex); IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n", sta->addr); - sta_priv->common.sta_id = IWL_INVALID_STATION; + sta_priv->sta_id = IWL_INVALID_STATION; atomic_set(&sta_priv->pending_frames, 0); if (vif->type == NL80211_IFTYPE_AP) @@ -2652,7 +2529,7 @@ static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, return ret; } - sta_priv->common.sta_id = sta_id; + sta_priv->sta_id = sta_id; /* Initialize rate scaling */ IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM\n", @@ -2893,15 +2770,6 @@ static int iwl_mac_remain_on_channel(struct ieee80211_hw *hw, mutex_lock(&priv->shrd->mutex); - /* - * TODO: Remove this hack! Firmware needs to be updated - * to allow longer off-channel periods in scanning for - * this use case, based on a flag (and we'll need an API - * flag in the firmware when it has that). - */ - if (iwl_is_associated(priv, IWL_RXON_CTX_BSS) && duration > 80) - duration = 80; - if (test_bit(STATUS_SCAN_HW, &priv->shrd->status)) { err = -EBUSY; goto out; @@ -2910,6 +2778,7 @@ static int iwl_mac_remain_on_channel(struct ieee80211_hw *hw, priv->hw_roc_channel = channel; priv->hw_roc_chantype = channel_type; priv->hw_roc_duration = duration; + priv->hw_roc_start_notified = false; cancel_delayed_work(&priv->hw_roc_disable_work); if (!ctx->is_active) { @@ -3208,7 +3077,7 @@ struct ieee80211_ops iwlagn_hw_ops = { .tx = iwlagn_mac_tx, .start = iwlagn_mac_start, .stop = iwlagn_mac_stop, -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP .suspend = iwlagn_mac_suspend, .resume = iwlagn_mac_resume, #endif @@ -3314,10 +3183,9 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops, priv = hw->priv; priv->bus = bus; priv->shrd = &priv->_shrd; + bus->shrd = priv->shrd; priv->shrd->bus = bus; priv->shrd->priv = priv; - priv->shrd->hw = hw; - bus_set_drv_data(priv->bus, priv->shrd); priv->shrd->trans = trans_ops->alloc(priv->shrd); if (priv->shrd->trans == NULL) { @@ -3480,8 +3348,6 @@ void __devexit iwl_remove(struct iwl_priv * priv) IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n"); iwl_dbgfs_unregister(priv); - sysfs_remove_group(&priv->bus->dev->kobj, - &iwl_attribute_group); /* ieee80211_unregister_hw call wil cause iwl_mac_stop to * to be called and iwl_down since we are removing the device @@ -3492,9 +3358,9 @@ void __devexit iwl_remove(struct iwl_priv * priv) iwl_testmode_cleanup(priv); iwl_leds_exit(priv); - if (priv->shrd->mac80211_registered) { + if (priv->mac80211_registered) { ieee80211_unregister_hw(priv->hw); - priv->shrd->mac80211_registered = 0; + priv->mac80211_registered = 0; } iwl_tt_exit(priv); @@ -3518,8 +3384,6 @@ void __devexit iwl_remove(struct iwl_priv * priv) iwl_trans_free(trans(priv)); - bus_set_drv_data(priv->bus, NULL); - iwl_uninit_drv(priv); dev_kfree_skb(priv->beacon_skb); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index 4bc1f46..2a297d1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h @@ -88,8 +88,9 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw, u32 changes); /* uCode */ -void iwlagn_rx_calib_result(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb); +int iwlagn_rx_calib_result(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd); int iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type); void iwlagn_send_prio_tbl(struct iwl_priv *priv); int iwlagn_run_init_ucode(struct iwl_priv *priv); @@ -116,9 +117,11 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn); int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid); -void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb); -void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); +int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd); +int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd); static inline u32 iwl_tx_status_to_mac80211(u32 status) { @@ -146,7 +149,6 @@ static inline bool iwl_is_tx_success(u32 status) u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid); /* scan */ -int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif); void iwlagn_post_scan(struct iwl_priv *priv); void iwlagn_disable_roc(struct iwl_priv *priv); @@ -156,8 +158,9 @@ int iwlagn_manage_ibss_station(struct iwl_priv *priv, /* bt coex */ void iwlagn_send_advance_bt_config(struct iwl_priv *priv); -void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb); +int iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd); void iwlagn_bt_rx_handler_setup(struct iwl_priv *priv); void iwlagn_bt_setup_deferred_work(struct iwl_priv *priv); void iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv); diff --git a/drivers/net/wireless/iwlwifi/iwl-bus.h b/drivers/net/wireless/iwlwifi/iwl-bus.h index 83aed46..08b9759 100644 --- a/drivers/net/wireless/iwlwifi/iwl-bus.h +++ b/drivers/net/wireless/iwlwifi/iwl-bus.h @@ -63,19 +63,65 @@ #ifndef __iwl_bus_h__ #define __iwl_bus_h__ -/*This file includes the declaration that are exported from the bus layer */ - #include <linux/types.h> #include <linux/spinlock.h> +/** + * DOC: Bus layer - role and goal + * + * iwl-bus.h defines the API to the bus layer of the iwlwifi driver. + * The bus layer is responsible for doing very basic bus operations that are + * listed in the iwl_bus_ops structure. + * The bus layer registers to the bus driver, advertises the supported HW and + * gets notifications about enumeration, suspend, resume. + * For the moment, the bus layer is not a linux kernel module as itself, and + * the module_init function of the driver must call the bus specific + * registration functions. These functions are listed at the end of this file. + * For the moment, there is only one implementation of this interface: PCI-e. + * This implementation is iwl-pci.c + */ + +/** + * DOC: encapsulation and type safety + * + * The iwl_bus describes the data that is shared amongst all the bus layer + * implementations. This data is visible to other layers. Data in the bus + * specific area is not visible outside the bus specific implementation. + * iwl_bus holds a pointer to iwl_shared which holds pointer to all the other + * layers of the driver (iwl_priv, iwl_trans). In fact, this is the way to go + * when the transport layer needs to call a function of another layer. + * + * In order to achieve encapsulation, iwl_priv cannot be dereferenced from the + * bus layer. Type safety is still kept since functions that gets iwl_priv gets + * a typed pointer (as opposed to void *). + */ + +/** + * DOC: probe flow + * + * The module_init calls the bus specific registration function. The + * registration to the bus layer will trigger an enumeration of the bus which + * will call the bus specific probe function. + * The first thing this function must do is to allocate the memory needed by + * iwl_bus + the bus_specific data. + * Once the bus specific probe function has configured the hardware, it + * chooses the appropriate transport layer and calls iwl_probe that will run + * the bus independent probe flow. + * + * Note: The bus specific code must set the following data in iwl_bus before it + * calls iwl_probe: + * * bus->dev + * * bus->irq + * * bus->ops + */ + struct iwl_shared; struct iwl_bus; /** * struct iwl_bus_ops - bus specific operations * @get_pm_support: must returns true if the bus can go to sleep - * @apm_config: will be called during the config of the APM configuration - * @set_drv_data: set the shared data pointer to the bus layer + * @apm_config: will be called during the config of the APM * @get_hw_id: prints the hw_id in the provided buffer * @write8: write a byte to register at offset ofs * @write32: write a dword to register at offset ofs @@ -84,7 +130,6 @@ struct iwl_bus; struct iwl_bus_ops { bool (*get_pm_support)(struct iwl_bus *bus); void (*apm_config)(struct iwl_bus *bus); - void (*set_drv_data)(struct iwl_bus *bus, struct iwl_shared *shrd); void (*get_hw_id)(struct iwl_bus *bus, char buf[], int buf_len); void (*write8)(struct iwl_bus *bus, u32 ofs, u8 val); void (*write32)(struct iwl_bus *bus, u32 ofs, u32 val); @@ -93,14 +138,18 @@ struct iwl_bus_ops { /** * struct iwl_bus - bus common data - * @dev - pointer to struct device * that represent the device + * + * This data is common to all bus layer implementations. + * + * @dev - pointer to struct device * that represents the device * @ops - pointer to iwl_bus_ops * @shrd - pointer to iwl_shared which holds shared data from the upper layer + * NB: for the time being this needs to be set by the upper layer since + * it allocates the shared data * @irq - the irq number for the device * @reg_lock - protect hw register access */ struct iwl_bus { - /* Common data to all buses */ struct device *dev; const struct iwl_bus_ops *ops; struct iwl_shared *shrd; @@ -123,12 +172,6 @@ static inline void bus_apm_config(struct iwl_bus *bus) bus->ops->apm_config(bus); } -static inline void bus_set_drv_data(struct iwl_bus *bus, - struct iwl_shared *shrd) -{ - bus->ops->set_drv_data(bus, shrd); -} - static inline void bus_get_hw_id(struct iwl_bus *bus, char buf[], int buf_len) { bus->ops->get_hw_id(bus, buf, buf_len); @@ -149,6 +192,9 @@ static inline u32 bus_read32(struct iwl_bus *bus, u32 ofs) return bus->ops->read32(bus, ofs); } +/***************************************************** +* Bus layer registration functions +******************************************************/ int __must_check iwl_pci_register_driver(void); void iwl_pci_unregister_driver(void); diff --git a/drivers/net/wireless/iwlwifi/iwl-pci.h b/drivers/net/wireless/iwlwifi/iwl-cfg.h index c0aea9e..d4f317c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-pci.h +++ b/drivers/net/wireless/iwlwifi/iwl-cfg.h @@ -64,11 +64,10 @@ #define __iwl_pci_h__ -/* This file includes the declaration that are internal to the PCI - * implementation of the bus layer +/* + * This file declares the config structures for all devices. */ -/* configuration for the _agn devices */ extern struct iwl_cfg iwl5300_agn_cfg; extern struct iwl_cfg iwl5100_agn_cfg; extern struct iwl_cfg iwl5350_agn_cfg; diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 82bfef4..64593aa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -3263,6 +3263,14 @@ struct iwl_calib_temperature_offset_cmd { __le16 reserved; } __packed; +struct iwl_calib_temperature_offset_v2_cmd { + struct iwl_calib_hdr hdr; + __le16 radio_sensor_offset_high; + __le16 radio_sensor_offset_low; + __le16 burntVoltageRef; + __le16 reserved; +} __packed; + /* IWL_PHY_CALIBRATE_CHAIN_NOISE_RESET_CMD */ struct iwl_calib_chain_noise_reset_cmd { struct iwl_calib_hdr hdr; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 9270f99..fc400bb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -125,12 +125,12 @@ int iwl_init_geos(struct iwl_priv *priv) return 0; } - channels = kzalloc(sizeof(struct ieee80211_channel) * - priv->channel_count, GFP_KERNEL); + channels = kcalloc(priv->channel_count, + sizeof(struct ieee80211_channel), GFP_KERNEL); if (!channels) return -ENOMEM; - rates = kzalloc((sizeof(struct ieee80211_rate) * IWL_RATE_COUNT_LEGACY), + rates = kcalloc(IWL_RATE_COUNT_LEGACY, sizeof(struct ieee80211_rate), GFP_KERNEL); if (!rates) { kfree(channels); @@ -817,7 +817,8 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) } #ifdef CONFIG_IWLWIFI_DEBUG -void iwl_print_rx_config_cmd(struct iwl_priv *priv, u8 ctxid) +void iwl_print_rx_config_cmd(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid) { struct iwl_rxon_context *ctx = &priv->contexts[ctxid]; struct iwl_rxon_cmd *rxon = &ctx->staging; @@ -868,7 +869,7 @@ void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) * commands by clearing the ready bit */ clear_bit(STATUS_READY, &priv->shrd->status); - wake_up_interruptible(&priv->shrd->wait_command_queue); + wake_up(&priv->shrd->wait_command_queue); if (!ondemand) { /* @@ -1327,7 +1328,13 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw, mutex_lock(&priv->shrd->mutex); - WARN_ON(ctx->vif != vif); + if (WARN_ON(ctx->vif != vif)) { + struct iwl_rxon_context *tmp; + IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif); + for_each_context(priv, tmp) + IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n", + tmp->ctxid, tmp, tmp->vif); + } ctx->vif = NULL; iwl_teardown_interface(priv, vif, false); @@ -1802,13 +1809,12 @@ u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, u32 beacon_interval) return 0; quot = (usec / interval) & - (iwl_beacon_time_mask_high(priv, - hw_params(priv).beacon_time_tsf_bits) >> - hw_params(priv).beacon_time_tsf_bits); + (iwl_beacon_time_mask_high(priv, IWLAGN_EXT_BEACON_TIME_POS) >> + IWLAGN_EXT_BEACON_TIME_POS); rem = (usec % interval) & iwl_beacon_time_mask_low(priv, - hw_params(priv).beacon_time_tsf_bits); + IWLAGN_EXT_BEACON_TIME_POS); - return (quot << hw_params(priv).beacon_time_tsf_bits) + rem; + return (quot << IWLAGN_EXT_BEACON_TIME_POS) + rem; } /* base is usually what we get from ucode with each received frame, @@ -1818,22 +1824,22 @@ __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, u32 addon, u32 beacon_interval) { u32 base_low = base & iwl_beacon_time_mask_low(priv, - hw_params(priv).beacon_time_tsf_bits); + IWLAGN_EXT_BEACON_TIME_POS); u32 addon_low = addon & iwl_beacon_time_mask_low(priv, - hw_params(priv).beacon_time_tsf_bits); + IWLAGN_EXT_BEACON_TIME_POS); u32 interval = beacon_interval * TIME_UNIT; u32 res = (base & iwl_beacon_time_mask_high(priv, - hw_params(priv).beacon_time_tsf_bits)) + + IWLAGN_EXT_BEACON_TIME_POS)) + (addon & iwl_beacon_time_mask_high(priv, - hw_params(priv).beacon_time_tsf_bits)); + IWLAGN_EXT_BEACON_TIME_POS)); if (base_low > addon_low) res += base_low - addon_low; else if (base_low < addon_low) { res += interval + base_low - addon_low; - res += (1 << hw_params(priv).beacon_time_tsf_bits); + res += (1 << IWLAGN_EXT_BEACON_TIME_POS); } else - res += (1 << hw_params(priv).beacon_time_tsf_bits); + res += (1 << IWLAGN_EXT_BEACON_TIME_POS); return cpu_to_le32(res); } @@ -1876,3 +1882,22 @@ void iwl_nic_config(struct iwl_priv *priv) priv->cfg->lib->nic_config(priv); } + +void iwl_free_skb(struct iwl_priv *priv, struct sk_buff *skb) +{ + struct ieee80211_tx_info *info; + + info = IEEE80211_SKB_CB(skb); + kmem_cache_free(priv->tx_cmd_pool, (info->driver_data[1])); + dev_kfree_skb_any(skb); +} + +void iwl_stop_sw_queue(struct iwl_priv *priv, u8 ac) +{ + ieee80211_stop_queue(priv->hw, ac); +} + +void iwl_wake_sw_queue(struct iwl_priv *priv, u8 ac) +{ + ieee80211_wake_queue(priv->hw, ac); +} diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 56b554c..e55ffad 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -108,7 +108,6 @@ struct iwl_lib_ops { * radio tuning when there is a high receiving plcp error rate * @chain_noise_scale: default chain noise scale used for gain computation * @wd_timeout: TX queues watchdog timeout - * @temperature_kelvin: temperature report by uCode in kelvin * @max_event_log_size: size of event log buffer size for ucode event logging * @shadow_reg_enable: HW shadhow register bit * @no_idle_support: do not support idle mode @@ -124,14 +123,12 @@ struct iwl_base_params { const u16 max_ll_items; const bool shadow_ram_support; u16 led_compensation; - int chain_noise_num_beacons; bool adv_thermal_throttle; bool support_ct_kill_exit; const bool support_wimax_coexist; u8 plcp_delta_threshold; s32 chain_noise_scale; unsigned int wd_timeout; - bool temperature_kelvin; u32 max_event_log_size; const bool shadow_reg_enable; const bool no_idle_support; @@ -194,6 +191,7 @@ struct iwl_ht_params { * @rx_with_siso_diversity: 1x1 device with rx antenna diversity * @internal_wimax_coex: internal wifi/wimax combo device * @iq_invert: I/Q inversion + * @temp_offset_v2: support v2 of temperature offset calibration * * We enable the driver to be backward compatible wrt API version. The * driver specifies which APIs it supports (with @ucode_api_max being the @@ -231,6 +229,7 @@ struct iwl_cfg { const bool rx_with_siso_diversity; const bool internal_wimax_coex; const bool iq_invert; + const bool temp_offset_v2; }; /*************************** @@ -319,7 +318,7 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force); ******************************************************************************/ void iwl_init_scan_params(struct iwl_priv *priv); int iwl_scan_cancel(struct iwl_priv *priv); -int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); +void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); void iwl_force_scan_end(struct iwl_priv *priv); int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -329,12 +328,6 @@ 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, const u8 *ta, const u8 *ie, int ie_len, int left); void iwl_setup_rx_scan_handlers(struct iwl_priv *priv); -u16 iwl_get_active_dwell_time(struct iwl_priv *priv, - enum ieee80211_band band, - u8 n_probes); -u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, - enum ieee80211_band band, - struct ieee80211_vif *vif); void iwl_setup_scan_deferred_work(struct iwl_priv *priv); void iwl_cancel_scan_deferred_work(struct iwl_priv *priv); int __must_check iwl_scan_initiate(struct iwl_priv *priv, diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index d0c63cf..6d49dfb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -64,6 +64,14 @@ goto err; \ } while (0) +#define DEBUGFS_ADD_U32(name, parent, ptr, mode) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_u32(#name, mode, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + /* file operation */ #define DEBUGFS_READ_FUNC(name) \ static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ @@ -707,6 +715,20 @@ static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } +static ssize_t iwl_dbgfs_temperature_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "%d\n", priv->temperature); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + + static ssize_t iwl_dbgfs_sleep_level_override_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) @@ -801,6 +823,7 @@ DEBUGFS_READ_WRITE_FILE_OPS(rx_handlers); DEBUGFS_READ_FILE_OPS(qos); DEBUGFS_READ_FILE_OPS(thermal_throttling); DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); +DEBUGFS_READ_FILE_OPS(temperature); DEBUGFS_READ_WRITE_FILE_OPS(sleep_level_override); DEBUGFS_READ_FILE_OPS(current_sleep_command); @@ -2229,8 +2252,8 @@ static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file, static ssize_t iwl_dbgfs_force_reset_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) { - + size_t count, loff_t *ppos) +{ struct iwl_priv *priv = file->private_data; int i, pos = 0; char buf[300]; @@ -2309,8 +2332,8 @@ static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file, static ssize_t iwl_dbgfs_wd_timeout_write(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) { - + size_t count, loff_t *ppos) +{ struct iwl_priv *priv = file->private_data; char buf[8]; int buf_size; @@ -2445,6 +2468,52 @@ DEBUGFS_READ_FILE_OPS(bt_traffic); DEBUGFS_READ_WRITE_FILE_OPS(protection_mode); DEBUGFS_READ_FILE_OPS(reply_tx_error); +#ifdef CONFIG_IWLWIFI_DEBUG +static ssize_t iwl_dbgfs_debug_level_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct iwl_shared *shrd = priv->shrd; + char buf[11]; + int len; + + len = scnprintf(buf, sizeof(buf), "0x%.8x", + iwl_get_debug_level(shrd)); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t iwl_dbgfs_debug_level_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct iwl_shared *shrd = priv->shrd; + char buf[11]; + unsigned long val; + int ret; + + if (count > sizeof(buf)) + return -EINVAL; + + memset(buf, 0, sizeof(buf)); + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + ret = strict_strtoul(buf, 0, &val); + if (ret) + return ret; + + shrd->dbg_level_dev = val; + if (iwl_alloc_traffic_mem(priv)) + IWL_ERR(priv, "Not enough memory to generate traffic log\n"); + + return count; +} +DEBUGFS_READ_WRITE_FILE_OPS(debug_level); +#endif /* CONFIG_IWLWIFI_DEBUG */ + /* * Create the debugfs files and directories * @@ -2482,6 +2551,8 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) DEBUGFS_ADD_FILE(current_sleep_command, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(thermal_throttling, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(temperature, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(rx_statistics, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(tx_statistics, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(traffic_log, dir_debug, S_IWUSR | S_IRUSR); @@ -2496,7 +2567,6 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(txfifo_flush, dir_debug, S_IWUSR); DEBUGFS_ADD_FILE(protection_mode, dir_debug, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR); @@ -2507,6 +2577,10 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) DEBUGFS_ADD_FILE(wd_timeout, dir_debug, S_IWUSR); if (iwl_advanced_bt_coexist(priv)) DEBUGFS_ADD_FILE(bt_traffic, dir_debug, S_IRUSR); +#ifdef CONFIG_IWLWIFI_DEBUG + DEBUGFS_ADD_FILE(debug_level, dir_debug, S_IRUSR | S_IWUSR); +#endif + DEBUGFS_ADD_BOOL(disable_sensitivity, dir_rf, &priv->disable_sens_cal); DEBUGFS_ADD_BOOL(disable_chain_noise, dir_rf, @@ -2534,6 +2608,3 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv) debugfs_remove_recursive(priv->debugfs_dir); priv->debugfs_dir = NULL; } - - - diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 8438a33..257aa9a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -202,11 +202,6 @@ struct iwl_station_entry { struct iwl_link_quality_cmd *lq; }; -struct iwl_station_priv_common { - struct iwl_rxon_context *ctx; - u8 sta_id; -}; - /* * iwl_station_priv: Driver's private station information * @@ -215,12 +210,13 @@ struct iwl_station_priv_common { * space. */ struct iwl_station_priv { - struct iwl_station_priv_common common; + struct iwl_rxon_context *ctx; struct iwl_lq_sta lq_sta; atomic_t pending_frames; bool client; bool asleep; u8 max_agg_bufsize; + u8 sta_id; }; /** @@ -845,8 +841,9 @@ struct iwl_priv { void (*pre_rx_handler)(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); - void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb); + int (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd); struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; @@ -880,7 +877,7 @@ struct iwl_priv { u8 channel_count; /* # of channels */ /* thermal calibration */ - s32 temperature; /* degrees Kelvin */ + s32 temperature; /* Celsius */ s32 last_temperature; /* init calibration results */ @@ -958,6 +955,8 @@ struct iwl_priv { struct iwl_station_entry stations[IWLAGN_STATION_COUNT]; unsigned long ucode_key_table; + u8 mac80211_registered; + /* Indication if ieee80211_ops->open has been called */ u8 is_open; @@ -1031,7 +1030,7 @@ struct iwl_priv { struct delayed_work hw_roc_disable_work; enum nl80211_channel_type hw_roc_chantype; int hw_roc_duration; - bool hw_roc_setup; + bool hw_roc_setup, hw_roc_start_notified; /* bt coex */ u8 bt_enable_flag; diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 80ee65b..a4e43bd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -72,6 +72,7 @@ #include "iwl-dev.h" #include "iwl-core.h" #include "iwl-debug.h" +#include "iwl-agn.h" #include "iwl-eeprom.h" #include "iwl-io.h" @@ -138,7 +139,7 @@ static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ /****************************************************************************** * - * EEPROM related functions + * generic NVM functions * ******************************************************************************/ @@ -214,6 +215,93 @@ static int iwl_eeprom_verify_signature(struct iwl_priv *priv) return ret; } +u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset) +{ + if (!priv->eeprom) + return 0; + return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); +} + +int iwl_eeprom_check_version(struct iwl_priv *priv) +{ + u16 eeprom_ver; + u16 calib_ver; + + eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); + calib_ver = iwlagn_eeprom_calib_version(priv); + + if (eeprom_ver < priv->cfg->eeprom_ver || + calib_ver < priv->cfg->eeprom_calib_ver) + goto err; + + IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n", + eeprom_ver, calib_ver); + + return 0; +err: + IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x " + "CALIB=0x%x < 0x%x\n", + eeprom_ver, priv->cfg->eeprom_ver, + calib_ver, priv->cfg->eeprom_calib_ver); + return -EINVAL; + +} + +int iwl_eeprom_check_sku(struct iwl_priv *priv) +{ + u16 radio_cfg; + + if (!priv->cfg->sku) { + /* not using sku overwrite */ + priv->cfg->sku = iwl_eeprom_query16(priv, EEPROM_SKU_CAP); + if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE && + !priv->cfg->ht_params) { + IWL_ERR(priv, "Invalid 11n configuration\n"); + return -EINVAL; + } + } + if (!priv->cfg->sku) { + IWL_ERR(priv, "Invalid device sku\n"); + return -EINVAL; + } + + IWL_INFO(priv, "Device SKU: 0X%x\n", priv->cfg->sku); + + if (!priv->cfg->valid_tx_ant && !priv->cfg->valid_rx_ant) { + /* not using .cfg overwrite */ + radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); + priv->cfg->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg); + priv->cfg->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg); + if (!priv->cfg->valid_tx_ant || !priv->cfg->valid_rx_ant) { + IWL_ERR(priv, "Invalid chain (0X%x, 0X%x)\n", + priv->cfg->valid_tx_ant, + priv->cfg->valid_rx_ant); + return -EINVAL; + } + IWL_INFO(priv, "Valid Tx ant: 0X%x, Valid Rx ant: 0X%x\n", + priv->cfg->valid_tx_ant, priv->cfg->valid_rx_ant); + } + /* + * for some special cases, + * EEPROM did not reflect the correct antenna setting + * so overwrite the valid tx/rx antenna from .cfg + */ + return 0; +} + +void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac) +{ + const u8 *addr = iwl_eeprom_query_addr(priv, + EEPROM_MAC_ADDRESS); + memcpy(mac, addr, ETH_ALEN); +} + +/****************************************************************************** + * + * OTP related functions + * +******************************************************************************/ + static void iwl_set_otp_access(struct iwl_priv *priv, enum iwl_access_mode mode) { iwl_read32(bus(priv), CSR_OTP_GP_REG); @@ -407,11 +495,152 @@ static int iwl_find_otp_image(struct iwl_priv *priv, return -EINVAL; } -u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset) +/****************************************************************************** + * + * Tx Power related functions + * +******************************************************************************/ +/** + * iwl_get_max_txpower_avg - get the highest tx power from all chains. + * find the highest tx power from all chains for the channel + */ +static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv, + struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, + int element, s8 *max_txpower_in_half_dbm) { - if (!priv->eeprom) - return 0; - return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); + s8 max_txpower_avg = 0; /* (dBm) */ + + /* Take the highest tx power from any valid chains */ + if ((priv->cfg->valid_tx_ant & ANT_A) && + (enhanced_txpower[element].chain_a_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_a_max; + if ((priv->cfg->valid_tx_ant & ANT_B) && + (enhanced_txpower[element].chain_b_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_b_max; + if ((priv->cfg->valid_tx_ant & ANT_C) && + (enhanced_txpower[element].chain_c_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].chain_c_max; + if (((priv->cfg->valid_tx_ant == ANT_AB) | + (priv->cfg->valid_tx_ant == ANT_BC) | + (priv->cfg->valid_tx_ant == ANT_AC)) && + (enhanced_txpower[element].mimo2_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].mimo2_max; + if ((priv->cfg->valid_tx_ant == ANT_ABC) && + (enhanced_txpower[element].mimo3_max > max_txpower_avg)) + max_txpower_avg = enhanced_txpower[element].mimo3_max; + + /* + * max. tx power in EEPROM is in 1/2 dBm format + * convert from 1/2 dBm to dBm (round-up convert) + * but we also do not want to loss 1/2 dBm resolution which + * will impact performance + */ + *max_txpower_in_half_dbm = max_txpower_avg; + return (max_txpower_avg & 0x01) + (max_txpower_avg >> 1); +} + +static void +iwl_eeprom_enh_txp_read_element(struct iwl_priv *priv, + struct iwl_eeprom_enhanced_txpwr *txp, + s8 max_txpower_avg) +{ + int ch_idx; + bool is_ht40 = txp->flags & IWL_EEPROM_ENH_TXP_FL_40MHZ; + enum ieee80211_band band; + + band = txp->flags & IWL_EEPROM_ENH_TXP_FL_BAND_52G ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + + for (ch_idx = 0; ch_idx < priv->channel_count; ch_idx++) { + struct iwl_channel_info *ch_info = &priv->channel_info[ch_idx]; + + /* update matching channel or from common data only */ + if (txp->channel != 0 && ch_info->channel != txp->channel) + continue; + + /* update matching band only */ + if (band != ch_info->band) + continue; + + if (ch_info->max_power_avg < max_txpower_avg && !is_ht40) { + ch_info->max_power_avg = max_txpower_avg; + ch_info->curr_txpow = max_txpower_avg; + ch_info->scan_power = max_txpower_avg; + } + + if (is_ht40 && ch_info->ht40_max_power_avg < max_txpower_avg) + ch_info->ht40_max_power_avg = max_txpower_avg; + } +} + +#define EEPROM_TXP_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT) +#define EEPROM_TXP_ENTRY_LEN sizeof(struct iwl_eeprom_enhanced_txpwr) +#define EEPROM_TXP_SZ_OFFS (0x00 | INDIRECT_ADDRESS | INDIRECT_TXP_LIMIT_SIZE) + +#define TXP_CHECK_AND_PRINT(x) ((txp->flags & IWL_EEPROM_ENH_TXP_FL_##x) \ + ? # x " " : "") + +void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv) +{ + struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; + int idx, entries; + __le16 *txp_len; + s8 max_txp_avg, max_txp_avg_halfdbm; + + BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); + + /* the length is in 16-bit words, but we want entries */ + txp_len = (__le16 *) iwl_eeprom_query_addr(priv, EEPROM_TXP_SZ_OFFS); + entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; + + txp_array = (void *) iwl_eeprom_query_addr(priv, EEPROM_TXP_OFFS); + + for (idx = 0; idx < entries; idx++) { + txp = &txp_array[idx]; + /* skip invalid entries */ + if (!(txp->flags & IWL_EEPROM_ENH_TXP_FL_VALID)) + continue; + + IWL_DEBUG_EEPROM(priv, "%s %d:\t %s%s%s%s%s%s%s%s (0x%02x)\n", + (txp->channel && (txp->flags & + IWL_EEPROM_ENH_TXP_FL_COMMON_TYPE)) ? + "Common " : (txp->channel) ? + "Channel" : "Common", + (txp->channel), + TXP_CHECK_AND_PRINT(VALID), + TXP_CHECK_AND_PRINT(BAND_52G), + TXP_CHECK_AND_PRINT(OFDM), + TXP_CHECK_AND_PRINT(40MHZ), + TXP_CHECK_AND_PRINT(HT_AP), + TXP_CHECK_AND_PRINT(RES1), + TXP_CHECK_AND_PRINT(RES2), + TXP_CHECK_AND_PRINT(COMMON_TYPE), + txp->flags); + IWL_DEBUG_EEPROM(priv, "\t\t chain_A: 0x%02x " + "chain_B: 0X%02x chain_C: 0X%02x\n", + txp->chain_a_max, txp->chain_b_max, + txp->chain_c_max); + IWL_DEBUG_EEPROM(priv, "\t\t MIMO2: 0x%02x " + "MIMO3: 0x%02x High 20_on_40: 0x%02x " + "Low 20_on_40: 0x%02x\n", + txp->mimo2_max, txp->mimo3_max, + ((txp->delta_20_in_40 & 0xf0) >> 4), + (txp->delta_20_in_40 & 0x0f)); + + max_txp_avg = iwl_get_max_txpower_avg(priv, txp_array, idx, + &max_txp_avg_halfdbm); + + /* + * Update the user limit values values to the highest + * power supported by any channel + */ + if (max_txp_avg > priv->tx_power_user_lmt) + priv->tx_power_user_lmt = max_txp_avg; + if (max_txp_avg_halfdbm > priv->tx_power_lmt_in_half_dbm) + priv->tx_power_lmt_in_half_dbm = max_txp_avg_halfdbm; + + iwl_eeprom_enh_txp_read_element(priv, txp, max_txp_avg); + } } /** @@ -670,8 +899,9 @@ int iwl_init_channel_map(struct iwl_priv *priv) IWL_DEBUG_EEPROM(priv, "Parsing data for %d channels.\n", priv->channel_count); - priv->channel_info = kzalloc(sizeof(struct iwl_channel_info) * - priv->channel_count, GFP_KERNEL); + priv->channel_info = kcalloc(priv->channel_count, + sizeof(struct iwl_channel_info), + GFP_KERNEL); if (!priv->channel_info) { IWL_ERR(priv, "Could not allocate channel_info\n"); priv->channel_count = 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index e2b5e0e..c94747e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -163,11 +163,19 @@ struct iwl_eeprom_enhanced_txpwr { } __packed; /* calibration */ +struct iwl_eeprom_calib_hdr { + u8 version; + u8 pa_type; + __le16 voltage; +} __packed; + #define EEPROM_CALIB_ALL (INDIRECT_ADDRESS | INDIRECT_CALIBRATION) #define EEPROM_XTAL ((2*0x128) | EEPROM_CALIB_ALL) /* temperature */ -#define EEPROM_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL) +#define EEPROM_KELVIN_TEMPERATURE ((2*0x12A) | EEPROM_CALIB_ALL) +#define EEPROM_RAW_TEMPERATURE ((2*0x12B) | EEPROM_CALIB_ALL) + /* agn links */ #define EEPROM_LINK_HOST (2*0x64) diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index 7dffed1..f149165 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -104,7 +104,6 @@ static int iwl_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) .len = { sizeof(struct iwl_led_cmd), }, .data = { led_cmd, }, .flags = CMD_ASYNC, - .callback = NULL, }; u32 reg; diff --git a/drivers/net/wireless/iwlwifi/iwl-pci.c b/drivers/net/wireless/iwlwifi/iwl-pci.c index 78a3f8d..1d7bb74 100644 --- a/drivers/net/wireless/iwlwifi/iwl-pci.c +++ b/drivers/net/wireless/iwlwifi/iwl-pci.c @@ -68,7 +68,7 @@ #include "iwl-shared.h" #include "iwl-trans.h" #include "iwl-csr.h" -#include "iwl-pci.h" +#include "iwl-cfg.h" /* PCI registers */ #define PCI_CFG_RETRY_TIMEOUT 0x041 @@ -134,12 +134,6 @@ static void iwl_pci_apm_config(struct iwl_bus *bus) } } -static void iwl_pci_set_drv_data(struct iwl_bus *bus, struct iwl_shared *shrd) -{ - bus->shrd = shrd; - pci_set_drvdata(IWL_BUS_GET_PCI_DEV(bus), shrd); -} - static void iwl_pci_get_hw_id(struct iwl_bus *bus, char buf[], int buf_len) { @@ -168,7 +162,6 @@ static u32 iwl_pci_read32(struct iwl_bus *bus, u32 ofs) static const struct iwl_bus_ops bus_ops_pci = { .get_pm_support = iwl_pci_is_pm_supported, .apm_config = iwl_pci_apm_config, - .set_drv_data = iwl_pci_set_drv_data, .get_hw_id = iwl_pci_get_hw_id, .write8 = iwl_pci_write8, .write32 = iwl_pci_write32, @@ -260,6 +253,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)}, {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)}, {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)}, /* 6x30 Series */ {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)}, @@ -392,6 +386,8 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_bus = IWL_BUS_GET_PCI_BUS(bus); pci_bus->pci_dev = pdev; + pci_set_drvdata(pdev, bus); + /* W/A - seems to solve weird behavior. We need to remove this if we * don't want to stay in L1 all the time. This wastes a lot of power */ pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | @@ -485,17 +481,18 @@ out_no_pci: static void __devexit iwl_pci_remove(struct pci_dev *pdev) { - struct iwl_shared *shrd = pci_get_drvdata(pdev); - struct iwl_bus *bus = shrd->bus; + struct iwl_bus *bus = pci_get_drvdata(pdev); struct iwl_pci_bus *pci_bus = IWL_BUS_GET_PCI_BUS(bus); + struct pci_dev *pci_dev = IWL_BUS_GET_PCI_DEV(bus); + struct iwl_shared *shrd = bus->shrd; iwl_remove(shrd->priv); - pci_disable_msi(pci_bus->pci_dev); - pci_iounmap(pci_bus->pci_dev, pci_bus->hw_base); - pci_release_regions(pci_bus->pci_dev); - pci_disable_device(pci_bus->pci_dev); - pci_set_drvdata(pci_bus->pci_dev, NULL); + pci_disable_msi(pci_dev); + pci_iounmap(pci_dev, pci_bus->hw_base); + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); + pci_set_drvdata(pci_dev, NULL); kfree(bus); } @@ -505,7 +502,8 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) static int iwl_pci_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); - struct iwl_shared *shrd = pci_get_drvdata(pdev); + struct iwl_bus *bus = pci_get_drvdata(pdev); + struct iwl_shared *shrd = bus->shrd; /* Before you put code here, think about WoWLAN. You cannot check here * whether WoWLAN is enabled or not, and your code will run even if @@ -518,7 +516,8 @@ static int iwl_pci_suspend(struct device *device) static int iwl_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); - struct iwl_shared *shrd = pci_get_drvdata(pdev); + struct iwl_bus *bus = pci_get_drvdata(pdev); + struct iwl_shared *shrd = bus->shrd; /* Before you put code here, think about WoWLAN. You cannot check here * whether WoWLAN is enabled or not, and your code will run even if diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index c7e6a74..bcd7f64 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -130,8 +130,9 @@ const char *get_cmd_string(u8 cmd) * ******************************************************************************/ -static void iwl_rx_reply_error(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_reply_error(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); @@ -142,9 +143,11 @@ static void iwl_rx_reply_error(struct iwl_priv *priv, pkt->u.err_resp.cmd_id, le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num), le32_to_cpu(pkt->u.err_resp.error_info)); + return 0; } -static void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_csa_notification *csa = &(pkt->u.csa_notif); @@ -156,7 +159,7 @@ static void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) struct iwl_rxon_cmd *rxon = (void *)&ctx->active; if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status)) - return; + return 0; if (!le32_to_cpu(csa->status) && csa->channel == priv->switch_channel) { rxon->channel = csa->channel; @@ -169,11 +172,13 @@ static void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) le16_to_cpu(csa->channel)); iwl_chswitch_done(priv, false); } + return 0; } -static void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif); @@ -181,15 +186,17 @@ static void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, if (!report->state) { IWL_DEBUG_11H(priv, "Spectrum Measure Notification: Start\n"); - return; + return 0; } memcpy(&priv->measure_report, report, sizeof(*report)); priv->measurement_status |= MEASUREMENT_READY; + return 0; } -static void iwl_rx_pm_sleep_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_pm_sleep_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { #ifdef CONFIG_IWLWIFI_DEBUG struct iwl_rx_packet *pkt = rxb_addr(rxb); @@ -197,10 +204,12 @@ static void iwl_rx_pm_sleep_notif(struct iwl_priv *priv, IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n", sleep->pm_sleep_mode, sleep->pm_wakeup_src); #endif + return 0; } -static void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u32 __maybe_unused len = @@ -209,10 +218,12 @@ static void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv, "notification for %s:\n", len, get_cmd_string(pkt->hdr.cmd)); iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->u.raw, len); + return 0; } -static void iwl_rx_beacon_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_beacon_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwlagn_beacon_notif *beacon = (void *)pkt->u.raw; @@ -233,6 +244,7 @@ static void iwl_rx_beacon_notif(struct iwl_priv *priv, if (!test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) queue_work(priv->shrd->workqueue, &priv->beacon_update); + return 0; } /* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */ @@ -475,8 +487,9 @@ iwl_accumulative_statistics(struct iwl_priv *priv, } #endif -static void iwl_rx_statistics(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { unsigned long stamp = jiffies; const int reg_recalib_period = 60; @@ -530,7 +543,7 @@ static void iwl_rx_statistics(struct iwl_priv *priv, WARN_ONCE(1, "len %d doesn't match BT (%zu) or normal (%zu)\n", len, sizeof(struct iwl_bt_notif_statistics), sizeof(struct iwl_notif_statistics)); - return; + return 0; } change = common->temperature != priv->statistics.common.temperature || @@ -573,10 +586,12 @@ static void iwl_rx_statistics(struct iwl_priv *priv, } if (priv->cfg->lib->temperature && change) priv->cfg->lib->temperature(priv); + return 0; } -static void iwl_rx_reply_statistics(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_reply_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); @@ -591,13 +606,15 @@ static void iwl_rx_reply_statistics(struct iwl_priv *priv, #endif IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); } - iwl_rx_statistics(priv, rxb); + iwl_rx_statistics(priv, rxb, cmd); + return 0; } /* Handle notification from uCode that card's power state is changing * due to software, hardware, or critical temperature RFKILL */ -static void iwl_rx_card_state_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_card_state_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); @@ -644,11 +661,13 @@ static void iwl_rx_card_state_notif(struct iwl_priv *priv, wiphy_rfkill_set_hw_state(priv->hw->wiphy, test_bit(STATUS_RF_KILL_HW, &priv->shrd->status)); else - wake_up_interruptible(&priv->shrd->wait_command_queue); + wake_up(&priv->shrd->wait_command_queue); + return 0; } -static void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_missed_beacon_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); @@ -666,18 +685,21 @@ static void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, if (!test_bit(STATUS_SCANNING, &priv->shrd->status)) iwl_init_sensitivity(priv); } + return 0; } /* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD). * This will be used later in iwl_rx_reply_rx() for REPLY_RX_MPDU_CMD. */ -static void iwl_rx_reply_rx_phy(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_reply_rx_phy(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); priv->last_phy_res_valid = true; memcpy(&priv->last_phy_res, pkt->u.raw, sizeof(struct iwl_rx_phy_res)); + return 0; } /* @@ -892,8 +914,9 @@ static int iwlagn_calc_rssi(struct iwl_priv *priv, /* Called for REPLY_RX (legacy ABG frames), or * REPLY_RX_MPDU_CMD (HT high-throughput N frames). */ -static void iwl_rx_reply_rx(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_reply_rx(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct ieee80211_hdr *header; struct ieee80211_rx_status rx_status; @@ -926,7 +949,7 @@ static void iwl_rx_reply_rx(struct iwl_priv *priv, } else { if (!priv->last_phy_res_valid) { IWL_ERR(priv, "MPDU frame without cached PHY data\n"); - return; + return 0; } phy_res = &priv->last_phy_res; amsdu = (struct iwl_rx_mpdu_res_start *)pkt->u.raw; @@ -940,14 +963,14 @@ static void iwl_rx_reply_rx(struct iwl_priv *priv, if ((unlikely(phy_res->cfg_phy_cnt > 20))) { IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d/n", phy_res->cfg_phy_cnt); - return; + return 0; } if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", le32_to_cpu(rx_pkt_status)); - return; + return 0; } /* This will be used in several places later */ @@ -1008,6 +1031,7 @@ static void iwl_rx_reply_rx(struct iwl_priv *priv, iwl_pass_packet_to_mac80211(priv, header, len, ampdu_status, rxb, &rx_status); + return 0; } /** @@ -1018,7 +1042,8 @@ static void iwl_rx_reply_rx(struct iwl_priv *priv, */ void iwl_setup_rx_handlers(struct iwl_priv *priv) { - void (**handlers)(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); + int (**handlers)(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd); handlers = priv->rx_handlers; @@ -1028,6 +1053,7 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv) handlers[PM_SLEEP_NOTIFICATION] = iwl_rx_pm_sleep_notif; handlers[PM_DEBUG_STATISTIC_NOTIFIC] = iwl_rx_pm_debug_statistics_notif; handlers[BEACON_NOTIFICATION] = iwl_rx_beacon_notif; + handlers[REPLY_ADD_STA] = iwl_add_sta_callback; /* * The same handler is used for both the REPLY to a discrete @@ -1065,9 +1091,11 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv) } -void iwl_rx_dispatch(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) +int iwl_rx_dispatch(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); + int err = 0; /* * Do the notification wait before RX handlers so @@ -1102,11 +1130,12 @@ void iwl_rx_dispatch(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) * rx_handlers table. See iwl_setup_rx_handlers() */ if (priv->rx_handlers[pkt->hdr.cmd]) { priv->rx_handlers_stats[pkt->hdr.cmd]++; - priv->rx_handlers[pkt->hdr.cmd] (priv, rxb); + err = priv->rx_handlers[pkt->hdr.cmd] (priv, rxb, cmd); } else { /* No handling needed */ IWL_DEBUG_RX(priv, "No handler needed for %s, 0x%02x\n", get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); } + return err; } diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index fc5af34..c5c95d5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -114,6 +114,65 @@ static void iwl_complete_scan(struct iwl_priv *priv, bool aborted) priv->scan_request = NULL; } +static void iwl_process_scan_complete(struct iwl_priv *priv) +{ + bool aborted; + + lockdep_assert_held(&priv->shrd->mutex); + + if (!test_and_clear_bit(STATUS_SCAN_COMPLETE, &priv->shrd->status)) + return; + + IWL_DEBUG_SCAN(priv, "Completed scan.\n"); + + cancel_delayed_work(&priv->scan_check); + + aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->shrd->status); + if (aborted) + IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n"); + + if (!test_and_clear_bit(STATUS_SCANNING, &priv->shrd->status)) { + IWL_DEBUG_SCAN(priv, "Scan already completed.\n"); + goto out_settings; + } + + if (priv->scan_type == IWL_SCAN_ROC) { + ieee80211_remain_on_channel_expired(priv->hw); + priv->hw_roc_channel = NULL; + schedule_delayed_work(&priv->hw_roc_disable_work, 10 * HZ); + } + + if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { + int err; + + /* Check if mac80211 requested scan during our internal scan */ + if (priv->scan_request == NULL) + goto out_complete; + + /* If so request a new scan */ + err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL, + priv->scan_request->channels[0]->band); + if (err) { + IWL_DEBUG_SCAN(priv, + "failed to initiate pending scan: %d\n", err); + aborted = true; + goto out_complete; + } + + return; + } + +out_complete: + iwl_complete_scan(priv, aborted); + +out_settings: + /* Can we still talk to firmware ? */ + if (!iwl_is_ready_rf(priv->shrd)) + return; + + iwlagn_post_scan(priv); +} + void iwl_force_scan_end(struct iwl_priv *priv) { lockdep_assert_held(&priv->shrd->mutex); @@ -127,6 +186,7 @@ void iwl_force_scan_end(struct iwl_priv *priv) clear_bit(STATUS_SCANNING, &priv->shrd->status); clear_bit(STATUS_SCAN_HW, &priv->shrd->status); clear_bit(STATUS_SCAN_ABORTING, &priv->shrd->status); + clear_bit(STATUS_SCAN_COMPLETE, &priv->shrd->status); iwl_complete_scan(priv, true); } @@ -169,7 +229,7 @@ int iwl_scan_cancel(struct iwl_priv *priv) * @ms: amount of time to wait (in milliseconds) for scan to abort * */ -int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) +void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) { unsigned long timeout = jiffies + msecs_to_jiffies(ms); @@ -181,16 +241,30 @@ int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) while (time_before_eq(jiffies, timeout)) { if (!test_bit(STATUS_SCAN_HW, &priv->shrd->status)) - break; + goto finished; msleep(20); } - return test_bit(STATUS_SCAN_HW, &priv->shrd->status); + return; + + finished: + /* + * Now STATUS_SCAN_HW is clear. This means that the + * device finished, but the background work is going + * to execute at best as soon as we release the mutex. + * Since we need to be able to issue a new scan right + * after this function returns, run the complete here. + * The STATUS_SCAN_COMPLETE bit will then be cleared + * and prevent the background work from "completing" + * a possible new scan. + */ + iwl_process_scan_complete(priv); } /* Service response to REPLY_SCAN_CMD (0x80) */ -static void iwl_rx_reply_scan(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_reply_scan(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { #ifdef CONFIG_IWLWIFI_DEBUG struct iwl_rx_packet *pkt = rxb_addr(rxb); @@ -199,11 +273,13 @@ static void iwl_rx_reply_scan(struct iwl_priv *priv, IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status); #endif + return 0; } /* Service SCAN_START_NOTIFICATION (0x82) */ -static void iwl_rx_scan_start_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_scan_start_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scanstart_notification *notif = @@ -218,13 +294,19 @@ static void iwl_rx_scan_start_notif(struct iwl_priv *priv, le32_to_cpu(notif->tsf_low), notif->status, notif->beacon_timer); - if (priv->scan_type == IWL_SCAN_ROC) + if (priv->scan_type == IWL_SCAN_ROC && + !priv->hw_roc_start_notified) { ieee80211_ready_on_channel(priv->hw); + priv->hw_roc_start_notified = true; + } + + return 0; } /* Service SCAN_RESULTS_NOTIFICATION (0x83) */ -static void iwl_rx_scan_results_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_scan_results_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { #ifdef CONFIG_IWLWIFI_DEBUG struct iwl_rx_packet *pkt = rxb_addr(rxb); @@ -233,20 +315,24 @@ static void iwl_rx_scan_results_notif(struct iwl_priv *priv, IWL_DEBUG_SCAN(priv, "Scan ch.res: " "%d [802.11%s] " + "probe status: %u:%u " "(TSF: 0x%08X:%08X) - %d " "elapsed=%lu usec\n", notif->channel, notif->band ? "bg" : "a", + notif->probe_status, notif->num_probe_not_sent, le32_to_cpu(notif->tsf_high), le32_to_cpu(notif->tsf_low), le32_to_cpu(notif->statistics[0]), le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf); #endif + return 0; } /* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ -static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, - struct iwl_rx_mem_buffer *rxb) +static int iwl_rx_scan_complete_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw; @@ -256,13 +342,20 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, scan_notif->tsf_low, scan_notif->tsf_high, scan_notif->status); - /* The HW is no longer scanning */ - clear_bit(STATUS_SCAN_HW, &priv->shrd->status); - IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", jiffies_to_msecs(jiffies - priv->scan_start)); + /* + * When aborting, we run the scan completed background work inline + * and the background work must then do nothing. The SCAN_COMPLETE + * bit helps implement that logic and thus needs to be set before + * queueing the work. Also, since the scan abort waits for SCAN_HW + * to clear, we need to set SCAN_COMPLETE before clearing SCAN_HW + * to avoid a race there. + */ + set_bit(STATUS_SCAN_COMPLETE, &priv->shrd->status); + clear_bit(STATUS_SCAN_HW, &priv->shrd->status); queue_work(priv->shrd->workqueue, &priv->scan_completed); if (priv->iw_mode != NL80211_IFTYPE_ADHOC && @@ -286,6 +379,7 @@ static void iwl_rx_scan_complete_notif(struct iwl_priv *priv, queue_work(priv->shrd->workqueue, &priv->bt_traffic_change_work); } + return 0; } void iwl_setup_rx_scan_handlers(struct iwl_priv *priv) @@ -299,9 +393,8 @@ void iwl_setup_rx_scan_handlers(struct iwl_priv *priv) iwl_rx_scan_complete_notif; } -inline u16 iwl_get_active_dwell_time(struct iwl_priv *priv, - enum ieee80211_band band, - u8 n_probes) +static u16 iwl_get_active_dwell_time(struct iwl_priv *priv, + enum ieee80211_band band, u8 n_probes) { if (band == IEEE80211_BAND_5GHZ) return IWL_ACTIVE_DWELL_TIME_52 + @@ -311,35 +404,481 @@ inline u16 iwl_get_active_dwell_time(struct iwl_priv *priv, IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); } -u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, - enum ieee80211_band band, - struct ieee80211_vif *vif) +static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) { struct iwl_rxon_context *ctx; + + /* + * If we're associated, we clamp the dwell time 98% + * of the smallest beacon interval (minus 2 * channel + * tune time) + */ + for_each_context(priv, ctx) { + u16 value; + + if (!iwl_is_associated_ctx(ctx)) + continue; + value = ctx->beacon_int; + if (!value) + value = IWL_PASSIVE_DWELL_BASE; + value = (value * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; + dwell_time = min(value, dwell_time); + } + + return dwell_time; +} + +static u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, + enum ieee80211_band band) +{ u16 passive = (band == IEEE80211_BAND_2GHZ) ? IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 : IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52; - if (iwl_is_any_associated(priv)) { + return iwl_limit_dwell(priv, passive); +} + +static int iwl_get_single_channel_for_scan(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum ieee80211_band band, + struct iwl_scan_channel *scan_ch) +{ + const struct ieee80211_supported_band *sband; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added = 0; + u16 channel = 0; + + sband = iwl_get_hw_mode(priv, band); + if (!sband) { + IWL_ERR(priv, "invalid band\n"); + return added; + } + + active_dwell = iwl_get_active_dwell_time(priv, band, 0); + passive_dwell = iwl_get_passive_dwell_time(priv, band); + + if (passive_dwell <= active_dwell) + passive_dwell = active_dwell + 1; + + channel = iwl_get_single_channel_number(priv, band); + if (channel) { + scan_ch->channel = cpu_to_le16(channel); + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + scan_ch->active_dwell = cpu_to_le16(active_dwell); + scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + added++; + } else + IWL_ERR(priv, "no valid channel found\n"); + return added; +} + +static int iwl_get_channels_for_scan(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum ieee80211_band band, + u8 is_active, u8 n_probes, + struct iwl_scan_channel *scan_ch) +{ + struct ieee80211_channel *chan; + const struct ieee80211_supported_band *sband; + const struct iwl_channel_info *ch_info; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added, i; + u16 channel; + + sband = iwl_get_hw_mode(priv, band); + if (!sband) + return 0; + + active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); + passive_dwell = iwl_get_passive_dwell_time(priv, band); + + if (passive_dwell <= active_dwell) + passive_dwell = active_dwell + 1; + + for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { + chan = priv->scan_request->channels[i]; + + if (chan->band != band) + continue; + + channel = chan->hw_value; + scan_ch->channel = cpu_to_le16(channel); + + ch_info = iwl_get_channel_info(priv, band, channel); + if (!is_channel_valid(ch_info)) { + IWL_DEBUG_SCAN(priv, + "Channel %d is INVALID for this band.\n", + channel); + continue; + } + + if (!is_active || is_channel_passive(ch_info) || + (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + else + scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; + + if (n_probes) + scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes); + + scan_ch->active_dwell = cpu_to_le16(active_dwell); + scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + + /* NOTE: if we were doing 6Mb OFDM for scans we'd use + * power level: + * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; + */ + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + + IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n", + channel, le32_to_cpu(scan_ch->type), + (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? + "ACTIVE" : "PASSIVE", + (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? + active_dwell : passive_dwell); + + scan_ch++; + added++; + } + + IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added); + return added; +} + +static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_SCAN_CMD, + .len = { sizeof(struct iwl_scan_cmd), }, + .flags = CMD_SYNC, + }; + struct iwl_scan_cmd *scan; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + u32 rate_flags = 0; + u16 cmd_len; + u16 rx_chain = 0; + enum ieee80211_band band; + u8 n_probes = 0; + u8 rx_ant = hw_params(priv).valid_rx_ant; + u8 rate; + bool is_active = false; + int chan_mod; + u8 active_chains; + u8 scan_tx_antennas = hw_params(priv).valid_tx_ant; + int ret; + + lockdep_assert_held(&priv->shrd->mutex); + + if (vif) + ctx = iwl_rxon_ctx_from_vif(vif); + + if (!priv->scan_cmd) { + priv->scan_cmd = kmalloc(sizeof(struct iwl_scan_cmd) + + IWL_MAX_SCAN_SIZE, GFP_KERNEL); + if (!priv->scan_cmd) { + IWL_DEBUG_SCAN(priv, + "fail to allocate memory for scan\n"); + return -ENOMEM; + } + } + scan = priv->scan_cmd; + memset(scan, 0, sizeof(struct iwl_scan_cmd) + IWL_MAX_SCAN_SIZE); + + scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; + scan->quiet_time = IWL_ACTIVE_QUIET_TIME; + + if (priv->scan_type != IWL_SCAN_ROC && + iwl_is_any_associated(priv)) { + u16 interval = 0; + u32 extra; + u32 suspend_time = 100; + u32 scan_suspend_time = 100; + + IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); + switch (priv->scan_type) { + case IWL_SCAN_ROC: + WARN_ON(1); + break; + case IWL_SCAN_RADIO_RESET: + interval = 0; + break; + case IWL_SCAN_NORMAL: + interval = vif->bss_conf.beacon_int; + break; + } + + scan->suspend_time = 0; + scan->max_out_time = cpu_to_le32(200 * 1024); + if (!interval) + interval = suspend_time; + + extra = (suspend_time / interval) << 22; + scan_suspend_time = (extra | + ((suspend_time % interval) * 1024)); + scan->suspend_time = cpu_to_le32(scan_suspend_time); + IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", + scan_suspend_time, interval); + } else if (priv->scan_type == IWL_SCAN_ROC) { + scan->suspend_time = 0; + scan->max_out_time = 0; + scan->quiet_time = 0; + scan->quiet_plcp_th = 0; + } + + switch (priv->scan_type) { + case IWL_SCAN_RADIO_RESET: + IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); + break; + case IWL_SCAN_NORMAL: + if (priv->scan_request->n_ssids) { + int i, p = 0; + IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); + for (i = 0; i < priv->scan_request->n_ssids; i++) { + /* always does wildcard anyway */ + if (!priv->scan_request->ssids[i].ssid_len) + continue; + scan->direct_scan[p].id = WLAN_EID_SSID; + scan->direct_scan[p].len = + priv->scan_request->ssids[i].ssid_len; + memcpy(scan->direct_scan[p].ssid, + priv->scan_request->ssids[i].ssid, + priv->scan_request->ssids[i].ssid_len); + n_probes++; + p++; + } + is_active = true; + } else + IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); + break; + case IWL_SCAN_ROC: + IWL_DEBUG_SCAN(priv, "Start ROC scan.\n"); + break; + } + + scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; + scan->tx_cmd.sta_id = ctx->bcast_sta_id; + scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + switch (priv->scan_band) { + case IEEE80211_BAND_2GHZ: + scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; + chan_mod = le32_to_cpu( + priv->contexts[IWL_RXON_CTX_BSS].active.flags & + RXON_FLG_CHANNEL_MODE_MSK) + >> RXON_FLG_CHANNEL_MODE_POS; + if (chan_mod == CHANNEL_MODE_PURE_40) { + rate = IWL_RATE_6M_PLCP; + } else { + rate = IWL_RATE_1M_PLCP; + rate_flags = RATE_MCS_CCK_MSK; + } /* - * If we're associated, we clamp the maximum passive - * dwell time to be 98% of the smallest beacon interval - * (minus 2 * channel tune time) + * Internal scans are passive, so we can indiscriminately set + * the BT ignore flag on 2.4 GHz since it applies to TX only. */ - for_each_context(priv, ctx) { - u16 value; - - if (!iwl_is_associated_ctx(ctx)) - continue; - value = ctx->vif ? ctx->vif->bss_conf.beacon_int : 0; - if ((value > IWL_PASSIVE_DWELL_BASE) || !value) - value = IWL_PASSIVE_DWELL_BASE; - value = (value * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; - passive = min(value, passive); + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist) + scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; + break; + case IEEE80211_BAND_5GHZ: + rate = IWL_RATE_6M_PLCP; + break; + default: + IWL_WARN(priv, "Invalid scan band\n"); + return -EIO; + } + + /* + * If active scanning is requested but a certain channel is + * marked passive, we can do active scanning if we detect + * transmissions. + * + * There is an issue with some firmware versions that triggers + * a sysassert on a "good CRC threshold" of zero (== disabled), + * on a radar channel even though this means that we should NOT + * send probes. + * + * The "good CRC threshold" is the number of frames that we + * need to receive during our dwell time on a channel before + * sending out probes -- setting this to a huge value will + * mean we never reach it, but at the same time work around + * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER + * here instead of IWL_GOOD_CRC_TH_DISABLED. + * + * This was fixed in later versions along with some other + * scan changes, and the threshold behaves as a flag in those + * versions. + */ + if (priv->new_scan_threshold_behaviour) + scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : + IWL_GOOD_CRC_TH_DISABLED; + else + scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : + IWL_GOOD_CRC_TH_NEVER; + + band = priv->scan_band; + + if (priv->cfg->scan_rx_antennas[band]) + rx_ant = priv->cfg->scan_rx_antennas[band]; + + if (band == IEEE80211_BAND_2GHZ && + priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist) { + /* transmit 2.4 GHz probes only on first antenna */ + scan_tx_antennas = first_antenna(scan_tx_antennas); + } + + priv->scan_tx_ant[band] = iwl_toggle_tx_ant(priv, + priv->scan_tx_ant[band], + scan_tx_antennas); + rate_flags |= iwl_ant_idx_to_flags(priv->scan_tx_ant[band]); + scan->tx_cmd.rate_n_flags = iwl_hw_set_rate_n_flags(rate, rate_flags); + + /* In power save mode use one chain, otherwise use all chains */ + if (test_bit(STATUS_POWER_PMI, &priv->shrd->status)) { + /* rx_ant has been set to all valid chains previously */ + active_chains = rx_ant & + ((u8)(priv->chain_noise_data.active_chains)); + if (!active_chains) + active_chains = rx_ant; + + IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n", + priv->chain_noise_data.active_chains); + + rx_ant = first_antenna(active_chains); + } + if (priv->cfg->bt_params && + priv->cfg->bt_params->advanced_bt_coexist && + priv->bt_full_concurrent) { + /* operated as 1x1 in full concurrency mode */ + rx_ant = first_antenna(rx_ant); + } + + /* MIMO is not used here, but value is required */ + rx_chain |= + hw_params(priv).valid_rx_ant << RXON_RX_CHAIN_VALID_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; + rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; + scan->rx_chain = cpu_to_le16(rx_chain); + switch (priv->scan_type) { + case IWL_SCAN_NORMAL: + cmd_len = iwl_fill_probe_req(priv, + (struct ieee80211_mgmt *)scan->data, + vif->addr, + priv->scan_request->ie, + priv->scan_request->ie_len, + IWL_MAX_SCAN_SIZE - sizeof(*scan)); + break; + case IWL_SCAN_RADIO_RESET: + case IWL_SCAN_ROC: + /* use bcast addr, will not be transmitted but must be valid */ + cmd_len = iwl_fill_probe_req(priv, + (struct ieee80211_mgmt *)scan->data, + iwl_bcast_addr, NULL, 0, + IWL_MAX_SCAN_SIZE - sizeof(*scan)); + break; + default: + BUG(); + } + scan->tx_cmd.len = cpu_to_le16(cmd_len); + + scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | + RXON_FILTER_BCON_AWARE_MSK); + + switch (priv->scan_type) { + case IWL_SCAN_RADIO_RESET: + scan->channel_count = + iwl_get_single_channel_for_scan(priv, vif, band, + (void *)&scan->data[cmd_len]); + break; + case IWL_SCAN_NORMAL: + scan->channel_count = + iwl_get_channels_for_scan(priv, vif, band, + is_active, n_probes, + (void *)&scan->data[cmd_len]); + break; + case IWL_SCAN_ROC: { + struct iwl_scan_channel *scan_ch; + int n_chan, i; + u16 dwell; + + dwell = iwl_limit_dwell(priv, priv->hw_roc_duration); + n_chan = DIV_ROUND_UP(priv->hw_roc_duration, dwell); + + scan->channel_count = n_chan; + + scan_ch = (void *)&scan->data[cmd_len]; + + for (i = 0; i < n_chan; i++) { + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + scan_ch->channel = + cpu_to_le16(priv->hw_roc_channel->hw_value); + + if (i == n_chan - 1) + dwell = priv->hw_roc_duration - i * dwell; + + scan_ch->active_dwell = + scan_ch->passive_dwell = cpu_to_le16(dwell); + + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + + /* NOTE: if we were doing 6Mb OFDM for scans we'd use + * power level: + * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; + */ + if (priv->hw_roc_channel->band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + + scan_ch++; + } } + + break; + } + + if (scan->channel_count == 0) { + IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); + return -EIO; + } + + cmd.len[0] += le16_to_cpu(scan->tx_cmd.len) + + scan->channel_count * sizeof(struct iwl_scan_channel); + cmd.data[0] = scan; + cmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + scan->len = cpu_to_le16(cmd.len[0]); + + /* set scan bit here for PAN params */ + set_bit(STATUS_SCAN_HW, &priv->shrd->status); + + ret = iwlagn_set_pan_params(priv); + if (ret) + return ret; + + ret = iwl_trans_send_cmd(trans(priv), &cmd); + if (ret) { + clear_bit(STATUS_SCAN_HW, &priv->shrd->status); + iwlagn_set_pan_params(priv); } - return passive; + return ret; } void iwl_init_scan_params(struct iwl_priv *priv) @@ -415,31 +954,33 @@ int iwl_mac_hw_scan(struct ieee80211_hw *hw, mutex_lock(&priv->shrd->mutex); - if (test_bit(STATUS_SCANNING, &priv->shrd->status) && - priv->scan_type != IWL_SCAN_NORMAL) { - IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); - ret = -EAGAIN; - goto out_unlock; - } - - /* mac80211 will only ask for one band at a time */ - priv->scan_request = req; - priv->scan_vif = vif; - /* * If an internal scan is in progress, just set * up the scan_request as per above. */ if (priv->scan_type != IWL_SCAN_NORMAL) { - IWL_DEBUG_SCAN(priv, "SCAN request during internal scan\n"); + IWL_DEBUG_SCAN(priv, + "SCAN request during internal scan - defer\n"); + priv->scan_request = req; + priv->scan_vif = vif; ret = 0; - } else + } else { + priv->scan_request = req; + priv->scan_vif = vif; + /* + * mac80211 will only ask for one band at a time + * so using channels[0] here is ok + */ ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL, req->channels[0]->band); + if (ret) { + priv->scan_request = NULL; + priv->scan_vif = NULL; + } + } IWL_DEBUG_MAC80211(priv, "leave\n"); -out_unlock: mutex_unlock(&priv->shrd->mutex); return ret; @@ -557,61 +1098,10 @@ static void iwl_bg_abort_scan(struct work_struct *work) static void iwl_bg_scan_completed(struct work_struct *work) { struct iwl_priv *priv = - container_of(work, struct iwl_priv, scan_completed); - bool aborted; - - IWL_DEBUG_SCAN(priv, "Completed scan.\n"); - - cancel_delayed_work(&priv->scan_check); + container_of(work, struct iwl_priv, scan_completed); mutex_lock(&priv->shrd->mutex); - - aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->shrd->status); - if (aborted) - IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n"); - - if (!test_and_clear_bit(STATUS_SCANNING, &priv->shrd->status)) { - IWL_DEBUG_SCAN(priv, "Scan already completed.\n"); - goto out_settings; - } - - if (priv->scan_type == IWL_SCAN_ROC) { - ieee80211_remain_on_channel_expired(priv->hw); - priv->hw_roc_channel = NULL; - schedule_delayed_work(&priv->hw_roc_disable_work, 10 * HZ); - } - - if (priv->scan_type != IWL_SCAN_NORMAL && !aborted) { - int err; - - /* Check if mac80211 requested scan during our internal scan */ - if (priv->scan_request == NULL) - goto out_complete; - - /* If so request a new scan */ - err = iwl_scan_initiate(priv, priv->scan_vif, IWL_SCAN_NORMAL, - priv->scan_request->channels[0]->band); - if (err) { - IWL_DEBUG_SCAN(priv, - "failed to initiate pending scan: %d\n", err); - aborted = true; - goto out_complete; - } - - goto out; - } - -out_complete: - iwl_complete_scan(priv, aborted); - -out_settings: - /* Can we still talk to firmware ? */ - if (!iwl_is_ready_rf(priv->shrd)) - goto out; - - iwlagn_post_scan(priv); - -out: + iwl_process_scan_complete(priv); mutex_unlock(&priv->shrd->mutex); } diff --git a/drivers/net/wireless/iwlwifi/iwl-shared.h b/drivers/net/wireless/iwlwifi/iwl-shared.h index d987bee..8747bbd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-shared.h +++ b/drivers/net/wireless/iwlwifi/iwl-shared.h @@ -72,8 +72,27 @@ #include "iwl-commands.h" -/*This files includes all the types / functions that are exported by the - * upper layer to the bus and transport layer */ +/** + * DOC: shared area - role and goal + * + * The shared area contains all the data exported by the upper layer to the + * other layers. Since the bus and transport layer shouldn't dereference + * iwl_priv, all the data needed by the upper layer and the transport / bus + * layer must be here. + * The shared area also holds pointer to all the other layers. This allows a + * layer to call a function from another layer. + * + * NOTE: All the layers hold a pointer to the shared area which must be shrd. + * A few macros assume that (_m)->shrd points to the shared area no matter + * what _m is. + * + * gets notifications about enumeration, suspend, resume. + * For the moment, the bus layer is not a linux kernel module as itself, and + * the module_init function of the driver must call the bus specific + * registration functions. These functions are listed at the end of this file. + * For the moment, there is only one implementation of this interface: PCI-e. + * This implementation is iwl-pci.c + */ struct iwl_cfg; struct iwl_bus; @@ -90,6 +109,9 @@ extern struct iwl_mod_params iwlagn_mod_params; /** * struct iwl_mod_params + * + * Holds the module parameters + * * @sw_crypto: using hardware encryption, default = 0 * @num_of_queues: number of tx queue, HW dependent * @disable_11n: 11n capabilities enabled, default = 0 @@ -134,20 +156,24 @@ struct iwl_mod_params { /** * struct iwl_hw_params + * + * Holds the module parameters + * * @max_txq_num: Max # Tx queues supported * @num_ampdu_queues: num of ampdu queues - * @tx/rx_chains_num: Number of TX/RX chains - * @valid_tx/rx_ant: usable antennas - * @max_stations: - * @ht40_channel: is 40MHz width possible in band 2.4 - * @beacon_time_tsf_bits: number of valid tsf bits for beacon time - * @sku: + * @tx_chains_num: Number of TX chains + * @rx_chains_num: Number of RX chains + * @valid_tx_ant: usable antennas for TX + * @valid_rx_ant: usable antennas for RX + * @max_stations: the maximal number of stations + * @ht40_channel: is 40MHz width possible: BIT(IEEE80211_BAND_XXX) + * @sku: sku read from EEPROM * @rx_page_order: Rx buffer page order - * @rx_wrt_ptr_reg: FH{39}_RSCSR_CHNL0_WPTR - * BIT(IEEE80211_BAND_5GHZ) BIT(IEEE80211_BAND_5GHZ) - * @sw_crypto: 0 for hw, 1 for sw - * @max_xxx_size: for ucode uses - * @ct_kill_threshold: temperature threshold + * @max_inst_size: for ucode use + * @max_data_size: for ucode use + * @ct_kill_threshold: temperature threshold - in hw dependent unit + * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit + * relevant for 1000, 6000 and up * @wd_timeout: TX queues watchdog timeout * @calib_init_cfg: setup initial calibrations for the hw * @calib_rt_cfg: setup runtime calibrations for the hw @@ -163,14 +189,12 @@ struct iwl_hw_params { u8 max_stations; u8 ht40_channel; bool shadow_reg_enable; - u16 beacon_time_tsf_bits; u16 sku; u32 rx_page_order; u32 max_inst_size; u32 max_data_size; - u32 ct_kill_threshold; /* value in hw-dependent units */ - u32 ct_kill_exit_threshold; /* value in hw-dependent units */ - /* for 1000, 6000 series and up */ + u32 ct_kill_threshold; + u32 ct_kill_exit_threshold; unsigned int wd_timeout; u32 calib_init_cfg; @@ -179,28 +203,59 @@ struct iwl_hw_params { }; /** - * struct iwl_ht_agg - aggregation status while waiting for block-ack - * @txq_id: Tx queue used for Tx attempt - * @wait_for_ba: Expect block-ack before next Tx reply - * @rate_n_flags: Rate at which Tx was attempted + * enum iwl_agg_state * - * If REPLY_TX indicates that aggregation was attempted, driver must wait - * for block ack (REPLY_COMPRESSED_BA). This struct stores tx reply info - * until block ack arrives. + * The state machine of the BA agreement establishment / tear down. + * These states relate to a specific RA / TID. + * + * @IWL_AGG_OFF: aggregation is not used + * @IWL_AGG_ON: aggregation session is up + * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the + * HW queue to be empty from packets for this RA /TID. + * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the + * HW queue to be empty from packets for this RA /TID. + */ +enum iwl_agg_state { + IWL_AGG_OFF = 0, + IWL_AGG_ON, + IWL_EMPTYING_HW_QUEUE_ADDBA, + IWL_EMPTYING_HW_QUEUE_DELBA, +}; + +/** + * struct iwl_ht_agg - aggregation state machine + + * This structs holds the states for the BA agreement establishment and tear + * down. It also holds the state during the BA session itself. This struct is + * duplicated for each RA / TID. + + * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the + * Tx response (REPLY_TX), and the block ack notification + * (REPLY_COMPRESSED_BA). + * @state: state of the BA agreement establishment / tear down. + * @txq_id: Tx queue used by the BA session - used by the transport layer. + * Needed by the upper layer for debugfs only. + * @wait_for_ba: Expect block-ack before next Tx reply */ struct iwl_ht_agg { - u16 txq_id; - u16 wait_for_ba; u32 rate_n_flags; -#define IWL_AGG_OFF 0 -#define IWL_AGG_ON 1 -#define IWL_EMPTYING_HW_QUEUE_ADDBA 2 -#define IWL_EMPTYING_HW_QUEUE_DELBA 3 - u8 state; + enum iwl_agg_state state; + u16 txq_id; + bool wait_for_ba; }; +/** + * struct iwl_tid_data - one for each RA / TID + + * This structs holds the states for each RA / TID. + + * @seq_number: the next WiFi sequence number to use + * @tfds_in_queue: number of packets sent to the HW queues. + * Exported for debugfs only + * @agg: aggregation state machine + */ struct iwl_tid_data { - u16 seq_number; /* agn only */ + u16 seq_number; u16 tfds_in_queue; struct iwl_ht_agg agg; }; @@ -246,12 +301,6 @@ struct iwl_shared { spinlock_t sta_lock; struct mutex mutex; - /*these 2 shouldn't really be here, but they are needed for - * iwl_queue_stop, which is called from the upper layer too - */ - u8 mac80211_registered; - struct ieee80211_hw *hw; - struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT]; wait_queue_head_t wait_command_queue; @@ -371,16 +420,14 @@ enum iwl_rxon_context_id { NUM_IWL_RXON_CTX }; -#ifdef CONFIG_PM -int iwl_suspend(struct iwl_priv *priv); -int iwl_resume(struct iwl_priv *priv); -#endif /* !CONFIG_PM */ - int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops, struct iwl_cfg *cfg); void __devexit iwl_remove(struct iwl_priv * priv); +struct iwl_device_cmd; +int __must_check iwl_rx_dispatch(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd); -void iwl_rx_dispatch(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); int iwlagn_hw_valid_rtc_data_addr(u32 addr); void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv, enum iwl_rxon_context_id ctx, @@ -390,20 +437,26 @@ void iwl_stop_tx_ba_trans_ready(struct iwl_priv *priv, u8 sta_id, u8 tid); void iwl_set_hw_rfkill_state(struct iwl_priv *priv, bool state); void iwl_nic_config(struct iwl_priv *priv); +void iwl_free_skb(struct iwl_priv *priv, struct sk_buff *skb); void iwl_apm_stop(struct iwl_priv *priv); int iwl_apm_init(struct iwl_priv *priv); void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand); const char *get_cmd_string(u8 cmd); bool iwl_check_for_ct_kill(struct iwl_priv *priv); +void iwl_stop_sw_queue(struct iwl_priv *priv, u8 ac); +void iwl_wake_sw_queue(struct iwl_priv *priv, u8 ac); + #ifdef CONFIG_IWLWIFI_DEBUGFS void iwl_reset_traffic_log(struct iwl_priv *priv); #endif /* CONFIG_IWLWIFI_DEBUGFS */ #ifdef CONFIG_IWLWIFI_DEBUG -void iwl_print_rx_config_cmd(struct iwl_priv *priv, u8 ctxid); +void iwl_print_rx_config_cmd(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid); #else -static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv, u8 ctxid) +static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid) { } #endif @@ -436,6 +489,7 @@ static inline void iwl_print_rx_config_cmd(struct iwl_priv *priv, u8 ctxid) #define STATUS_FW_ERROR 17 #define STATUS_DEVICE_ENABLED 18 #define STATUS_CHANNEL_SWITCH_PENDING 19 +#define STATUS_SCAN_COMPLETE 20 static inline int iwl_is_ready(struct iwl_shared *shrd) { diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index e24135e..580a4d7 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -59,8 +59,7 @@ static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) static int iwl_process_add_sta_resp(struct iwl_priv *priv, struct iwl_addsta_cmd *addsta, - struct iwl_rx_packet *pkt, - bool sync) + struct iwl_rx_packet *pkt) { u8 sta_id = addsta->sta.sta_id; unsigned long flags; @@ -123,15 +122,14 @@ static int iwl_process_add_sta_resp(struct iwl_priv *priv, return ret; } -static void iwl_add_sta_callback(struct iwl_shared *shrd, - struct iwl_device_cmd *cmd, - struct iwl_rx_packet *pkt) +int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) { + struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_addsta_cmd *addsta = - (struct iwl_addsta_cmd *)cmd->cmd.payload; - - iwl_process_add_sta_resp(shrd->priv, addsta, pkt, false); + (struct iwl_addsta_cmd *) cmd->payload; + return iwl_process_add_sta_resp(priv, addsta, pkt); } static u16 iwlagn_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) @@ -147,7 +145,6 @@ static u16 iwlagn_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) int iwl_send_add_sta(struct iwl_priv *priv, struct iwl_addsta_cmd *sta, u8 flags) { - struct iwl_rx_packet *pkt = NULL; int ret = 0; u8 data[sizeof(*sta)]; struct iwl_host_cmd cmd = { @@ -160,9 +157,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); - if (flags & CMD_ASYNC) - cmd.callback = iwl_add_sta_callback; - else { + if (!(flags & CMD_ASYNC)) { cmd.flags |= CMD_WANT_SKB; might_sleep(); } @@ -172,14 +167,16 @@ int iwl_send_add_sta(struct iwl_priv *priv, if (ret || (flags & CMD_ASYNC)) return ret; + /*else the command was successfully sent in SYNC mode, need to free + * the reply page */ - if (ret == 0) { - pkt = (struct iwl_rx_packet *)cmd.reply_page; - ret = iwl_process_add_sta_resp(priv, sta, pkt, true); - } iwl_free_pages(priv->shrd, cmd.reply_page); - return ret; + if (cmd.handler_status) + IWL_ERR(priv, "%s - error in the CMD response %d", __func__, + cmd.handler_status); + + return cmd.handler_status; } static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, @@ -305,7 +302,7 @@ u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, station->ctxid = ctx->ctxid; if (sta) { - struct iwl_station_priv_common *sta_priv; + struct iwl_station_priv *sta_priv; sta_priv = (void *)sta->drv_priv; sta_priv->ctx = ctx; @@ -821,7 +818,7 @@ int iwl_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct iwl_priv *priv = hw->priv; - struct iwl_station_priv_common *sta_common = (void *)sta->drv_priv; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; int ret; IWL_DEBUG_INFO(priv, "received request to remove station %pM\n", @@ -829,7 +826,7 @@ int iwl_mac_sta_remove(struct ieee80211_hw *hw, mutex_lock(&priv->shrd->mutex); IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", sta->addr); - ret = iwl_remove_station(priv, sta_common->sta_id, sta->addr); + ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr); if (ret) IWL_ERR(priv, "Error removing station %pM\n", sta->addr); diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.h b/drivers/net/wireless/iwlwifi/iwl-sta.h index 9641eb6..1bca0da 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.h +++ b/drivers/net/wireless/iwlwifi/iwl-sta.h @@ -61,6 +61,9 @@ u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct iwl_link_quality_cmd *lq, u8 flags, bool init); void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx); +int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd); + /** * iwl_clear_driver_stations - clear knowledge of all stations from driver @@ -102,7 +105,7 @@ static inline int iwl_sta_id(struct ieee80211_sta *sta) if (WARN_ON(!sta)) return IWL_INVALID_STATION; - return ((struct iwl_station_priv_common *)sta->drv_priv)->sta_id; + return ((struct iwl_station_priv *)sta->drv_priv)->sta_id; } /** diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h index 8047e95..2b6756e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h @@ -118,16 +118,6 @@ struct iwl_dma_ptr { struct iwl_cmd_meta { /* only for SYNC commands, iff the reply skb is wanted */ struct iwl_host_cmd *source; - /* - * only for ASYNC commands - * (which is somewhat stupid -- look at iwl-sta.c for instance - * which duplicates a bunch of code because the callback isn't - * invoked for SYNC commands, if it were and its result passed - * through it would be simpler...) - */ - void (*callback)(struct iwl_shared *shrd, - struct iwl_device_cmd *cmd, - struct iwl_rx_packet *pkt); u32 flags; @@ -285,10 +275,8 @@ int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, dma_addr_t addr, u16 len, u8 reset); int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id); int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); -int __must_check iwl_trans_pcie_send_cmd_pdu(struct iwl_trans *trans, u8 id, - u32 flags, u16 len, const void *data); void iwl_tx_cmd_complete(struct iwl_trans *trans, - struct iwl_rx_mem_buffer *rxb); + struct iwl_rx_mem_buffer *rxb, int handler_status); void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, struct iwl_tx_queue *txq, u16 byte_cnt); @@ -307,7 +295,7 @@ void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, enum iwl_rxon_context_id ctx, int sta_id, int tid, int frame_limit); void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, - int index); + int index, enum dma_data_direction dma_dir); int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, struct sk_buff_head *skbs); int iwl_queue_space(const struct iwl_queue *q); @@ -375,12 +363,9 @@ static inline void iwl_wake_queue(struct iwl_trans *trans, struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (unlikely(!trans->shrd->mac80211_registered)) - return; - if (test_and_clear_bit(hwq, trans_pcie->queue_stopped)) if (atomic_dec_return(&trans_pcie->queue_stop_count[ac]) <= 0) - ieee80211_wake_queue(trans->shrd->hw, ac); + iwl_wake_sw_queue(priv(trans), ac); } static inline void iwl_stop_queue(struct iwl_trans *trans, @@ -392,12 +377,9 @@ static inline void iwl_stop_queue(struct iwl_trans *trans, struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (unlikely(!trans->shrd->mac80211_registered)) - return; - if (!test_and_set_bit(hwq, trans_pcie->queue_stopped)) if (atomic_inc_return(&trans_pcie->queue_stop_count[ac]) > 0) - ieee80211_stop_queue(trans->shrd->hw, ac); + iwl_stop_sw_queue(priv(trans), ac); } #ifdef ieee80211_stop_queue diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c index 126e5a4..b4eff55 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-rx-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c @@ -34,7 +34,7 @@ #include "iwl-core.h" #include "iwl-io.h" #include "iwl-helpers.h" -#include "iwl-trans-int-pcie.h" +#include "iwl-trans-pcie-int.h" /****************************************************************************** * @@ -372,12 +372,15 @@ static void iwl_rx_handle(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_rx_queue *rxq = &trans_pcie->rxq; + struct iwl_tx_queue *txq = &trans_pcie->txq[trans->shrd->cmd_queue]; + struct iwl_device_cmd *cmd; u32 r, i; int reclaim; unsigned long flags; u8 fill_rx = 0; u32 count = 8; int total_empty; + int index, cmd_index; /* uCode's read index (stored in shared DRAM) indicates the last Rx * buffer that the driver may process (last buffer filled by ucode). */ @@ -397,7 +400,8 @@ static void iwl_rx_handle(struct iwl_trans *trans) fill_rx = 1; while (i != r) { - int len; + int len, err; + u16 sequence; rxb = rxq->queue[i]; @@ -437,7 +441,27 @@ static void iwl_rx_handle(struct iwl_trans *trans) (pkt->hdr.cmd != STATISTICS_NOTIFICATION) && (pkt->hdr.cmd != REPLY_TX); - iwl_rx_dispatch(priv(trans), rxb); + sequence = le16_to_cpu(pkt->hdr.sequence); + index = SEQ_TO_INDEX(sequence); + cmd_index = get_cmd_index(&txq->q, index); + + if (reclaim) + cmd = txq->cmd[cmd_index]; + else + cmd = NULL; + + /* warn if this is cmd response / notification and the uCode + * didn't set the SEQ_RX_FRAME for a frame that is + * uCode-originated + * If you saw this code after the second half of 2012, then + * please remove it + */ + WARN(pkt->hdr.cmd != REPLY_TX && reclaim == false && + (!(pkt->hdr.sequence & SEQ_RX_FRAME)), + "reclaim is false, SEQ_RX_FRAME unset: %s\n", + get_cmd_string(pkt->hdr.cmd)); + + err = iwl_rx_dispatch(priv(trans), rxb, cmd); /* * XXX: After here, we should always check rxb->page @@ -452,7 +476,7 @@ static void iwl_rx_handle(struct iwl_trans *trans) * iwl_trans_send_cmd() * as we reclaim the driver command queue */ if (rxb->page) - iwl_tx_cmd_complete(trans, rxb); + iwl_tx_cmd_complete(trans, rxb, err); else IWL_WARN(trans, "Claim null rxb?\n"); } @@ -645,7 +669,7 @@ static void iwl_irq_handle_error(struct iwl_trans *trans) */ clear_bit(STATUS_READY, &trans->shrd->status); clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status); - wake_up_interruptible(&priv->shrd->wait_command_queue); + wake_up(&priv->shrd->wait_command_queue); IWL_ERR(trans, "RF is used by WiMAX\n"); return; } @@ -1086,7 +1110,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) handled |= CSR_INT_BIT_FH_TX; /* Wake up uCode load routine, now that load is complete */ priv(trans)->ucode_write_complete = 1; - wake_up_interruptible(&trans->shrd->wait_command_queue); + wake_up(&trans->shrd->wait_command_queue); } if (inta & ~handled) { diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c index f6d823f..ee7059d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c @@ -38,7 +38,7 @@ #include "iwl-io.h" #include "iwl-agn-hw.h" #include "iwl-helpers.h" -#include "iwl-trans-int-pcie.h" +#include "iwl-trans-pcie-int.h" #define IWL_TX_CRC_SIZE 4 #define IWL_TX_DELIMITER_SIZE 4 @@ -59,13 +59,15 @@ void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, u8 sta_id = 0; u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE; __le16 bc_ent; + struct iwl_tx_cmd *tx_cmd = + (struct iwl_tx_cmd *) txq->cmd[txq->q.write_ptr]->payload; scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX); - sta_id = txq->cmd[txq->q.write_ptr]->cmd.tx.sta_id; - sec_ctl = txq->cmd[txq->q.write_ptr]->cmd.tx.sec_ctl; + sta_id = tx_cmd->sta_id; + sec_ctl = tx_cmd->sec_ctl; switch (sec_ctl & TX_CMD_SEC_MSK) { case TX_CMD_SEC_CCM: @@ -207,17 +209,17 @@ static void iwlagn_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, * @trans - transport private data * @txq - tx queue * @index - the index of the TFD to be freed + *@dma_dir - the direction of the DMA mapping * * Does NOT advance any TFD circular buffer read/write indexes * Does NOT free the TFD itself (which is within circular buffer) */ void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, - int index) + int index, enum dma_data_direction dma_dir) { struct iwl_tfd *tfd_tmp = txq->tfds; - iwlagn_unmap_tfd(trans, &txq->meta[index], &tfd_tmp[index], - DMA_TO_DEVICE); + iwlagn_unmap_tfd(trans, &txq->meta[index], &tfd_tmp[index], dma_dir); /* free SKB */ if (txq->skbs) { @@ -225,9 +227,12 @@ void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, skb = txq->skbs[index]; - /* can be called from irqs-disabled context */ + /* Can be called from irqs-disabled context + * If skb is not NULL, it means that the whole queue is being + * freed and that the queue is not empty - free the skb + */ if (skb) { - dev_kfree_skb_any(skb); + iwl_free_skb(priv(trans), skb); txq->skbs[index] = NULL; } } @@ -350,11 +355,13 @@ static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, int read_ptr = txq->q.read_ptr; u8 sta_id = 0; __le16 bc_ent; + struct iwl_tx_cmd *tx_cmd = + (struct iwl_tx_cmd *) txq->cmd[txq->q.read_ptr]->payload; WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX); if (txq_id != trans->shrd->cmd_queue) - sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id; + sta_id = tx_cmd->sta_id; bc_ent = cpu_to_le16(1 | (sta_id << 12)); scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; @@ -759,8 +766,6 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ if (cmd->flags & CMD_WANT_SKB) out_meta->source = cmd; - if (cmd->flags & CMD_ASYNC) - out_meta->callback = cmd->callback; /* set up the header */ @@ -772,7 +777,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) /* and copy the data that needs to be copied */ - cmd_dest = &out_cmd->cmd.payload[0]; + cmd_dest = out_cmd->payload; for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; @@ -891,12 +896,15 @@ static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, /** * iwl_tx_cmd_complete - Pull unused buffers off the queue and reclaim them * @rxb: Rx buffer to reclaim + * @handler_status: return value of the handler of the command + * (put in setup_rx_handlers) * * If an Rx buffer has an async callback associated with it the callback * will be executed. The attached skb (if present) will only be freed * if the callback returns 1 */ -void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb) +void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb, + int handler_status) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); @@ -933,9 +941,9 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb) /* Input error checking is done when commands are added to queue. */ if (meta->flags & CMD_WANT_SKB) { meta->source->reply_page = (unsigned long)rxb_addr(rxb); + meta->source->handler_status = handler_status; rxb->page = NULL; - } else if (meta->callback) - meta->callback(trans->shrd, cmd, pkt); + } spin_lock_irqsave(&trans->hcmd_lock, flags); @@ -945,7 +953,7 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb) clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", get_cmd_string(cmd->hdr.cmd)); - wake_up_interruptible(&trans->shrd->wait_command_queue); + wake_up(&trans->shrd->wait_command_queue); } meta->flags = 0; @@ -955,30 +963,6 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb) #define HOST_COMPLETE_TIMEOUT (2 * HZ) -static void iwl_generic_cmd_callback(struct iwl_shared *shrd, - struct iwl_device_cmd *cmd, - struct iwl_rx_packet *pkt) -{ - if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { - IWL_ERR(shrd->trans, "Bad return from %s (0x%08X)\n", - get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); - return; - } - -#ifdef CONFIG_IWLWIFI_DEBUG - switch (cmd->hdr.cmd) { - case REPLY_TX_LINK_QUALITY_CMD: - case SENSITIVITY_CMD: - IWL_DEBUG_HC_DUMP(shrd->trans, "back from %s (0x%08X)\n", - get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); - break; - default: - IWL_DEBUG_HC(shrd->trans, "back from %s (0x%08X)\n", - get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); - } -#endif -} - static int iwl_send_cmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { int ret; @@ -987,9 +971,6 @@ static int iwl_send_cmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (WARN_ON(cmd->flags & CMD_WANT_SKB)) return -EINVAL; - /* Assign a generic callback if one is not provided */ - if (!cmd->callback) - cmd->callback = iwl_generic_cmd_callback; if (test_bit(STATUS_EXIT_PENDING, &trans->shrd->status)) return -EBUSY; @@ -1011,10 +992,6 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) lockdep_assert_held(&trans->shrd->mutex); - /* A synchronous command can not have a callback set. */ - if (WARN_ON(cmd->callback)) - return -EINVAL; - IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", get_cmd_string(cmd->id)); @@ -1031,7 +1008,7 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) return ret; } - ret = wait_event_interruptible_timeout(trans->shrd->wait_command_queue, + ret = wait_event_timeout(trans->shrd->wait_command_queue, !test_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status), HOST_COMPLETE_TIMEOUT); if (!ret) { @@ -1098,19 +1075,6 @@ int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) return iwl_send_cmd_sync(trans, cmd); } -int iwl_trans_pcie_send_cmd_pdu(struct iwl_trans *trans, u8 id, u32 flags, - u16 len, const void *data) -{ - struct iwl_host_cmd cmd = { - .id = id, - .len = { len, }, - .data = { data, }, - .flags = flags, - }; - - return iwl_trans_pcie_send_cmd(trans, &cmd); -} - /* Frees buffers until index _not_ inclusive */ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, struct sk_buff_head *skbs) @@ -1121,6 +1085,10 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, int last_to_free; int freed = 0; + /* This function is not meant to release cmd queue*/ + if (WARN_ON(txq_id == trans->shrd->cmd_queue)) + return 0; + /*Since we free until index _not_ inclusive, the one before index is * the last we will free. This one must be used */ last_to_free = iwl_queue_dec_wrap(index, q->n_bd); @@ -1153,7 +1121,7 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, iwlagn_txq_inval_byte_cnt_tbl(trans, txq); - iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr); + iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr, DMA_TO_DEVICE); freed++; } return freed; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c new file mode 100644 index 0000000..416e992 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -0,0 +1,1989 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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 Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#include <linux/interrupt.h> +#include <linux/debugfs.h> +#include <linux/bitops.h> +#include <linux/gfp.h> + +#include "iwl-trans.h" +#include "iwl-trans-pcie-int.h" +#include "iwl-csr.h" +#include "iwl-prph.h" +#include "iwl-shared.h" +#include "iwl-eeprom.h" +#include "iwl-agn-hw.h" + +static int iwl_trans_rx_alloc(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + struct device *dev = bus(trans)->dev; + + memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq)); + + spin_lock_init(&rxq->lock); + + if (WARN_ON(rxq->bd || rxq->rb_stts)) + return -EINVAL; + + /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ + rxq->bd = dma_alloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, + &rxq->bd_dma, GFP_KERNEL); + if (!rxq->bd) + goto err_bd; + memset(rxq->bd, 0, sizeof(__le32) * RX_QUEUE_SIZE); + + /*Allocate the driver's pointer to receive buffer status */ + rxq->rb_stts = dma_alloc_coherent(dev, sizeof(*rxq->rb_stts), + &rxq->rb_stts_dma, GFP_KERNEL); + if (!rxq->rb_stts) + goto err_rb_stts; + memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts)); + + return 0; + +err_rb_stts: + dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, + rxq->bd, rxq->bd_dma); + memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); + rxq->bd = NULL; +err_bd: + return -ENOMEM; +} + +static void iwl_trans_rxq_free_rx_bufs(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + int i; + + /* Fill the rx_used queue with _all_ of the Rx buffers */ + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { + /* In the reset function, these buffers may have been allocated + * to an SKB, so we need to unmap and free potential storage */ + if (rxq->pool[i].page != NULL) { + dma_unmap_page(bus(trans)->dev, rxq->pool[i].page_dma, + PAGE_SIZE << hw_params(trans).rx_page_order, + DMA_FROM_DEVICE); + __free_pages(rxq->pool[i].page, + hw_params(trans).rx_page_order); + rxq->pool[i].page = NULL; + } + list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + } +} + +static void iwl_trans_rx_hw_init(struct iwl_trans *trans, + struct iwl_rx_queue *rxq) +{ + u32 rb_size; + const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ + u32 rb_timeout = RX_RB_TIMEOUT; /* FIXME: RX_RB_TIMEOUT for all devices? */ + + if (iwlagn_mod_params.amsdu_size_8K) + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; + else + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; + + /* Stop Rx DMA */ + iwl_write_direct32(bus(trans), FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + + /* Reset driver's Rx queue write index */ + iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); + + /* Tell device where to find RBD circular buffer in DRAM */ + iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_RBDCB_BASE_REG, + (u32)(rxq->bd_dma >> 8)); + + /* Tell device where in DRAM to update its Rx status */ + iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_STTS_WPTR_REG, + rxq->rb_stts_dma >> 4); + + /* Enable Rx DMA + * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in + * the credit mechanism in 5000 HW RX FIFO + * Direct rx interrupts to hosts + * Rx buffer size 4 or 8k + * RB timeout 0x10 + * 256 RBDs + */ + iwl_write_direct32(bus(trans), FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | + FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | + FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | + FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | + rb_size| + (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| + (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); + + /* Set interrupt coalescing timer to default (2048 usecs) */ + iwl_write8(bus(trans), CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); +} + +static int iwl_rx_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + + int i, err; + unsigned long flags; + + if (!rxq->bd) { + err = iwl_trans_rx_alloc(trans); + if (err) + return err; + } + + spin_lock_irqsave(&rxq->lock, flags); + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + + iwl_trans_rxq_free_rx_bufs(trans); + + for (i = 0; i < RX_QUEUE_SIZE; i++) + rxq->queue[i] = NULL; + + /* Set us so that we have processed and used all buffers, but have + * not restocked the Rx queue with fresh buffers */ + rxq->read = rxq->write = 0; + rxq->write_actual = 0; + rxq->free_count = 0; + spin_unlock_irqrestore(&rxq->lock, flags); + + iwlagn_rx_replenish(trans); + + iwl_trans_rx_hw_init(trans, rxq); + + spin_lock_irqsave(&trans->shrd->lock, flags); + rxq->need_update = 1; + iwl_rx_queue_update_write_ptr(trans, rxq); + spin_unlock_irqrestore(&trans->shrd->lock, flags); + + return 0; +} + +static void iwl_trans_pcie_rx_free(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + + unsigned long flags; + + /*if rxq->bd is NULL, it means that nothing has been allocated, + * exit now */ + if (!rxq->bd) { + IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); + return; + } + + spin_lock_irqsave(&rxq->lock, flags); + iwl_trans_rxq_free_rx_bufs(trans); + spin_unlock_irqrestore(&rxq->lock, flags); + + dma_free_coherent(bus(trans)->dev, sizeof(__le32) * RX_QUEUE_SIZE, + rxq->bd, rxq->bd_dma); + memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); + rxq->bd = NULL; + + if (rxq->rb_stts) + dma_free_coherent(bus(trans)->dev, + sizeof(struct iwl_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); + else + IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); + memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma)); + rxq->rb_stts = NULL; +} + +static int iwl_trans_rx_stop(struct iwl_trans *trans) +{ + + /* stop Rx DMA */ + iwl_write_direct32(bus(trans), FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + return iwl_poll_direct_bit(bus(trans), FH_MEM_RSSR_RX_STATUS_REG, + FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); +} + +static inline int iwlagn_alloc_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr, size_t size) +{ + if (WARN_ON(ptr->addr)) + return -EINVAL; + + ptr->addr = dma_alloc_coherent(bus(trans)->dev, size, + &ptr->dma, GFP_KERNEL); + if (!ptr->addr) + return -ENOMEM; + ptr->size = size; + return 0; +} + +static inline void iwlagn_free_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr) +{ + if (unlikely(!ptr->addr)) + return; + + dma_free_coherent(bus(trans)->dev, ptr->size, ptr->addr, ptr->dma); + memset(ptr, 0, sizeof(*ptr)); +} + +static int iwl_trans_txq_alloc(struct iwl_trans *trans, + struct iwl_tx_queue *txq, int slots_num, + u32 txq_id) +{ + size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; + int i; + + if (WARN_ON(txq->meta || txq->cmd || txq->skbs || txq->tfds)) + return -EINVAL; + + txq->q.n_window = slots_num; + + txq->meta = kcalloc(slots_num, sizeof(txq->meta[0]), GFP_KERNEL); + txq->cmd = kcalloc(slots_num, sizeof(txq->cmd[0]), GFP_KERNEL); + + if (!txq->meta || !txq->cmd) + goto error; + + if (txq_id == trans->shrd->cmd_queue) + for (i = 0; i < slots_num; i++) { + txq->cmd[i] = kmalloc(sizeof(struct iwl_device_cmd), + GFP_KERNEL); + if (!txq->cmd[i]) + goto error; + } + + /* Alloc driver data array and TFD circular buffer */ + /* Driver private data, only for Tx (not command) queues, + * not shared with device. */ + if (txq_id != trans->shrd->cmd_queue) { + txq->skbs = kcalloc(TFD_QUEUE_SIZE_MAX, sizeof(txq->skbs[0]), + GFP_KERNEL); + if (!txq->skbs) { + IWL_ERR(trans, "kmalloc for auxiliary BD " + "structures failed\n"); + goto error; + } + } else { + txq->skbs = NULL; + } + + /* Circular buffer of transmit frame descriptors (TFDs), + * shared with device */ + txq->tfds = dma_alloc_coherent(bus(trans)->dev, tfd_sz, + &txq->q.dma_addr, GFP_KERNEL); + if (!txq->tfds) { + IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz); + goto error; + } + txq->q.id = txq_id; + + return 0; +error: + kfree(txq->skbs); + txq->skbs = NULL; + /* since txq->cmd has been zeroed, + * all non allocated cmd[i] will be NULL */ + if (txq->cmd && txq_id == trans->shrd->cmd_queue) + for (i = 0; i < slots_num; i++) + kfree(txq->cmd[i]); + kfree(txq->meta); + kfree(txq->cmd); + txq->meta = NULL; + txq->cmd = NULL; + + return -ENOMEM; + +} + +static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq, + int slots_num, u32 txq_id) +{ + int ret; + + txq->need_update = 0; + memset(txq->meta, 0, sizeof(txq->meta[0]) * slots_num); + + /* + * For the default queues 0-3, set up the swq_id + * already -- all others need to get one later + * (if they need one at all). + */ + if (txq_id < 4) + iwl_set_swq_id(txq, txq_id, txq_id); + + /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise + * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ + BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); + + /* Initialize queue's high/low-water marks, and head/tail indexes */ + ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num, + txq_id); + if (ret) + return ret; + + /* + * Tell nic where to find circular buffer of Tx Frame Descriptors for + * given Tx queue, and enable the DMA channel used for that queue. + * Circular buffer (TFD queue in DRAM) physical base address */ + iwl_write_direct32(bus(trans), FH_MEM_CBBC_QUEUE(txq_id), + txq->q.dma_addr >> 8); + + return 0; +} + +/** + * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's + */ +static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; + struct iwl_queue *q = &txq->q; + enum dma_data_direction dma_dir; + + if (!q->n_bd) + return; + + /* In the command queue, all the TBs are mapped as BIDI + * so unmap them as such. + */ + if (txq_id == trans->shrd->cmd_queue) + dma_dir = DMA_BIDIRECTIONAL; + else + dma_dir = DMA_TO_DEVICE; + + while (q->write_ptr != q->read_ptr) { + /* The read_ptr needs to bound by q->n_window */ + iwlagn_txq_free_tfd(trans, txq, get_cmd_index(q, q->read_ptr), + dma_dir); + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); + } +} + +/** + * iwl_tx_queue_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; + struct device *dev = bus(trans)->dev; + int i; + if (WARN_ON(!txq)) + return; + + iwl_tx_queue_unmap(trans, txq_id); + + /* De-alloc array of command/tx buffers */ + + if (txq_id == trans->shrd->cmd_queue) + for (i = 0; i < txq->q.n_window; i++) + kfree(txq->cmd[i]); + + /* De-alloc circular buffer of TFDs */ + if (txq->q.n_bd) { + dma_free_coherent(dev, sizeof(struct iwl_tfd) * + txq->q.n_bd, txq->tfds, txq->q.dma_addr); + memset(&txq->q.dma_addr, 0, sizeof(txq->q.dma_addr)); + } + + /* De-alloc array of per-TFD driver data */ + kfree(txq->skbs); + txq->skbs = NULL; + + /* deallocate arrays */ + kfree(txq->cmd); + kfree(txq->meta); + txq->cmd = NULL; + txq->meta = NULL; + + /* 0-fill queue descriptor structure */ + memset(txq, 0, sizeof(*txq)); +} + +/** + * iwl_trans_tx_free - Free TXQ Context + * + * Destroy all TX DMA queues and structures + */ +static void iwl_trans_pcie_tx_free(struct iwl_trans *trans) +{ + int txq_id; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + /* Tx queues */ + if (trans_pcie->txq) { + for (txq_id = 0; + txq_id < hw_params(trans).max_txq_num; txq_id++) + iwl_tx_queue_free(trans, txq_id); + } + + kfree(trans_pcie->txq); + trans_pcie->txq = NULL; + + iwlagn_free_dma_ptr(trans, &trans_pcie->kw); + + iwlagn_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls); +} + +/** + * iwl_trans_tx_alloc - allocate TX context + * Allocate all Tx DMA structures and initialize them + * + * @param priv + * @return error code + */ +static int iwl_trans_tx_alloc(struct iwl_trans *trans) +{ + int ret; + int txq_id, slots_num; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + u16 scd_bc_tbls_size = hw_params(trans).max_txq_num * + sizeof(struct iwlagn_scd_bc_tbl); + + /*It is not allowed to alloc twice, so warn when this happens. + * We cannot rely on the previous allocation, so free and fail */ + if (WARN_ON(trans_pcie->txq)) { + ret = -EINVAL; + goto error; + } + + ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls, + scd_bc_tbls_size); + if (ret) { + IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); + goto error; + } + + /* Alloc keep-warm buffer */ + ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE); + if (ret) { + IWL_ERR(trans, "Keep Warm allocation failed\n"); + goto error; + } + + trans_pcie->txq = kcalloc(hw_params(trans).max_txq_num, + sizeof(struct iwl_tx_queue), GFP_KERNEL); + if (!trans_pcie->txq) { + IWL_ERR(trans, "Not enough memory for txq\n"); + ret = ENOMEM; + goto error; + } + + /* Alloc and init all Tx queues, including the command queue (#4/#9) */ + for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++) { + slots_num = (txq_id == trans->shrd->cmd_queue) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + ret = iwl_trans_txq_alloc(trans, &trans_pcie->txq[txq_id], + slots_num, txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); + goto error; + } + } + + return 0; + +error: + iwl_trans_pcie_tx_free(trans); + + return ret; +} +static int iwl_tx_init(struct iwl_trans *trans) +{ + int ret; + int txq_id, slots_num; + unsigned long flags; + bool alloc = false; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (!trans_pcie->txq) { + ret = iwl_trans_tx_alloc(trans); + if (ret) + goto error; + alloc = true; + } + + spin_lock_irqsave(&trans->shrd->lock, flags); + + /* Turn off all Tx DMA fifos */ + iwl_write_prph(bus(trans), SCD_TXFACT, 0); + + /* Tell NIC where to find the "keep warm" buffer */ + iwl_write_direct32(bus(trans), FH_KW_MEM_ADDR_REG, + trans_pcie->kw.dma >> 4); + + spin_unlock_irqrestore(&trans->shrd->lock, flags); + + /* Alloc and init all Tx queues, including the command queue (#4/#9) */ + for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++) { + slots_num = (txq_id == trans->shrd->cmd_queue) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + ret = iwl_trans_txq_init(trans, &trans_pcie->txq[txq_id], + slots_num, txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); + goto error; + } + } + + return 0; +error: + /*Upon error, free only if we allocated something */ + if (alloc) + iwl_trans_pcie_tx_free(trans); + return ret; +} + +static void iwl_set_pwr_vmain(struct iwl_trans *trans) +{ +/* + * (for documentation purposes) + * to set power to V_AUX, do: + + if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) + iwl_set_bits_mask_prph(bus(trans), APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + */ + + iwl_set_bits_mask_prph(bus(trans), APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); +} + +static int iwl_nic_init(struct iwl_trans *trans) +{ + unsigned long flags; + + /* nic_init */ + spin_lock_irqsave(&trans->shrd->lock, flags); + iwl_apm_init(priv(trans)); + + /* Set interrupt coalescing calibration timer to default (512 usecs) */ + iwl_write8(bus(trans), CSR_INT_COALESCING, + IWL_HOST_INT_CALIB_TIMEOUT_DEF); + + spin_unlock_irqrestore(&trans->shrd->lock, flags); + + iwl_set_pwr_vmain(trans); + + iwl_nic_config(priv(trans)); + + /* Allocate the RX queue, or reset if it is already allocated */ + iwl_rx_init(trans); + + /* Allocate or reset and init all Tx and Command queues */ + if (iwl_tx_init(trans)) + return -ENOMEM; + + if (hw_params(trans).shadow_reg_enable) { + /* enable shadow regs in HW */ + iwl_set_bit(bus(trans), CSR_MAC_SHADOW_REG_CTRL, + 0x800FFFFF); + } + + set_bit(STATUS_INIT, &trans->shrd->status); + + return 0; +} + +#define HW_READY_TIMEOUT (50) + +/* Note: returns poll_bit return value, which is >= 0 if success */ +static int iwl_set_hw_ready(struct iwl_trans *trans) +{ + int ret; + + iwl_set_bit(bus(trans), CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); + + /* See if we got it */ + ret = iwl_poll_bit(bus(trans), CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + HW_READY_TIMEOUT); + + IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : ""); + return ret; +} + +/* Note: returns standard 0/-ERROR code */ +static int iwl_trans_pcie_prepare_card_hw(struct iwl_trans *trans) +{ + int ret; + + IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); + + ret = iwl_set_hw_ready(trans); + if (ret >= 0) + return 0; + + /* If HW is not ready, prepare the conditions to check again */ + iwl_set_bit(bus(trans), CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PREPARE); + + ret = iwl_poll_bit(bus(trans), CSR_HW_IF_CONFIG_REG, + ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, + CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); + + if (ret < 0) + return ret; + + /* HW should be ready by now, check again. */ + ret = iwl_set_hw_ready(trans); + if (ret >= 0) + return 0; + return ret; +} + +#define IWL_AC_UNSET -1 + +struct queue_to_fifo_ac { + s8 fifo, ac; +}; + +static const struct queue_to_fifo_ac iwlagn_default_queue_to_tx_fifo[] = { + { IWL_TX_FIFO_VO, IEEE80211_AC_VO, }, + { IWL_TX_FIFO_VI, IEEE80211_AC_VI, }, + { IWL_TX_FIFO_BE, IEEE80211_AC_BE, }, + { IWL_TX_FIFO_BK, IEEE80211_AC_BK, }, + { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, }, + { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, + { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, + { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, + { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, + { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, + { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, +}; + +static const struct queue_to_fifo_ac iwlagn_ipan_queue_to_tx_fifo[] = { + { IWL_TX_FIFO_VO, IEEE80211_AC_VO, }, + { IWL_TX_FIFO_VI, IEEE80211_AC_VI, }, + { IWL_TX_FIFO_BE, IEEE80211_AC_BE, }, + { IWL_TX_FIFO_BK, IEEE80211_AC_BK, }, + { IWL_TX_FIFO_BK_IPAN, IEEE80211_AC_BK, }, + { IWL_TX_FIFO_BE_IPAN, IEEE80211_AC_BE, }, + { IWL_TX_FIFO_VI_IPAN, IEEE80211_AC_VI, }, + { IWL_TX_FIFO_VO_IPAN, IEEE80211_AC_VO, }, + { IWL_TX_FIFO_BE_IPAN, 2, }, + { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, }, + { IWL_TX_FIFO_AUX, IWL_AC_UNSET, }, +}; + +static const u8 iwlagn_bss_ac_to_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, +}; +static const u8 iwlagn_bss_ac_to_queue[] = { + 0, 1, 2, 3, +}; +static const u8 iwlagn_pan_ac_to_fifo[] = { + IWL_TX_FIFO_VO_IPAN, + IWL_TX_FIFO_VI_IPAN, + IWL_TX_FIFO_BE_IPAN, + IWL_TX_FIFO_BK_IPAN, +}; +static const u8 iwlagn_pan_ac_to_queue[] = { + 7, 6, 5, 4, +}; + +static int iwl_trans_pcie_start_device(struct iwl_trans *trans) +{ + int ret; + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + + trans->shrd->ucode_owner = IWL_OWNERSHIP_DRIVER; + trans_pcie->ac_to_queue[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_queue; + trans_pcie->ac_to_queue[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_queue; + + trans_pcie->ac_to_fifo[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_fifo; + trans_pcie->ac_to_fifo[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_fifo; + + trans_pcie->mcast_queue[IWL_RXON_CTX_BSS] = 0; + trans_pcie->mcast_queue[IWL_RXON_CTX_PAN] = IWL_IPAN_MCAST_QUEUE; + + if ((hw_params(trans).sku & EEPROM_SKU_CAP_AMT_ENABLE) && + iwl_trans_pcie_prepare_card_hw(trans)) { + IWL_WARN(trans, "Exit HW not ready\n"); + return -EIO; + } + + /* If platform's RF_KILL switch is NOT set to KILL */ + if (iwl_read32(bus(trans), CSR_GP_CNTRL) & + CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) + clear_bit(STATUS_RF_KILL_HW, &trans->shrd->status); + else + set_bit(STATUS_RF_KILL_HW, &trans->shrd->status); + + if (iwl_is_rfkill(trans->shrd)) { + iwl_set_hw_rfkill_state(priv(trans), true); + iwl_enable_interrupts(trans); + return -ERFKILL; + } + + iwl_write32(bus(trans), CSR_INT, 0xFFFFFFFF); + + ret = iwl_nic_init(trans); + if (ret) { + IWL_ERR(trans, "Unable to init nic\n"); + return ret; + } + + /* make sure rfkill handshake bits are cleared */ + iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + iwl_write32(bus(trans), CSR_INT, 0xFFFFFFFF); + iwl_enable_interrupts(trans); + + /* really make sure rfkill handshake bits are cleared */ + iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + + return 0; +} + +/* + * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask + * must be called under priv->shrd->lock and mac access + */ +static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask) +{ + iwl_write_prph(bus(trans), SCD_TXFACT, mask); +} + +static void iwl_trans_pcie_tx_start(struct iwl_trans *trans) +{ + const struct queue_to_fifo_ac *queue_to_fifo; + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + u32 a; + unsigned long flags; + int i, chan; + u32 reg_val; + + spin_lock_irqsave(&trans->shrd->lock, flags); + + trans_pcie->scd_base_addr = + iwl_read_prph(bus(trans), SCD_SRAM_BASE_ADDR); + a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND; + /* reset conext data memory */ + for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND; + a += 4) + iwl_write_targ_mem(bus(trans), a, 0); + /* reset tx status memory */ + for (; a < trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_UPPER_BOUND; + a += 4) + iwl_write_targ_mem(bus(trans), a, 0); + for (; a < trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE(hw_params(trans).max_txq_num); + a += 4) + iwl_write_targ_mem(bus(trans), a, 0); + + iwl_write_prph(bus(trans), SCD_DRAM_BASE_ADDR, + trans_pcie->scd_bc_tbls.dma >> 10); + + /* Enable DMA channel */ + for (chan = 0; chan < FH_TCSR_CHNL_NUM ; chan++) + iwl_write_direct32(bus(trans), FH_TCSR_CHNL_TX_CONFIG_REG(chan), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); + + /* Update FH chicken bits */ + reg_val = iwl_read_direct32(bus(trans), FH_TX_CHICKEN_BITS_REG); + iwl_write_direct32(bus(trans), FH_TX_CHICKEN_BITS_REG, + reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); + + iwl_write_prph(bus(trans), SCD_QUEUECHAIN_SEL, + SCD_QUEUECHAIN_SEL_ALL(trans)); + iwl_write_prph(bus(trans), SCD_AGGR_SEL, 0); + + /* initiate the queues */ + for (i = 0; i < hw_params(trans).max_txq_num; i++) { + iwl_write_prph(bus(trans), SCD_QUEUE_RDPTR(i), 0); + iwl_write_direct32(bus(trans), HBUS_TARG_WRPTR, 0 | (i << 8)); + iwl_write_targ_mem(bus(trans), trans_pcie->scd_base_addr + + SCD_CONTEXT_QUEUE_OFFSET(i), 0); + iwl_write_targ_mem(bus(trans), trans_pcie->scd_base_addr + + SCD_CONTEXT_QUEUE_OFFSET(i) + + sizeof(u32), + ((SCD_WIN_SIZE << + SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & + SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | + ((SCD_FRAME_LIMIT << + SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & + SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); + } + + iwl_write_prph(bus(trans), SCD_INTERRUPT_MASK, + IWL_MASK(0, hw_params(trans).max_txq_num)); + + /* Activate all Tx DMA/FIFO channels */ + iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); + + /* map queues to FIFOs */ + if (trans->shrd->valid_contexts != BIT(IWL_RXON_CTX_BSS)) + queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo; + else + queue_to_fifo = iwlagn_default_queue_to_tx_fifo; + + iwl_trans_set_wr_ptrs(trans, trans->shrd->cmd_queue, 0); + + /* make sure all queue are not stopped */ + memset(&trans_pcie->queue_stopped[0], 0, + sizeof(trans_pcie->queue_stopped)); + for (i = 0; i < 4; i++) + atomic_set(&trans_pcie->queue_stop_count[i], 0); + + /* reset to 0 to enable all the queue first */ + trans_pcie->txq_ctx_active_msk = 0; + + BUILD_BUG_ON(ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo) < + IWLAGN_FIRST_AMPDU_QUEUE); + BUILD_BUG_ON(ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo) < + IWLAGN_FIRST_AMPDU_QUEUE); + + for (i = 0; i < IWLAGN_FIRST_AMPDU_QUEUE; i++) { + int fifo = queue_to_fifo[i].fifo; + int ac = queue_to_fifo[i].ac; + + iwl_txq_ctx_activate(trans_pcie, i); + + if (fifo == IWL_TX_FIFO_UNUSED) + continue; + + if (ac != IWL_AC_UNSET) + iwl_set_swq_id(&trans_pcie->txq[i], ac, i); + iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[i], + fifo, 0); + } + + spin_unlock_irqrestore(&trans->shrd->lock, flags); + + /* Enable L1-Active */ + iwl_clear_bits_prph(bus(trans), APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); +} + +/** + * iwlagn_txq_ctx_stop - Stop all Tx DMA channels + */ +static int iwl_trans_tx_stop(struct iwl_trans *trans) +{ + int ch, txq_id; + unsigned long flags; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + /* Turn off all Tx DMA fifos */ + spin_lock_irqsave(&trans->shrd->lock, flags); + + iwl_trans_txq_set_sched(trans, 0); + + /* Stop each Tx DMA channel, and wait for it to be idle */ + for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { + iwl_write_direct32(bus(trans), + FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); + if (iwl_poll_direct_bit(bus(trans), FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), + 1000)) + IWL_ERR(trans, "Failing on timeout while stopping" + " DMA channel %d [0x%08x]", ch, + iwl_read_direct32(bus(trans), + FH_TSSR_TX_STATUS_REG)); + } + spin_unlock_irqrestore(&trans->shrd->lock, flags); + + if (!trans_pcie->txq) { + IWL_WARN(trans, "Stopping tx queues that aren't allocated..."); + return 0; + } + + /* Unmap DMA from host system and free skb's */ + for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++) + iwl_tx_queue_unmap(trans, txq_id); + + return 0; +} + +static void iwl_trans_pcie_disable_sync_irq(struct iwl_trans *trans) +{ + unsigned long flags; + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + + spin_lock_irqsave(&trans->shrd->lock, flags); + iwl_disable_interrupts(trans); + spin_unlock_irqrestore(&trans->shrd->lock, flags); + + /* wait to make sure we flush pending tasklet*/ + synchronize_irq(bus(trans)->irq); + tasklet_kill(&trans_pcie->irq_tasklet); +} + +static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) +{ + /* stop and reset the on-board processor */ + iwl_write32(bus(trans), CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + /* tell the device to stop sending interrupts */ + iwl_trans_pcie_disable_sync_irq(trans); + + /* device going down, Stop using ICT table */ + iwl_disable_ict(trans); + + /* + * If a HW restart happens during firmware loading, + * then the firmware loading might call this function + * and later it might be called again due to the + * restart. So don't process again if the device is + * already dead. + */ + if (test_bit(STATUS_DEVICE_ENABLED, &trans->shrd->status)) { + iwl_trans_tx_stop(trans); + iwl_trans_rx_stop(trans); + + /* Power-down device's busmaster DMA clocks */ + iwl_write_prph(bus(trans), APMG_CLK_DIS_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + udelay(5); + } + + /* Make sure (redundant) we've released our request to stay awake */ + iwl_clear_bit(bus(trans), CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + /* Stop the device, and put it in low power state */ + iwl_apm_stop(priv(trans)); +} + +static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx, + u8 sta_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; + struct iwl_cmd_meta *out_meta; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + + dma_addr_t phys_addr = 0; + dma_addr_t txcmd_phys; + dma_addr_t scratch_phys; + u16 len, firstlen, secondlen; + u16 seq_number = 0; + u8 wait_write_ptr = 0; + u8 txq_id; + u8 tid = 0; + bool is_agg = false; + __le16 fc = hdr->frame_control; + u8 hdr_len = ieee80211_hdrlen(fc); + + /* + * Send this frame after DTIM -- there's a special queue + * reserved for this for contexts that support AP mode. + */ + if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { + txq_id = trans_pcie->mcast_queue[ctx]; + + /* + * The microcode will clear the more data + * bit in the last frame it transmits. + */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) + txq_id = IWL_AUX_QUEUE; + else + txq_id = + trans_pcie->ac_to_queue[ctx][skb_get_queue_mapping(skb)]; + + if (ieee80211_is_data_qos(fc)) { + u8 *qc = NULL; + struct iwl_tid_data *tid_data; + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + tid_data = &trans->shrd->tid_data[sta_id][tid]; + + if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) + return -1; + + seq_number = tid_data->seq_number; + seq_number &= IEEE80211_SCTL_SEQ; + hdr->seq_ctrl = hdr->seq_ctrl & + cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(seq_number); + seq_number += 0x10; + /* aggregation is on for this <sta,tid> */ + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + WARN_ON(tid_data->agg.state != IWL_AGG_ON); + txq_id = tid_data->agg.txq_id; + is_agg = true; + } + } + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, hdr_len); + + txq = &trans_pcie->txq[txq_id]; + q = &txq->q; + + /* Set up driver data for this TFD */ + txq->skbs[q->write_ptr] = skb; + txq->cmd[q->write_ptr] = dev_cmd; + + dev_cmd->hdr.cmd = REPLY_TX; + dev_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | + INDEX_TO_SEQ(q->write_ptr))); + + /* Set up first empty entry in queue's array of Tx/cmd buffers */ + out_meta = &txq->meta[q->write_ptr]; + + /* + * Use the first empty entry in this queue's command buffer array + * to contain the Tx command and MAC header concatenated together + * (payload data will be in another buffer). + * Size of this varies, due to varying MAC header length. + * If end is not dword aligned, we'll have 2 extra bytes at the end + * of the MAC header (device reads on dword boundaries). + * We'll tell device about this padding later. + */ + len = sizeof(struct iwl_tx_cmd) + + sizeof(struct iwl_cmd_header) + hdr_len; + firstlen = (len + 3) & ~3; + + /* Tell NIC about any 2-byte padding after MAC header */ + if (firstlen != len) + tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; + + /* Physical address of this Tx command's header (not MAC header!), + * within command buffer array. */ + txcmd_phys = dma_map_single(bus(trans)->dev, + &dev_cmd->hdr, firstlen, + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(bus(trans)->dev, txcmd_phys))) + return -1; + dma_unmap_addr_set(out_meta, mapping, txcmd_phys); + dma_unmap_len_set(out_meta, len, firstlen); + + if (!ieee80211_has_morefrags(fc)) { + txq->need_update = 1; + } else { + wait_write_ptr = 1; + txq->need_update = 0; + } + + /* Set up TFD's 2nd entry to point directly to remainder of skb, + * if any (802.11 null frames have no payload). */ + secondlen = skb->len - hdr_len; + if (secondlen > 0) { + phys_addr = dma_map_single(bus(trans)->dev, skb->data + hdr_len, + secondlen, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(bus(trans)->dev, phys_addr))) { + dma_unmap_single(bus(trans)->dev, + dma_unmap_addr(out_meta, mapping), + dma_unmap_len(out_meta, len), + DMA_BIDIRECTIONAL); + return -1; + } + } + + /* Attach buffers to TFD */ + iwlagn_txq_attach_buf_to_tfd(trans, txq, txcmd_phys, firstlen, 1); + if (secondlen > 0) + iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, + secondlen, 0); + + scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + + offsetof(struct iwl_tx_cmd, scratch); + + /* take back ownership of DMA buffer to enable update */ + dma_sync_single_for_cpu(bus(trans)->dev, txcmd_phys, firstlen, + DMA_BIDIRECTIONAL); + tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); + tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); + + IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n", + le16_to_cpu(dev_cmd->hdr.sequence)); + IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); + iwl_print_hex_dump(trans, IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd)); + iwl_print_hex_dump(trans, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len); + + /* Set up entry for this TFD in Tx byte-count array */ + if (is_agg) + iwl_trans_txq_update_byte_cnt_tbl(trans, txq, + le16_to_cpu(tx_cmd->len)); + + dma_sync_single_for_device(bus(trans)->dev, txcmd_phys, firstlen, + DMA_BIDIRECTIONAL); + + trace_iwlwifi_dev_tx(priv(trans), + &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr], + sizeof(struct iwl_tfd), + &dev_cmd->hdr, firstlen, + skb->data + hdr_len, secondlen); + + /* Tell device the write index *just past* this latest filled TFD */ + q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); + iwl_txq_update_write_ptr(trans, txq); + + if (ieee80211_is_data_qos(fc)) { + trans->shrd->tid_data[sta_id][tid].tfds_in_queue++; + if (!ieee80211_has_morefrags(fc)) + trans->shrd->tid_data[sta_id][tid].seq_number = + seq_number; + } + + /* + * At this point the frame is "transmitted" successfully + * and we will get a TX status notification eventually, + * regardless of the value of ret. "ret" only indicates + * whether or not we should update the write pointer. + */ + if (iwl_queue_space(q) < q->high_mark) { + if (wait_write_ptr) { + txq->need_update = 1; + iwl_txq_update_write_ptr(trans, txq); + } else { + iwl_stop_queue(trans, txq); + } + } + return 0; +} + +static void iwl_trans_pcie_kick_nic(struct iwl_trans *trans) +{ + /* Remove all resets to allow NIC to operate */ + iwl_write32(bus(trans), CSR_RESET, 0); +} + +static int iwl_trans_pcie_request_irq(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + int err; + + trans_pcie->inta_mask = CSR_INI_SET_MASK; + + tasklet_init(&trans_pcie->irq_tasklet, (void (*)(unsigned long)) + iwl_irq_tasklet, (unsigned long)trans); + + iwl_alloc_isr_ict(trans); + + err = request_irq(bus(trans)->irq, iwl_isr_ict, IRQF_SHARED, + DRV_NAME, trans); + if (err) { + IWL_ERR(trans, "Error allocating IRQ %d\n", bus(trans)->irq); + iwl_free_isr_ict(trans); + return err; + } + + INIT_WORK(&trans_pcie->rx_replenish, iwl_bg_rx_replenish); + return 0; +} + +static int iwlagn_txq_check_empty(struct iwl_trans *trans, + int sta_id, u8 tid, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_queue *q = &trans_pcie->txq[txq_id].q; + struct iwl_tid_data *tid_data = &trans->shrd->tid_data[sta_id][tid]; + + lockdep_assert_held(&trans->shrd->sta_lock); + + switch (trans->shrd->tid_data[sta_id][tid].agg.state) { + case IWL_EMPTYING_HW_QUEUE_DELBA: + /* We are reclaiming the last packet of the */ + /* aggregated HW queue */ + if ((txq_id == tid_data->agg.txq_id) && + (q->read_ptr == q->write_ptr)) { + IWL_DEBUG_HT(trans, + "HW queue empty: continue DELBA flow\n"); + iwl_trans_pcie_txq_agg_disable(trans, txq_id); + tid_data->agg.state = IWL_AGG_OFF; + iwl_stop_tx_ba_trans_ready(priv(trans), + NUM_IWL_RXON_CTX, + sta_id, tid); + iwl_wake_queue(trans, &trans_pcie->txq[txq_id]); + } + break; + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* We are reclaiming the last packet of the queue */ + if (tid_data->tfds_in_queue == 0) { + IWL_DEBUG_HT(trans, + "HW queue empty: continue ADDBA flow\n"); + tid_data->agg.state = IWL_AGG_ON; + iwl_start_tx_ba_trans_ready(priv(trans), + NUM_IWL_RXON_CTX, + sta_id, tid); + } + break; + default: + break; + } + + return 0; +} + +static void iwl_free_tfds_in_queue(struct iwl_trans *trans, + int sta_id, int tid, int freed) +{ + lockdep_assert_held(&trans->shrd->sta_lock); + + if (trans->shrd->tid_data[sta_id][tid].tfds_in_queue >= freed) + trans->shrd->tid_data[sta_id][tid].tfds_in_queue -= freed; + else { + IWL_DEBUG_TX(trans, "free more than tfds_in_queue (%u:%d)\n", + trans->shrd->tid_data[sta_id][tid].tfds_in_queue, + freed); + trans->shrd->tid_data[sta_id][tid].tfds_in_queue = 0; + } +} + +static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid, + int txq_id, int ssn, u32 status, + struct sk_buff_head *skbs) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; + enum iwl_agg_state agg_state; + /* n_bd is usually 256 => n_bd - 1 = 0xff */ + int tfd_num = ssn & (txq->q.n_bd - 1); + int freed = 0; + bool cond; + + txq->time_stamp = jiffies; + + if (txq->sched_retry) { + agg_state = + trans->shrd->tid_data[txq->sta_id][txq->tid].agg.state; + cond = (agg_state != IWL_EMPTYING_HW_QUEUE_DELBA); + } else { + cond = (status != TX_STATUS_FAIL_PASSIVE_NO_RX); + } + + if (txq->q.read_ptr != tfd_num) { + IWL_DEBUG_TX_REPLY(trans, "Retry scheduler reclaim " + "scd_ssn=%d idx=%d txq=%d swq=%d\n", + ssn , tfd_num, txq_id, txq->swq_id); + freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs); + if (iwl_queue_space(&txq->q) > txq->q.low_mark && cond) + iwl_wake_queue(trans, txq); + } + + iwl_free_tfds_in_queue(trans, sta_id, tid, freed); + iwlagn_txq_check_empty(trans, sta_id, tid, txq_id); +} + +static void iwl_trans_pcie_free(struct iwl_trans *trans) +{ + iwl_trans_pcie_tx_free(trans); + iwl_trans_pcie_rx_free(trans); + free_irq(bus(trans)->irq, trans); + iwl_free_isr_ict(trans); + trans->shrd->trans = NULL; + kfree(trans); +} + +#ifdef CONFIG_PM_SLEEP +static int iwl_trans_pcie_suspend(struct iwl_trans *trans) +{ + /* + * This function is called when system goes into suspend state + * mac80211 will call iwl_mac_stop() from the mac80211 suspend function + * first but since iwl_mac_stop() has no knowledge of who the caller is, + * it will not call apm_ops.stop() to stop the DMA operation. + * Calling apm_ops.stop here to make sure we stop the DMA. + * + * But of course ... if we have configured WoWLAN then we did other + * things already :-) + */ + if (!trans->shrd->wowlan) + iwl_apm_stop(priv(trans)); + + return 0; +} + +static int iwl_trans_pcie_resume(struct iwl_trans *trans) +{ + bool hw_rfkill = false; + + iwl_enable_interrupts(trans); + + if (!(iwl_read32(bus(trans), CSR_GP_CNTRL) & + CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) + hw_rfkill = true; + + if (hw_rfkill) + set_bit(STATUS_RF_KILL_HW, &trans->shrd->status); + else + clear_bit(STATUS_RF_KILL_HW, &trans->shrd->status); + + iwl_set_hw_rfkill_state(priv(trans), hw_rfkill); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static void iwl_trans_pcie_wake_any_queue(struct iwl_trans *trans, + enum iwl_rxon_context_id ctx) +{ + u8 ac, txq_id; + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + + for (ac = 0; ac < AC_NUM; ac++) { + txq_id = trans_pcie->ac_to_queue[ctx][ac]; + IWL_DEBUG_INFO(trans, "Queue Status: Q[%d] %s\n", + ac, + (atomic_read(&trans_pcie->queue_stop_count[ac]) > 0) + ? "stopped" : "awake"); + iwl_wake_queue(trans, &trans_pcie->txq[txq_id]); + } +} + +const struct iwl_trans_ops trans_ops_pcie; + +static struct iwl_trans *iwl_trans_pcie_alloc(struct iwl_shared *shrd) +{ + struct iwl_trans *iwl_trans = kzalloc(sizeof(struct iwl_trans) + + sizeof(struct iwl_trans_pcie), + GFP_KERNEL); + if (iwl_trans) { + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(iwl_trans); + iwl_trans->ops = &trans_ops_pcie; + iwl_trans->shrd = shrd; + trans_pcie->trans = iwl_trans; + spin_lock_init(&iwl_trans->hcmd_lock); + } + + return iwl_trans; +} + +static void iwl_trans_pcie_stop_queue(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + iwl_stop_queue(trans, &trans_pcie->txq[txq_id]); +} + +#define IWL_FLUSH_WAIT_MS 2000 + +static int iwl_trans_pcie_wait_tx_queue_empty(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + 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 < hw_params(trans).max_txq_num; cnt++) { + if (cnt == trans->shrd->cmd_queue) + continue; + txq = &trans_pcie->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(trans, "fail to flush all tx fifo queues\n"); + ret = -ETIMEDOUT; + break; + } + } + return ret; +} + +/* + * On every watchdog tick we check (latest) time stamp. If it does not + * change during timeout period and queue is not empty we reset firmware. + */ +static int iwl_trans_pcie_check_stuck_queue(struct iwl_trans *trans, int cnt) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq = &trans_pcie->txq[cnt]; + struct iwl_queue *q = &txq->q; + unsigned long timeout; + + if (q->read_ptr == q->write_ptr) { + txq->time_stamp = jiffies; + return 0; + } + + timeout = txq->time_stamp + + msecs_to_jiffies(hw_params(trans).wd_timeout); + + if (time_after(jiffies, timeout)) { + IWL_ERR(trans, "Queue %d stuck for %u ms.\n", q->id, + hw_params(trans).wd_timeout); + IWL_ERR(trans, "Current read_ptr %d write_ptr %d\n", + q->read_ptr, q->write_ptr); + return 1; + } + + return 0; +} + +static const char *get_fh_string(int cmd) +{ + switch (cmd) { + IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); + IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); + IWL_CMD(FH_RSCSR_CHNL0_WPTR); + IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); + IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); + IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); + IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); + IWL_CMD(FH_TSSR_TX_STATUS_REG); + IWL_CMD(FH_TSSR_TX_ERROR_REG); + default: + return "UNKNOWN"; + } +} + +int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display) +{ + int i; +#ifdef CONFIG_IWLWIFI_DEBUG + int pos = 0; + size_t bufsz = 0; +#endif + static const u32 fh_tbl[] = { + FH_RSCSR_CHNL0_STTS_WPTR_REG, + FH_RSCSR_CHNL0_RBDCB_BASE_REG, + FH_RSCSR_CHNL0_WPTR, + FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_MEM_RSSR_SHARED_CTRL_REG, + FH_MEM_RSSR_RX_STATUS_REG, + FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, + FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_ERROR_REG + }; +#ifdef CONFIG_IWLWIFI_DEBUG + if (display) { + bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + pos += scnprintf(*buf + pos, bufsz - pos, + "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + pos += scnprintf(*buf + pos, bufsz - pos, + " %34s: 0X%08x\n", + get_fh_string(fh_tbl[i]), + iwl_read_direct32(bus(trans), fh_tbl[i])); + } + return pos; + } +#endif + IWL_ERR(trans, "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + IWL_ERR(trans, " %34s: 0X%08x\n", + get_fh_string(fh_tbl[i]), + iwl_read_direct32(bus(trans), fh_tbl[i])); + } + return 0; +} + +static const char *get_csr_string(int cmd) +{ + switch (cmd) { + IWL_CMD(CSR_HW_IF_CONFIG_REG); + IWL_CMD(CSR_INT_COALESCING); + IWL_CMD(CSR_INT); + IWL_CMD(CSR_INT_MASK); + IWL_CMD(CSR_FH_INT_STATUS); + IWL_CMD(CSR_GPIO_IN); + IWL_CMD(CSR_RESET); + IWL_CMD(CSR_GP_CNTRL); + IWL_CMD(CSR_HW_REV); + IWL_CMD(CSR_EEPROM_REG); + IWL_CMD(CSR_EEPROM_GP); + IWL_CMD(CSR_OTP_GP_REG); + IWL_CMD(CSR_GIO_REG); + IWL_CMD(CSR_GP_UCODE_REG); + IWL_CMD(CSR_GP_DRIVER_REG); + IWL_CMD(CSR_UCODE_DRV_GP1); + IWL_CMD(CSR_UCODE_DRV_GP2); + IWL_CMD(CSR_LED_REG); + IWL_CMD(CSR_DRAM_INT_TBL_REG); + IWL_CMD(CSR_GIO_CHICKEN_BITS); + IWL_CMD(CSR_ANA_PLL_CFG); + IWL_CMD(CSR_HW_REV_WA_REG); + IWL_CMD(CSR_DBG_HPET_MEM_REG); + default: + return "UNKNOWN"; + } +} + +void iwl_dump_csr(struct iwl_trans *trans) +{ + int i; + static const u32 csr_tbl[] = { + CSR_HW_IF_CONFIG_REG, + CSR_INT_COALESCING, + CSR_INT, + CSR_INT_MASK, + CSR_FH_INT_STATUS, + CSR_GPIO_IN, + CSR_RESET, + CSR_GP_CNTRL, + CSR_HW_REV, + CSR_EEPROM_REG, + CSR_EEPROM_GP, + CSR_OTP_GP_REG, + CSR_GIO_REG, + CSR_GP_UCODE_REG, + CSR_GP_DRIVER_REG, + CSR_UCODE_DRV_GP1, + CSR_UCODE_DRV_GP2, + CSR_LED_REG, + CSR_DRAM_INT_TBL_REG, + CSR_GIO_CHICKEN_BITS, + CSR_ANA_PLL_CFG, + CSR_HW_REV_WA_REG, + CSR_DBG_HPET_MEM_REG + }; + IWL_ERR(trans, "CSR values:\n"); + IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is " + "CSR_INT_PERIODIC_REG)\n"); + for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) { + IWL_ERR(trans, " %25s: 0X%08x\n", + get_csr_string(csr_tbl[i]), + iwl_read32(bus(trans), csr_tbl[i])); + } +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +/* create and remove of files */ +#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, trans, \ + &iwl_dbgfs_##name##_ops)) \ + return -ENOMEM; \ +} while (0) + +/* file operation */ +#define DEBUGFS_READ_FUNC(name) \ +static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos); + +#define DEBUGFS_WRITE_FUNC(name) \ +static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos); + + +static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +#define DEBUGFS_READ_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name); \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = iwl_dbgfs_##name##_read, \ + .open = iwl_dbgfs_open_file_generic, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_WRITE_FILE_OPS(name) \ + DEBUGFS_WRITE_FUNC(name); \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = iwl_dbgfs_##name##_write, \ + .open = iwl_dbgfs_open_file_generic, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name); \ + DEBUGFS_WRITE_FUNC(name); \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = iwl_dbgfs_##name##_write, \ + .read = iwl_dbgfs_##name##_read, \ + .open = iwl_dbgfs_open_file_generic, \ + .llseek = generic_file_llseek, \ +}; + +static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_tx_queue *txq; + struct iwl_queue *q; + char *buf; + int pos = 0; + int cnt; + int ret; + const size_t bufsz = sizeof(char) * 64 * hw_params(trans).max_txq_num; + + if (!trans_pcie->txq) { + IWL_ERR(trans, "txq not ready\n"); + return -EAGAIN; + } + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (cnt = 0; cnt < hw_params(trans).max_txq_num; cnt++) { + txq = &trans_pcie->txq[cnt]; + q = &txq->q; + pos += scnprintf(buf + pos, bufsz - pos, + "hwq %.2d: read=%u write=%u stop=%d" + " swq_id=%#.2x (ac %d/hwq %d)\n", + cnt, q->read_ptr, q->write_ptr, + !!test_bit(cnt, trans_pcie->queue_stopped), + txq->swq_id, txq->swq_id & 3, + (txq->swq_id >> 2) & 0x1f); + if (cnt >= 4) + continue; + /* for the ACs, display the stop count too */ + pos += scnprintf(buf + pos, bufsz - pos, + " stop-count: %d\n", + atomic_read(&trans_pcie->queue_stop_count[cnt])); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rx_queue *rxq = &trans_pcie->rxq; + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", + rxq->read); + pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", + rxq->write); + pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", + rxq->free_count); + if (rxq->rb_stts) { + pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", + le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); + } else { + pos += scnprintf(buf + pos, bufsz - pos, + "closed_rb_num: Not Allocated\n"); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_log_event_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + char *buf; + int pos = 0; + ssize_t ret = -ENOMEM; + + ret = pos = iwl_dump_nic_event_log(trans, true, &buf, true); + if (buf) { + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + } + return ret; +} + +static ssize_t iwl_dbgfs_log_event_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + u32 event_log_flag; + char buf[8]; + int buf_size; + + 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", &event_log_flag) != 1) + return -EFAULT; + if (event_log_flag == 1) + iwl_dump_nic_event_log(trans, true, NULL, false); + + return count; +} + +static ssize_t iwl_dbgfs_interrupt_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + struct isr_statistics *isr_stats = &trans_pcie->isr_stats; + + int pos = 0; + char *buf; + int bufsz = 24 * 64; /* 24 items * 64 char per item */ + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(trans, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += scnprintf(buf + pos, bufsz - pos, + "Interrupt Statistics Report:\n"); + + pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", + isr_stats->hw); + pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", + isr_stats->sw); + if (isr_stats->sw || isr_stats->hw) { + pos += scnprintf(buf + pos, bufsz - pos, + "\tLast Restarting Code: 0x%X\n", + isr_stats->err_code); + } +#ifdef CONFIG_IWLWIFI_DEBUG + pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", + isr_stats->sch); + pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", + isr_stats->alive); +#endif + pos += scnprintf(buf + pos, bufsz - pos, + "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill); + + pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", + isr_stats->ctkill); + + pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", + isr_stats->wakeup); + + pos += scnprintf(buf + pos, bufsz - pos, + "Rx command responses:\t\t %u\n", isr_stats->rx); + + pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", + isr_stats->tx); + + pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", + isr_stats->unhandled); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_dbgfs_interrupt_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + struct isr_statistics *isr_stats = &trans_pcie->isr_stats; + + char buf[8]; + int buf_size; + u32 reset_flag; + + 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, "%x", &reset_flag) != 1) + return -EFAULT; + if (reset_flag == 0) + memset(isr_stats, 0, sizeof(*isr_stats)); + + return count; +} + +static ssize_t iwl_dbgfs_csr_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + char buf[8]; + int buf_size; + int csr; + + 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", &csr) != 1) + return -EFAULT; + + iwl_dump_csr(trans); + + return count; +} + +static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_trans *trans = file->private_data; + char *buf; + int pos = 0; + ssize_t ret = -EFAULT; + + ret = pos = iwl_dump_fh(trans, &buf, true); + if (buf) { + ret = simple_read_from_buffer(user_buf, + count, ppos, buf, pos); + kfree(buf); + } + + return ret; +} + +DEBUGFS_READ_WRITE_FILE_OPS(log_event); +DEBUGFS_READ_WRITE_FILE_OPS(interrupt); +DEBUGFS_READ_FILE_OPS(fh_reg); +DEBUGFS_READ_FILE_OPS(rx_queue); +DEBUGFS_READ_FILE_OPS(tx_queue); +DEBUGFS_WRITE_FILE_OPS(csr); + +/* + * Create the debugfs files and directories + * + */ +static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, + struct dentry *dir) +{ + DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR); + DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR); + DEBUGFS_ADD_FILE(log_event, dir, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(csr, dir, S_IWUSR); + DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR); + return 0; +} +#else +static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, + struct dentry *dir) +{ return 0; } + +#endif /*CONFIG_IWLWIFI_DEBUGFS */ + +const struct iwl_trans_ops trans_ops_pcie = { + .alloc = iwl_trans_pcie_alloc, + .request_irq = iwl_trans_pcie_request_irq, + .start_device = iwl_trans_pcie_start_device, + .prepare_card_hw = iwl_trans_pcie_prepare_card_hw, + .stop_device = iwl_trans_pcie_stop_device, + + .tx_start = iwl_trans_pcie_tx_start, + .wake_any_queue = iwl_trans_pcie_wake_any_queue, + + .send_cmd = iwl_trans_pcie_send_cmd, + + .tx = iwl_trans_pcie_tx, + .reclaim = iwl_trans_pcie_reclaim, + + .tx_agg_disable = iwl_trans_pcie_tx_agg_disable, + .tx_agg_alloc = iwl_trans_pcie_tx_agg_alloc, + .tx_agg_setup = iwl_trans_pcie_tx_agg_setup, + + .kick_nic = iwl_trans_pcie_kick_nic, + + .free = iwl_trans_pcie_free, + .stop_queue = iwl_trans_pcie_stop_queue, + + .dbgfs_register = iwl_trans_pcie_dbgfs_register, + + .wait_tx_queue_empty = iwl_trans_pcie_wait_tx_queue_empty, + .check_stuck_queue = iwl_trans_pcie_check_stuck_queue, + +#ifdef CONFIG_PM_SLEEP + .suspend = iwl_trans_pcie_suspend, + .resume = iwl_trans_pcie_resume, +#endif +}; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/iwlwifi/iwl-trans.c index b2e89077..1b20c4f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans.c @@ -60,1928 +60,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *****************************************************************************/ -#include <linux/interrupt.h> -#include <linux/debugfs.h> -#include <linux/bitops.h> -#include <linux/gfp.h> #include "iwl-trans.h" -#include "iwl-trans-int-pcie.h" -#include "iwl-csr.h" -#include "iwl-prph.h" -#include "iwl-shared.h" -#include "iwl-eeprom.h" -#include "iwl-agn-hw.h" -static int iwl_trans_rx_alloc(struct iwl_trans *trans) +int iwl_trans_send_cmd_pdu(struct iwl_trans *trans, u8 id, + u32 flags, u16 len, const void *data) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct device *dev = bus(trans)->dev; - - memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq)); - - spin_lock_init(&rxq->lock); - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - - if (WARN_ON(rxq->bd || rxq->rb_stts)) - return -EINVAL; - - /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ - rxq->bd = dma_alloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - &rxq->bd_dma, GFP_KERNEL); - if (!rxq->bd) - goto err_bd; - memset(rxq->bd, 0, sizeof(__le32) * RX_QUEUE_SIZE); - - /*Allocate the driver's pointer to receive buffer status */ - rxq->rb_stts = dma_alloc_coherent(dev, sizeof(*rxq->rb_stts), - &rxq->rb_stts_dma, GFP_KERNEL); - if (!rxq->rb_stts) - goto err_rb_stts; - memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts)); - - return 0; - -err_rb_stts: - dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); - memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); - rxq->bd = NULL; -err_bd: - return -ENOMEM; -} - -static void iwl_trans_rxq_free_rx_bufs(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - int i; - - /* Fill the rx_used queue with _all_ of the Rx buffers */ - for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { - /* In the reset function, these buffers may have been allocated - * to an SKB, so we need to unmap and free potential storage */ - if (rxq->pool[i].page != NULL) { - dma_unmap_page(bus(trans)->dev, rxq->pool[i].page_dma, - PAGE_SIZE << hw_params(trans).rx_page_order, - DMA_FROM_DEVICE); - __free_pages(rxq->pool[i].page, - hw_params(trans).rx_page_order); - rxq->pool[i].page = NULL; - } - list_add_tail(&rxq->pool[i].list, &rxq->rx_used); - } -} - -static void iwl_trans_rx_hw_init(struct iwl_trans *trans, - struct iwl_rx_queue *rxq) -{ - u32 rb_size; - const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ - u32 rb_timeout = 0; /* FIXME: RX_RB_TIMEOUT for all devices? */ - - rb_timeout = RX_RB_TIMEOUT; - - if (iwlagn_mod_params.amsdu_size_8K) - rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; - else - rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; - - /* Stop Rx DMA */ - iwl_write_direct32(bus(trans), FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); - - /* Reset driver's Rx queue write index */ - iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); - - /* Tell device where to find RBD circular buffer in DRAM */ - iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_RBDCB_BASE_REG, - (u32)(rxq->bd_dma >> 8)); - - /* Tell device where in DRAM to update its Rx status */ - iwl_write_direct32(bus(trans), FH_RSCSR_CHNL0_STTS_WPTR_REG, - rxq->rb_stts_dma >> 4); - - /* Enable Rx DMA - * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in - * the credit mechanism in 5000 HW RX FIFO - * Direct rx interrupts to hosts - * Rx buffer size 4 or 8k - * RB timeout 0x10 - * 256 RBDs - */ - iwl_write_direct32(bus(trans), FH_MEM_RCSR_CHNL0_CONFIG_REG, - FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | - FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | - FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | - FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | - rb_size| - (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| - (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); - - /* Set interrupt coalescing timer to default (2048 usecs) */ - iwl_write8(bus(trans), CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); -} - -static int iwl_rx_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - - int i, err; - unsigned long flags; - - if (!rxq->bd) { - err = iwl_trans_rx_alloc(trans); - if (err) - return err; - } - - spin_lock_irqsave(&rxq->lock, flags); - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - - iwl_trans_rxq_free_rx_bufs(trans); - - for (i = 0; i < RX_QUEUE_SIZE; i++) - rxq->queue[i] = NULL; - - /* Set us so that we have processed and used all buffers, but have - * not restocked the Rx queue with fresh buffers */ - rxq->read = rxq->write = 0; - rxq->write_actual = 0; - rxq->free_count = 0; - spin_unlock_irqrestore(&rxq->lock, flags); - - iwlagn_rx_replenish(trans); - - iwl_trans_rx_hw_init(trans, rxq); - - spin_lock_irqsave(&trans->shrd->lock, flags); - rxq->need_update = 1; - iwl_rx_queue_update_write_ptr(trans, rxq); - spin_unlock_irqrestore(&trans->shrd->lock, flags); - - return 0; -} - -static void iwl_trans_pcie_rx_free(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - - unsigned long flags; - - /*if rxq->bd is NULL, it means that nothing has been allocated, - * exit now */ - if (!rxq->bd) { - IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); - return; - } - - spin_lock_irqsave(&rxq->lock, flags); - iwl_trans_rxq_free_rx_bufs(trans); - spin_unlock_irqrestore(&rxq->lock, flags); - - dma_free_coherent(bus(trans)->dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); - memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); - rxq->bd = NULL; - - if (rxq->rb_stts) - dma_free_coherent(bus(trans)->dev, - sizeof(struct iwl_rb_status), - rxq->rb_stts, rxq->rb_stts_dma); - else - IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); - memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma)); - rxq->rb_stts = NULL; -} - -static int iwl_trans_rx_stop(struct iwl_trans *trans) -{ - - /* stop Rx DMA */ - iwl_write_direct32(bus(trans), FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); - return iwl_poll_direct_bit(bus(trans), FH_MEM_RSSR_RX_STATUS_REG, - FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); -} - -static inline int iwlagn_alloc_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr, size_t size) -{ - if (WARN_ON(ptr->addr)) - return -EINVAL; - - ptr->addr = dma_alloc_coherent(bus(trans)->dev, size, - &ptr->dma, GFP_KERNEL); - if (!ptr->addr) - return -ENOMEM; - ptr->size = size; - return 0; -} - -static inline void iwlagn_free_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr) -{ - if (unlikely(!ptr->addr)) - return; - - dma_free_coherent(bus(trans)->dev, ptr->size, ptr->addr, ptr->dma); - memset(ptr, 0, sizeof(*ptr)); -} - -static int iwl_trans_txq_alloc(struct iwl_trans *trans, - struct iwl_tx_queue *txq, int slots_num, - u32 txq_id) -{ - size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; - int i; - - if (WARN_ON(txq->meta || txq->cmd || txq->skbs || txq->tfds)) - return -EINVAL; - - txq->q.n_window = slots_num; - - txq->meta = kzalloc(sizeof(txq->meta[0]) * slots_num, - GFP_KERNEL); - txq->cmd = kzalloc(sizeof(txq->cmd[0]) * slots_num, - GFP_KERNEL); - - if (!txq->meta || !txq->cmd) - goto error; - - if (txq_id == trans->shrd->cmd_queue) - for (i = 0; i < slots_num; i++) { - txq->cmd[i] = kmalloc(sizeof(struct iwl_device_cmd), - GFP_KERNEL); - if (!txq->cmd[i]) - goto error; - } - - /* Alloc driver data array and TFD circular buffer */ - /* Driver private data, only for Tx (not command) queues, - * not shared with device. */ - if (txq_id != trans->shrd->cmd_queue) { - txq->skbs = kzalloc(sizeof(txq->skbs[0]) * - TFD_QUEUE_SIZE_MAX, GFP_KERNEL); - if (!txq->skbs) { - IWL_ERR(trans, "kmalloc for auxiliary BD " - "structures failed\n"); - goto error; - } - } else { - txq->skbs = NULL; - } - - /* Circular buffer of transmit frame descriptors (TFDs), - * shared with device */ - txq->tfds = dma_alloc_coherent(bus(trans)->dev, tfd_sz, - &txq->q.dma_addr, GFP_KERNEL); - if (!txq->tfds) { - IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz); - goto error; - } - txq->q.id = txq_id; - - return 0; -error: - kfree(txq->skbs); - txq->skbs = NULL; - /* since txq->cmd has been zeroed, - * all non allocated cmd[i] will be NULL */ - if (txq->cmd && txq_id == trans->shrd->cmd_queue) - for (i = 0; i < slots_num; i++) - kfree(txq->cmd[i]); - kfree(txq->meta); - kfree(txq->cmd); - txq->meta = NULL; - txq->cmd = NULL; - - return -ENOMEM; - -} - -static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq, - int slots_num, u32 txq_id) -{ - int ret; - - txq->need_update = 0; - memset(txq->meta, 0, sizeof(txq->meta[0]) * slots_num); - - /* - * For the default queues 0-3, set up the swq_id - * already -- all others need to get one later - * (if they need one at all). - */ - if (txq_id < 4) - iwl_set_swq_id(txq, txq_id, txq_id); - - /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise - * iwl_queue_inc_wrap and iwl_queue_dec_wrap are broken. */ - BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); - - /* Initialize queue's high/low-water marks, and head/tail indexes */ - ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num, - txq_id); - if (ret) - return ret; - - /* - * Tell nic where to find circular buffer of Tx Frame Descriptors for - * given Tx queue, and enable the DMA channel used for that queue. - * Circular buffer (TFD queue in DRAM) physical base address */ - iwl_write_direct32(bus(trans), FH_MEM_CBBC_QUEUE(txq_id), - txq->q.dma_addr >> 8); - - return 0; -} - -/** - * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's - */ -static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - struct iwl_queue *q = &txq->q; - - if (!q->n_bd) - return; - - while (q->write_ptr != q->read_ptr) { - /* The read_ptr needs to bound by q->n_window */ - iwlagn_txq_free_tfd(trans, txq, get_cmd_index(q, q->read_ptr)); - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); - } -} - -/** - * iwl_tx_queue_free - Deallocate DMA queue. - * @txq: Transmit queue to deallocate. - * - * Empty queue by removing and destroying all BD's. - * Free all buffers. - * 0-fill, but do not free "txq" descriptor structure. - */ -static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - struct device *dev = bus(trans)->dev; - int i; - if (WARN_ON(!txq)) - return; - - iwl_tx_queue_unmap(trans, txq_id); - - /* De-alloc array of command/tx buffers */ - - if (txq_id == trans->shrd->cmd_queue) - for (i = 0; i < txq->q.n_window; i++) - kfree(txq->cmd[i]); - - /* De-alloc circular buffer of TFDs */ - if (txq->q.n_bd) { - dma_free_coherent(dev, sizeof(struct iwl_tfd) * - txq->q.n_bd, txq->tfds, txq->q.dma_addr); - memset(&txq->q.dma_addr, 0, sizeof(txq->q.dma_addr)); - } - - /* De-alloc array of per-TFD driver data */ - kfree(txq->skbs); - txq->skbs = NULL; - - /* deallocate arrays */ - kfree(txq->cmd); - kfree(txq->meta); - txq->cmd = NULL; - txq->meta = NULL; - - /* 0-fill queue descriptor structure */ - memset(txq, 0, sizeof(*txq)); -} - -/** - * iwl_trans_tx_free - Free TXQ Context - * - * Destroy all TX DMA queues and structures - */ -static void iwl_trans_pcie_tx_free(struct iwl_trans *trans) -{ - int txq_id; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - /* Tx queues */ - if (trans_pcie->txq) { - for (txq_id = 0; - txq_id < hw_params(trans).max_txq_num; txq_id++) - iwl_tx_queue_free(trans, txq_id); - } - - kfree(trans_pcie->txq); - trans_pcie->txq = NULL; - - iwlagn_free_dma_ptr(trans, &trans_pcie->kw); - - iwlagn_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls); -} - -/** - * iwl_trans_tx_alloc - allocate TX context - * Allocate all Tx DMA structures and initialize them - * - * @param priv - * @return error code - */ -static int iwl_trans_tx_alloc(struct iwl_trans *trans) -{ - int ret; - int txq_id, slots_num; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - u16 scd_bc_tbls_size = hw_params(trans).max_txq_num * - sizeof(struct iwlagn_scd_bc_tbl); - - /*It is not allowed to alloc twice, so warn when this happens. - * We cannot rely on the previous allocation, so free and fail */ - if (WARN_ON(trans_pcie->txq)) { - ret = -EINVAL; - goto error; - } - - ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls, - scd_bc_tbls_size); - if (ret) { - IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); - goto error; - } - - /* Alloc keep-warm buffer */ - ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE); - if (ret) { - IWL_ERR(trans, "Keep Warm allocation failed\n"); - goto error; - } - - trans_pcie->txq = kzalloc(sizeof(struct iwl_tx_queue) * - hw_params(trans).max_txq_num, GFP_KERNEL); - if (!trans_pcie->txq) { - IWL_ERR(trans, "Not enough memory for txq\n"); - ret = ENOMEM; - goto error; - } - - /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++) { - slots_num = (txq_id == trans->shrd->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_trans_txq_alloc(trans, &trans_pcie->txq[txq_id], - slots_num, txq_id); - if (ret) { - IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); - goto error; - } - } - - return 0; - -error: - iwl_trans_pcie_tx_free(trans); - - return ret; -} -static int iwl_tx_init(struct iwl_trans *trans) -{ - int ret; - int txq_id, slots_num; - unsigned long flags; - bool alloc = false; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (!trans_pcie->txq) { - ret = iwl_trans_tx_alloc(trans); - if (ret) - goto error; - alloc = true; - } - - spin_lock_irqsave(&trans->shrd->lock, flags); - - /* Turn off all Tx DMA fifos */ - iwl_write_prph(bus(trans), SCD_TXFACT, 0); - - /* Tell NIC where to find the "keep warm" buffer */ - iwl_write_direct32(bus(trans), FH_KW_MEM_ADDR_REG, - trans_pcie->kw.dma >> 4); - - spin_unlock_irqrestore(&trans->shrd->lock, flags); - - /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++) { - slots_num = (txq_id == trans->shrd->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_trans_txq_init(trans, &trans_pcie->txq[txq_id], - slots_num, txq_id); - if (ret) { - IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); - goto error; - } - } - - return 0; -error: - /*Upon error, free only if we allocated something */ - if (alloc) - iwl_trans_pcie_tx_free(trans); - return ret; -} - -static void iwl_set_pwr_vmain(struct iwl_trans *trans) -{ -/* - * (for documentation purposes) - * to set power to V_AUX, do: - - if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) - iwl_set_bits_mask_prph(bus(trans), APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VAUX, - ~APMG_PS_CTRL_MSK_PWR_SRC); - */ - - iwl_set_bits_mask_prph(bus(trans), APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, - ~APMG_PS_CTRL_MSK_PWR_SRC); -} - -static int iwl_nic_init(struct iwl_trans *trans) -{ - unsigned long flags; - - /* nic_init */ - spin_lock_irqsave(&trans->shrd->lock, flags); - iwl_apm_init(priv(trans)); - - /* Set interrupt coalescing calibration timer to default (512 usecs) */ - iwl_write8(bus(trans), CSR_INT_COALESCING, - IWL_HOST_INT_CALIB_TIMEOUT_DEF); - - spin_unlock_irqrestore(&trans->shrd->lock, flags); - - iwl_set_pwr_vmain(trans); - - iwl_nic_config(priv(trans)); - - /* Allocate the RX queue, or reset if it is already allocated */ - iwl_rx_init(trans); - - /* Allocate or reset and init all Tx and Command queues */ - if (iwl_tx_init(trans)) - return -ENOMEM; - - if (hw_params(trans).shadow_reg_enable) { - /* enable shadow regs in HW */ - iwl_set_bit(bus(trans), CSR_MAC_SHADOW_REG_CTRL, - 0x800FFFFF); - } - - set_bit(STATUS_INIT, &trans->shrd->status); - - return 0; -} - -#define HW_READY_TIMEOUT (50) - -/* Note: returns poll_bit return value, which is >= 0 if success */ -static int iwl_set_hw_ready(struct iwl_trans *trans) -{ - int ret; - - iwl_set_bit(bus(trans), CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); - - /* See if we got it */ - ret = iwl_poll_bit(bus(trans), CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, - HW_READY_TIMEOUT); - - IWL_DEBUG_INFO(trans, "hardware%s ready\n", ret < 0 ? " not" : ""); - return ret; -} - -/* Note: returns standard 0/-ERROR code */ -static int iwl_trans_pcie_prepare_card_hw(struct iwl_trans *trans) -{ - int ret; - - IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); - - ret = iwl_set_hw_ready(trans); - if (ret >= 0) - return 0; - - /* If HW is not ready, prepare the conditions to check again */ - iwl_set_bit(bus(trans), CSR_HW_IF_CONFIG_REG, - CSR_HW_IF_CONFIG_REG_PREPARE); - - ret = iwl_poll_bit(bus(trans), CSR_HW_IF_CONFIG_REG, - ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, - CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); - - if (ret < 0) - return ret; - - /* HW should be ready by now, check again. */ - ret = iwl_set_hw_ready(trans); - if (ret >= 0) - return 0; - return ret; -} - -#define IWL_AC_UNSET -1 - -struct queue_to_fifo_ac { - s8 fifo, ac; -}; - -static const struct queue_to_fifo_ac iwlagn_default_queue_to_tx_fifo[] = { - { IWL_TX_FIFO_VO, IEEE80211_AC_VO, }, - { IWL_TX_FIFO_VI, IEEE80211_AC_VI, }, - { IWL_TX_FIFO_BE, IEEE80211_AC_BE, }, - { IWL_TX_FIFO_BK, IEEE80211_AC_BK, }, - { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, }, - { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, - { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, - { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, - { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, - { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, - { IWL_TX_FIFO_UNUSED, IWL_AC_UNSET, }, -}; - -static const struct queue_to_fifo_ac iwlagn_ipan_queue_to_tx_fifo[] = { - { IWL_TX_FIFO_VO, IEEE80211_AC_VO, }, - { IWL_TX_FIFO_VI, IEEE80211_AC_VI, }, - { IWL_TX_FIFO_BE, IEEE80211_AC_BE, }, - { IWL_TX_FIFO_BK, IEEE80211_AC_BK, }, - { IWL_TX_FIFO_BK_IPAN, IEEE80211_AC_BK, }, - { IWL_TX_FIFO_BE_IPAN, IEEE80211_AC_BE, }, - { IWL_TX_FIFO_VI_IPAN, IEEE80211_AC_VI, }, - { IWL_TX_FIFO_VO_IPAN, IEEE80211_AC_VO, }, - { IWL_TX_FIFO_BE_IPAN, 2, }, - { IWLAGN_CMD_FIFO_NUM, IWL_AC_UNSET, }, - { IWL_TX_FIFO_AUX, IWL_AC_UNSET, }, -}; - -static const u8 iwlagn_bss_ac_to_fifo[] = { - IWL_TX_FIFO_VO, - IWL_TX_FIFO_VI, - IWL_TX_FIFO_BE, - IWL_TX_FIFO_BK, -}; -static const u8 iwlagn_bss_ac_to_queue[] = { - 0, 1, 2, 3, -}; -static const u8 iwlagn_pan_ac_to_fifo[] = { - IWL_TX_FIFO_VO_IPAN, - IWL_TX_FIFO_VI_IPAN, - IWL_TX_FIFO_BE_IPAN, - IWL_TX_FIFO_BK_IPAN, -}; -static const u8 iwlagn_pan_ac_to_queue[] = { - 7, 6, 5, 4, -}; - -static int iwl_trans_pcie_start_device(struct iwl_trans *trans) -{ - int ret; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - - trans->shrd->ucode_owner = IWL_OWNERSHIP_DRIVER; - trans_pcie->ac_to_queue[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_queue; - trans_pcie->ac_to_queue[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_queue; - - trans_pcie->ac_to_fifo[IWL_RXON_CTX_BSS] = iwlagn_bss_ac_to_fifo; - trans_pcie->ac_to_fifo[IWL_RXON_CTX_PAN] = iwlagn_pan_ac_to_fifo; - - trans_pcie->mcast_queue[IWL_RXON_CTX_BSS] = 0; - trans_pcie->mcast_queue[IWL_RXON_CTX_PAN] = IWL_IPAN_MCAST_QUEUE; - - if ((hw_params(trans).sku & EEPROM_SKU_CAP_AMT_ENABLE) && - iwl_trans_pcie_prepare_card_hw(trans)) { - IWL_WARN(trans, "Exit HW not ready\n"); - return -EIO; - } - - /* If platform's RF_KILL switch is NOT set to KILL */ - if (iwl_read32(bus(trans), CSR_GP_CNTRL) & - CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) - clear_bit(STATUS_RF_KILL_HW, &trans->shrd->status); - else - set_bit(STATUS_RF_KILL_HW, &trans->shrd->status); - - if (iwl_is_rfkill(trans->shrd)) { - iwl_set_hw_rfkill_state(priv(trans), true); - iwl_enable_interrupts(trans); - return -ERFKILL; - } - - iwl_write32(bus(trans), CSR_INT, 0xFFFFFFFF); - - ret = iwl_nic_init(trans); - if (ret) { - IWL_ERR(trans, "Unable to init nic\n"); - return ret; - } - - /* make sure rfkill handshake bits are cleared */ - iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); - - /* clear (again), then enable host interrupts */ - iwl_write32(bus(trans), CSR_INT, 0xFFFFFFFF); - iwl_enable_interrupts(trans); - - /* really make sure rfkill handshake bits are cleared */ - iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - iwl_write32(bus(trans), CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); - - return 0; -} - -/* - * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask - * must be called under priv->shrd->lock and mac access - */ -static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask) -{ - iwl_write_prph(bus(trans), SCD_TXFACT, mask); -} - -static void iwl_trans_pcie_tx_start(struct iwl_trans *trans) -{ - const struct queue_to_fifo_ac *queue_to_fifo; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - u32 a; - unsigned long flags; - int i, chan; - u32 reg_val; - - spin_lock_irqsave(&trans->shrd->lock, flags); - - trans_pcie->scd_base_addr = - iwl_read_prph(bus(trans), SCD_SRAM_BASE_ADDR); - a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND; - /* reset conext data memory */ - for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND; - a += 4) - iwl_write_targ_mem(bus(trans), a, 0); - /* reset tx status memory */ - for (; a < trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_UPPER_BOUND; - a += 4) - iwl_write_targ_mem(bus(trans), a, 0); - for (; a < trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE(hw_params(trans).max_txq_num); - a += 4) - iwl_write_targ_mem(bus(trans), a, 0); - - iwl_write_prph(bus(trans), SCD_DRAM_BASE_ADDR, - trans_pcie->scd_bc_tbls.dma >> 10); - - /* Enable DMA channel */ - for (chan = 0; chan < FH_TCSR_CHNL_NUM ; chan++) - iwl_write_direct32(bus(trans), FH_TCSR_CHNL_TX_CONFIG_REG(chan), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); - - /* Update FH chicken bits */ - reg_val = iwl_read_direct32(bus(trans), FH_TX_CHICKEN_BITS_REG); - iwl_write_direct32(bus(trans), FH_TX_CHICKEN_BITS_REG, - reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); - - iwl_write_prph(bus(trans), SCD_QUEUECHAIN_SEL, - SCD_QUEUECHAIN_SEL_ALL(trans)); - iwl_write_prph(bus(trans), SCD_AGGR_SEL, 0); - - /* initiate the queues */ - for (i = 0; i < hw_params(trans).max_txq_num; i++) { - iwl_write_prph(bus(trans), SCD_QUEUE_RDPTR(i), 0); - iwl_write_direct32(bus(trans), HBUS_TARG_WRPTR, 0 | (i << 8)); - iwl_write_targ_mem(bus(trans), trans_pcie->scd_base_addr + - SCD_CONTEXT_QUEUE_OFFSET(i), 0); - iwl_write_targ_mem(bus(trans), trans_pcie->scd_base_addr + - SCD_CONTEXT_QUEUE_OFFSET(i) + - sizeof(u32), - ((SCD_WIN_SIZE << - SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & - SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | - ((SCD_FRAME_LIMIT << - SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); - } - - iwl_write_prph(bus(trans), SCD_INTERRUPT_MASK, - IWL_MASK(0, hw_params(trans).max_txq_num)); - - /* Activate all Tx DMA/FIFO channels */ - iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); - - /* map queues to FIFOs */ - if (trans->shrd->valid_contexts != BIT(IWL_RXON_CTX_BSS)) - queue_to_fifo = iwlagn_ipan_queue_to_tx_fifo; - else - queue_to_fifo = iwlagn_default_queue_to_tx_fifo; - - iwl_trans_set_wr_ptrs(trans, trans->shrd->cmd_queue, 0); - - /* make sure all queue are not stopped */ - memset(&trans_pcie->queue_stopped[0], 0, - sizeof(trans_pcie->queue_stopped)); - for (i = 0; i < 4; i++) - atomic_set(&trans_pcie->queue_stop_count[i], 0); - - /* reset to 0 to enable all the queue first */ - trans_pcie->txq_ctx_active_msk = 0; - - BUILD_BUG_ON(ARRAY_SIZE(iwlagn_default_queue_to_tx_fifo) < - IWLAGN_FIRST_AMPDU_QUEUE); - BUILD_BUG_ON(ARRAY_SIZE(iwlagn_ipan_queue_to_tx_fifo) < - IWLAGN_FIRST_AMPDU_QUEUE); - - for (i = 0; i < IWLAGN_FIRST_AMPDU_QUEUE; i++) { - int fifo = queue_to_fifo[i].fifo; - int ac = queue_to_fifo[i].ac; - - iwl_txq_ctx_activate(trans_pcie, i); - - if (fifo == IWL_TX_FIFO_UNUSED) - continue; - - if (ac != IWL_AC_UNSET) - iwl_set_swq_id(&trans_pcie->txq[i], ac, i); - iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[i], - fifo, 0); - } - - spin_unlock_irqrestore(&trans->shrd->lock, flags); - - /* Enable L1-Active */ - iwl_clear_bits_prph(bus(trans), APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); -} - -/** - * iwlagn_txq_ctx_stop - Stop all Tx DMA channels - */ -static int iwl_trans_tx_stop(struct iwl_trans *trans) -{ - int ch, txq_id; - unsigned long flags; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - /* Turn off all Tx DMA fifos */ - spin_lock_irqsave(&trans->shrd->lock, flags); - - iwl_trans_txq_set_sched(trans, 0); - - /* Stop each Tx DMA channel, and wait for it to be idle */ - for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { - iwl_write_direct32(bus(trans), - FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); - if (iwl_poll_direct_bit(bus(trans), FH_TSSR_TX_STATUS_REG, - FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), - 1000)) - IWL_ERR(trans, "Failing on timeout while stopping" - " DMA channel %d [0x%08x]", ch, - iwl_read_direct32(bus(trans), - FH_TSSR_TX_STATUS_REG)); - } - spin_unlock_irqrestore(&trans->shrd->lock, flags); - - if (!trans_pcie->txq) { - IWL_WARN(trans, "Stopping tx queues that aren't allocated..."); - return 0; - } - - /* Unmap DMA from host system and free skb's */ - for (txq_id = 0; txq_id < hw_params(trans).max_txq_num; txq_id++) - iwl_tx_queue_unmap(trans, txq_id); - - return 0; -} - -static void iwl_trans_pcie_disable_sync_irq(struct iwl_trans *trans) -{ - unsigned long flags; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - - spin_lock_irqsave(&trans->shrd->lock, flags); - iwl_disable_interrupts(trans); - spin_unlock_irqrestore(&trans->shrd->lock, flags); - - /* wait to make sure we flush pending tasklet*/ - synchronize_irq(bus(trans)->irq); - tasklet_kill(&trans_pcie->irq_tasklet); -} - -static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) -{ - /* stop and reset the on-board processor */ - iwl_write32(bus(trans), CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); - - /* tell the device to stop sending interrupts */ - iwl_trans_pcie_disable_sync_irq(trans); - - /* device going down, Stop using ICT table */ - iwl_disable_ict(trans); - - /* - * If a HW restart happens during firmware loading, - * then the firmware loading might call this function - * and later it might be called again due to the - * restart. So don't process again if the device is - * already dead. - */ - if (test_bit(STATUS_DEVICE_ENABLED, &trans->shrd->status)) { - iwl_trans_tx_stop(trans); - iwl_trans_rx_stop(trans); - - /* Power-down device's busmaster DMA clocks */ - iwl_write_prph(bus(trans), APMG_CLK_DIS_REG, - APMG_CLK_VAL_DMA_CLK_RQT); - udelay(5); - } - - /* Make sure (redundant) we've released our request to stay awake */ - iwl_clear_bit(bus(trans), CSR_GP_CNTRL, - CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); - - /* Stop the device, and put it in low power state */ - iwl_apm_stop(priv(trans)); -} - -static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_device_cmd *dev_cmd, u8 ctx, u8 sta_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct iwl_tx_cmd *tx_cmd = &dev_cmd->cmd.tx; - struct iwl_cmd_meta *out_meta; - struct iwl_tx_queue *txq; - struct iwl_queue *q; - - dma_addr_t phys_addr = 0; - dma_addr_t txcmd_phys; - dma_addr_t scratch_phys; - u16 len, firstlen, secondlen; - u16 seq_number = 0; - u8 wait_write_ptr = 0; - u8 txq_id; - u8 tid = 0; - bool is_agg = false; - __le16 fc = hdr->frame_control; - u8 hdr_len = ieee80211_hdrlen(fc); - - /* - * Send this frame after DTIM -- there's a special queue - * reserved for this for contexts that support AP mode. - */ - if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { - txq_id = trans_pcie->mcast_queue[ctx]; - - /* - * The microcode will clear the more data - * bit in the last frame it transmits. - */ - hdr->frame_control |= - cpu_to_le16(IEEE80211_FCTL_MOREDATA); - } else if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) - txq_id = IWL_AUX_QUEUE; - else - txq_id = - trans_pcie->ac_to_queue[ctx][skb_get_queue_mapping(skb)]; - - if (ieee80211_is_data_qos(fc)) { - u8 *qc = NULL; - struct iwl_tid_data *tid_data; - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - tid_data = &trans->shrd->tid_data[sta_id][tid]; - - if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) - return -1; - - seq_number = tid_data->seq_number; - seq_number &= IEEE80211_SCTL_SEQ; - hdr->seq_ctrl = hdr->seq_ctrl & - cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(seq_number); - seq_number += 0x10; - /* aggregation is on for this <sta,tid> */ - if (info->flags & IEEE80211_TX_CTL_AMPDU && - tid_data->agg.state == IWL_AGG_ON) { - txq_id = tid_data->agg.txq_id; - is_agg = true; - } - } - - txq = &trans_pcie->txq[txq_id]; - q = &txq->q; - - /* Set up driver data for this TFD */ - txq->skbs[q->write_ptr] = skb; - txq->cmd[q->write_ptr] = dev_cmd; - - dev_cmd->hdr.cmd = REPLY_TX; - dev_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | - INDEX_TO_SEQ(q->write_ptr))); - - /* Set up first empty entry in queue's array of Tx/cmd buffers */ - out_meta = &txq->meta[q->write_ptr]; - - /* - * Use the first empty entry in this queue's command buffer array - * to contain the Tx command and MAC header concatenated together - * (payload data will be in another buffer). - * Size of this varies, due to varying MAC header length. - * If end is not dword aligned, we'll have 2 extra bytes at the end - * of the MAC header (device reads on dword boundaries). - * We'll tell device about this padding later. - */ - len = sizeof(struct iwl_tx_cmd) + - sizeof(struct iwl_cmd_header) + hdr_len; - firstlen = (len + 3) & ~3; - - /* Tell NIC about any 2-byte padding after MAC header */ - if (firstlen != len) - tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; - - /* Physical address of this Tx command's header (not MAC header!), - * within command buffer array. */ - txcmd_phys = dma_map_single(bus(trans)->dev, - &dev_cmd->hdr, firstlen, - DMA_BIDIRECTIONAL); - if (unlikely(dma_mapping_error(bus(trans)->dev, txcmd_phys))) - return -1; - dma_unmap_addr_set(out_meta, mapping, txcmd_phys); - dma_unmap_len_set(out_meta, len, firstlen); - - if (!ieee80211_has_morefrags(fc)) { - txq->need_update = 1; - } else { - wait_write_ptr = 1; - txq->need_update = 0; - } - - /* Set up TFD's 2nd entry to point directly to remainder of skb, - * if any (802.11 null frames have no payload). */ - secondlen = skb->len - hdr_len; - if (secondlen > 0) { - phys_addr = dma_map_single(bus(trans)->dev, skb->data + hdr_len, - secondlen, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(bus(trans)->dev, phys_addr))) { - dma_unmap_single(bus(trans)->dev, - dma_unmap_addr(out_meta, mapping), - dma_unmap_len(out_meta, len), - DMA_BIDIRECTIONAL); - return -1; - } - } - - /* Attach buffers to TFD */ - iwlagn_txq_attach_buf_to_tfd(trans, txq, txcmd_phys, firstlen, 1); - if (secondlen > 0) - iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, - secondlen, 0); - - scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + - offsetof(struct iwl_tx_cmd, scratch); - - /* take back ownership of DMA buffer to enable update */ - dma_sync_single_for_cpu(bus(trans)->dev, txcmd_phys, firstlen, - DMA_BIDIRECTIONAL); - tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); - tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); - - IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n", - le16_to_cpu(dev_cmd->hdr.sequence)); - IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); - iwl_print_hex_dump(trans, IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd)); - iwl_print_hex_dump(trans, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len); - - /* Set up entry for this TFD in Tx byte-count array */ - if (is_agg) - iwl_trans_txq_update_byte_cnt_tbl(trans, txq, - le16_to_cpu(tx_cmd->len)); - - dma_sync_single_for_device(bus(trans)->dev, txcmd_phys, firstlen, - DMA_BIDIRECTIONAL); - - trace_iwlwifi_dev_tx(priv(trans), - &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr], - sizeof(struct iwl_tfd), - &dev_cmd->hdr, firstlen, - skb->data + hdr_len, secondlen); - - /* Tell device the write index *just past* this latest filled TFD */ - q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); - iwl_txq_update_write_ptr(trans, txq); - - if (ieee80211_is_data_qos(fc)) { - trans->shrd->tid_data[sta_id][tid].tfds_in_queue++; - if (!ieee80211_has_morefrags(fc)) - trans->shrd->tid_data[sta_id][tid].seq_number = - seq_number; - } - - /* - * At this point the frame is "transmitted" successfully - * and we will get a TX status notification eventually, - * regardless of the value of ret. "ret" only indicates - * whether or not we should update the write pointer. - */ - if (iwl_queue_space(q) < q->high_mark) { - if (wait_write_ptr) { - txq->need_update = 1; - iwl_txq_update_write_ptr(trans, txq); - } else { - iwl_stop_queue(trans, txq); - } - } - return 0; -} - -static void iwl_trans_pcie_kick_nic(struct iwl_trans *trans) -{ - /* Remove all resets to allow NIC to operate */ - iwl_write32(bus(trans), CSR_RESET, 0); -} - -static int iwl_trans_pcie_request_irq(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - int err; - - trans_pcie->inta_mask = CSR_INI_SET_MASK; - - tasklet_init(&trans_pcie->irq_tasklet, (void (*)(unsigned long)) - iwl_irq_tasklet, (unsigned long)trans); - - iwl_alloc_isr_ict(trans); - - err = request_irq(bus(trans)->irq, iwl_isr_ict, IRQF_SHARED, - DRV_NAME, trans); - if (err) { - IWL_ERR(trans, "Error allocating IRQ %d\n", bus(trans)->irq); - iwl_free_isr_ict(trans); - return err; - } - - INIT_WORK(&trans_pcie->rx_replenish, iwl_bg_rx_replenish); - return 0; -} - -static int iwlagn_txq_check_empty(struct iwl_trans *trans, - int sta_id, u8 tid, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_queue *q = &trans_pcie->txq[txq_id].q; - struct iwl_tid_data *tid_data = &trans->shrd->tid_data[sta_id][tid]; - - lockdep_assert_held(&trans->shrd->sta_lock); - - switch (trans->shrd->tid_data[sta_id][tid].agg.state) { - case IWL_EMPTYING_HW_QUEUE_DELBA: - /* We are reclaiming the last packet of the */ - /* aggregated HW queue */ - if ((txq_id == tid_data->agg.txq_id) && - (q->read_ptr == q->write_ptr)) { - IWL_DEBUG_HT(trans, - "HW queue empty: continue DELBA flow\n"); - iwl_trans_pcie_txq_agg_disable(trans, txq_id); - tid_data->agg.state = IWL_AGG_OFF; - iwl_stop_tx_ba_trans_ready(priv(trans), - NUM_IWL_RXON_CTX, - sta_id, tid); - iwl_wake_queue(trans, &trans_pcie->txq[txq_id]); - } - break; - case IWL_EMPTYING_HW_QUEUE_ADDBA: - /* We are reclaiming the last packet of the queue */ - if (tid_data->tfds_in_queue == 0) { - IWL_DEBUG_HT(trans, - "HW queue empty: continue ADDBA flow\n"); - tid_data->agg.state = IWL_AGG_ON; - iwl_start_tx_ba_trans_ready(priv(trans), - NUM_IWL_RXON_CTX, - sta_id, tid); - } - break; - } - - return 0; -} - -static void iwl_free_tfds_in_queue(struct iwl_trans *trans, - int sta_id, int tid, int freed) -{ - lockdep_assert_held(&trans->shrd->sta_lock); - - if (trans->shrd->tid_data[sta_id][tid].tfds_in_queue >= freed) - trans->shrd->tid_data[sta_id][tid].tfds_in_queue -= freed; - else { - IWL_DEBUG_TX(trans, "free more than tfds_in_queue (%u:%d)\n", - trans->shrd->tid_data[sta_id][tid].tfds_in_queue, - freed); - trans->shrd->tid_data[sta_id][tid].tfds_in_queue = 0; - } -} - -static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid, - int txq_id, int ssn, u32 status, - struct sk_buff_head *skbs) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - /* n_bd is usually 256 => n_bd - 1 = 0xff */ - int tfd_num = ssn & (txq->q.n_bd - 1); - int freed = 0; - u8 agg_state; - bool cond; - - txq->time_stamp = jiffies; - - if (txq->sched_retry) { - agg_state = - trans->shrd->tid_data[txq->sta_id][txq->tid].agg.state; - cond = (agg_state != IWL_EMPTYING_HW_QUEUE_DELBA); - } else { - cond = (status != TX_STATUS_FAIL_PASSIVE_NO_RX); - } - - if (txq->q.read_ptr != tfd_num) { - IWL_DEBUG_TX_REPLY(trans, "Retry scheduler reclaim " - "scd_ssn=%d idx=%d txq=%d swq=%d\n", - ssn , tfd_num, txq_id, txq->swq_id); - freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs); - if (iwl_queue_space(&txq->q) > txq->q.low_mark && cond) - iwl_wake_queue(trans, txq); - } - - iwl_free_tfds_in_queue(trans, sta_id, tid, freed); - iwlagn_txq_check_empty(trans, sta_id, tid, txq_id); -} - -static void iwl_trans_pcie_free(struct iwl_trans *trans) -{ - iwl_trans_pcie_tx_free(trans); - iwl_trans_pcie_rx_free(trans); - free_irq(bus(trans)->irq, trans); - iwl_free_isr_ict(trans); - trans->shrd->trans = NULL; - kfree(trans); -} - -#ifdef CONFIG_PM - -static int iwl_trans_pcie_suspend(struct iwl_trans *trans) -{ - /* - * This function is called when system goes into suspend state - * mac80211 will call iwl_mac_stop() from the mac80211 suspend function - * first but since iwl_mac_stop() has no knowledge of who the caller is, - * it will not call apm_ops.stop() to stop the DMA operation. - * Calling apm_ops.stop here to make sure we stop the DMA. - * - * But of course ... if we have configured WoWLAN then we did other - * things already :-) - */ - if (!trans->shrd->wowlan) - iwl_apm_stop(priv(trans)); - - return 0; -} - -static int iwl_trans_pcie_resume(struct iwl_trans *trans) -{ - bool hw_rfkill = false; - - iwl_enable_interrupts(trans); - - if (!(iwl_read32(bus(trans), CSR_GP_CNTRL) & - CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) - hw_rfkill = true; - - if (hw_rfkill) - set_bit(STATUS_RF_KILL_HW, &trans->shrd->status); - else - clear_bit(STATUS_RF_KILL_HW, &trans->shrd->status); - - iwl_set_hw_rfkill_state(priv(trans), hw_rfkill); - - return 0; -} -#else /* CONFIG_PM */ -static int iwl_trans_pcie_suspend(struct iwl_trans *trans) -{ return 0; } - -static int iwl_trans_pcie_resume(struct iwl_trans *trans) -{ return 0; } - -#endif /* CONFIG_PM */ - -static void iwl_trans_pcie_wake_any_queue(struct iwl_trans *trans, - u8 ctx) -{ - u8 ac, txq_id; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - - for (ac = 0; ac < AC_NUM; ac++) { - txq_id = trans_pcie->ac_to_queue[ctx][ac]; - IWL_DEBUG_INFO(trans, "Queue Status: Q[%d] %s\n", - ac, - (atomic_read(&trans_pcie->queue_stop_count[ac]) > 0) - ? "stopped" : "awake"); - iwl_wake_queue(trans, &trans_pcie->txq[txq_id]); - } -} - -const struct iwl_trans_ops trans_ops_pcie; - -static struct iwl_trans *iwl_trans_pcie_alloc(struct iwl_shared *shrd) -{ - struct iwl_trans *iwl_trans = kzalloc(sizeof(struct iwl_trans) + - sizeof(struct iwl_trans_pcie), - GFP_KERNEL); - if (iwl_trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(iwl_trans); - iwl_trans->ops = &trans_ops_pcie; - iwl_trans->shrd = shrd; - trans_pcie->trans = iwl_trans; - spin_lock_init(&iwl_trans->hcmd_lock); - } - - return iwl_trans; -} - -static void iwl_trans_pcie_stop_queue(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - iwl_stop_queue(trans, &trans_pcie->txq[txq_id]); -} - -#define IWL_FLUSH_WAIT_MS 2000 - -static int iwl_trans_pcie_wait_tx_queue_empty(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - 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 < hw_params(trans).max_txq_num; cnt++) { - if (cnt == trans->shrd->cmd_queue) - continue; - txq = &trans_pcie->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(trans, "fail to flush all tx fifo queues\n"); - ret = -ETIMEDOUT; - break; - } - } - return ret; -} - -/* - * On every watchdog tick we check (latest) time stamp. If it does not - * change during timeout period and queue is not empty we reset firmware. - */ -static int iwl_trans_pcie_check_stuck_queue(struct iwl_trans *trans, int cnt) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[cnt]; - struct iwl_queue *q = &txq->q; - unsigned long timeout; - - if (q->read_ptr == q->write_ptr) { - txq->time_stamp = jiffies; - return 0; - } - - timeout = txq->time_stamp + - msecs_to_jiffies(hw_params(trans).wd_timeout); - - if (time_after(jiffies, timeout)) { - IWL_ERR(trans, "Queue %d stuck for %u ms.\n", q->id, - hw_params(trans).wd_timeout); - IWL_ERR(trans, "Current read_ptr %d write_ptr %d\n", - q->read_ptr, q->write_ptr); - return 1; - } - - return 0; -} - -static const char *get_fh_string(int cmd) -{ - switch (cmd) { - IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); - IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); - IWL_CMD(FH_RSCSR_CHNL0_WPTR); - IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); - IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); - IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); - IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); - IWL_CMD(FH_TSSR_TX_STATUS_REG); - IWL_CMD(FH_TSSR_TX_ERROR_REG); - default: - return "UNKNOWN"; - } -} - -int iwl_dump_fh(struct iwl_trans *trans, char **buf, bool display) -{ - int i; -#ifdef CONFIG_IWLWIFI_DEBUG - int pos = 0; - size_t bufsz = 0; -#endif - static const u32 fh_tbl[] = { - FH_RSCSR_CHNL0_STTS_WPTR_REG, - FH_RSCSR_CHNL0_RBDCB_BASE_REG, - FH_RSCSR_CHNL0_WPTR, - FH_MEM_RCSR_CHNL0_CONFIG_REG, - FH_MEM_RSSR_SHARED_CTRL_REG, - FH_MEM_RSSR_RX_STATUS_REG, - FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, - FH_TSSR_TX_STATUS_REG, - FH_TSSR_TX_ERROR_REG - }; -#ifdef CONFIG_IWLWIFI_DEBUG - if (display) { - bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; - *buf = kmalloc(bufsz, GFP_KERNEL); - if (!*buf) - return -ENOMEM; - pos += scnprintf(*buf + pos, bufsz - pos, - "FH register values:\n"); - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { - pos += scnprintf(*buf + pos, bufsz - pos, - " %34s: 0X%08x\n", - get_fh_string(fh_tbl[i]), - iwl_read_direct32(bus(trans), fh_tbl[i])); - } - return pos; - } -#endif - IWL_ERR(trans, "FH register values:\n"); - for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { - IWL_ERR(trans, " %34s: 0X%08x\n", - get_fh_string(fh_tbl[i]), - iwl_read_direct32(bus(trans), fh_tbl[i])); - } - return 0; -} - -static const char *get_csr_string(int cmd) -{ - switch (cmd) { - IWL_CMD(CSR_HW_IF_CONFIG_REG); - IWL_CMD(CSR_INT_COALESCING); - IWL_CMD(CSR_INT); - IWL_CMD(CSR_INT_MASK); - IWL_CMD(CSR_FH_INT_STATUS); - IWL_CMD(CSR_GPIO_IN); - IWL_CMD(CSR_RESET); - IWL_CMD(CSR_GP_CNTRL); - IWL_CMD(CSR_HW_REV); - IWL_CMD(CSR_EEPROM_REG); - IWL_CMD(CSR_EEPROM_GP); - IWL_CMD(CSR_OTP_GP_REG); - IWL_CMD(CSR_GIO_REG); - IWL_CMD(CSR_GP_UCODE_REG); - IWL_CMD(CSR_GP_DRIVER_REG); - IWL_CMD(CSR_UCODE_DRV_GP1); - IWL_CMD(CSR_UCODE_DRV_GP2); - IWL_CMD(CSR_LED_REG); - IWL_CMD(CSR_DRAM_INT_TBL_REG); - IWL_CMD(CSR_GIO_CHICKEN_BITS); - IWL_CMD(CSR_ANA_PLL_CFG); - IWL_CMD(CSR_HW_REV_WA_REG); - IWL_CMD(CSR_DBG_HPET_MEM_REG); - default: - return "UNKNOWN"; - } -} - -void iwl_dump_csr(struct iwl_trans *trans) -{ - int i; - static const u32 csr_tbl[] = { - CSR_HW_IF_CONFIG_REG, - CSR_INT_COALESCING, - CSR_INT, - CSR_INT_MASK, - CSR_FH_INT_STATUS, - CSR_GPIO_IN, - CSR_RESET, - CSR_GP_CNTRL, - CSR_HW_REV, - CSR_EEPROM_REG, - CSR_EEPROM_GP, - CSR_OTP_GP_REG, - CSR_GIO_REG, - CSR_GP_UCODE_REG, - CSR_GP_DRIVER_REG, - CSR_UCODE_DRV_GP1, - CSR_UCODE_DRV_GP2, - CSR_LED_REG, - CSR_DRAM_INT_TBL_REG, - CSR_GIO_CHICKEN_BITS, - CSR_ANA_PLL_CFG, - CSR_HW_REV_WA_REG, - CSR_DBG_HPET_MEM_REG + struct iwl_host_cmd cmd = { + .id = id, + .len = { len, }, + .data = { data, }, + .flags = flags, }; - IWL_ERR(trans, "CSR values:\n"); - IWL_ERR(trans, "(2nd byte of CSR_INT_COALESCING is " - "CSR_INT_PERIODIC_REG)\n"); - for (i = 0; i < ARRAY_SIZE(csr_tbl); i++) { - IWL_ERR(trans, " %25s: 0X%08x\n", - get_csr_string(csr_tbl[i]), - iwl_read32(bus(trans), csr_tbl[i])); - } -} - -#ifdef CONFIG_IWLWIFI_DEBUGFS -/* create and remove of files */ -#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ - if (!debugfs_create_file(#name, mode, parent, trans, \ - &iwl_dbgfs_##name##_ops)) \ - return -ENOMEM; \ -} while (0) - -/* file operation */ -#define DEBUGFS_READ_FUNC(name) \ -static ssize_t iwl_dbgfs_##name##_read(struct file *file, \ - char __user *user_buf, \ - size_t count, loff_t *ppos); - -#define DEBUGFS_WRITE_FUNC(name) \ -static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ - const char __user *user_buf, \ - size_t count, loff_t *ppos); - - -static int iwl_dbgfs_open_file_generic(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -#define DEBUGFS_READ_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .read = iwl_dbgfs_##name##_read, \ - .open = iwl_dbgfs_open_file_generic, \ - .llseek = generic_file_llseek, \ -}; -#define DEBUGFS_WRITE_FILE_OPS(name) \ - DEBUGFS_WRITE_FUNC(name); \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .open = iwl_dbgfs_open_file_generic, \ - .llseek = generic_file_llseek, \ -}; - -#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ - DEBUGFS_READ_FUNC(name); \ - DEBUGFS_WRITE_FUNC(name); \ -static const struct file_operations iwl_dbgfs_##name##_ops = { \ - .write = iwl_dbgfs_##name##_write, \ - .read = iwl_dbgfs_##name##_read, \ - .open = iwl_dbgfs_open_file_generic, \ - .llseek = generic_file_llseek, \ -}; - -static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq; - struct iwl_queue *q; - char *buf; - int pos = 0; - int cnt; - int ret; - const size_t bufsz = sizeof(char) * 64 * hw_params(trans).max_txq_num; - - if (!trans_pcie->txq) { - IWL_ERR(trans, "txq not ready\n"); - return -EAGAIN; - } - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (cnt = 0; cnt < hw_params(trans).max_txq_num; cnt++) { - txq = &trans_pcie->txq[cnt]; - q = &txq->q; - pos += scnprintf(buf + pos, bufsz - pos, - "hwq %.2d: read=%u write=%u stop=%d" - " swq_id=%#.2x (ac %d/hwq %d)\n", - cnt, q->read_ptr, q->write_ptr, - !!test_bit(cnt, trans_pcie->queue_stopped), - txq->swq_id, txq->swq_id & 3, - (txq->swq_id >> 2) & 0x1f); - if (cnt >= 4) - continue; - /* for the ACs, display the stop count too */ - pos += scnprintf(buf + pos, bufsz - pos, - " stop-count: %d\n", - atomic_read(&trans_pcie->queue_stop_count[cnt])); - } - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; + return iwl_trans_send_cmd(trans, &cmd); } - -static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - char buf[256]; - int pos = 0; - const size_t bufsz = sizeof(buf); - - pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", - rxq->read); - pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", - rxq->write); - pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", - rxq->free_count); - if (rxq->rb_stts) { - pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", - le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); - } else { - pos += scnprintf(buf + pos, bufsz - pos, - "closed_rb_num: Not Allocated\n"); - } - return simple_read_from_buffer(user_buf, count, ppos, buf, pos); -} - -static ssize_t iwl_dbgfs_log_event_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - char *buf; - int pos = 0; - ssize_t ret = -ENOMEM; - - ret = pos = iwl_dump_nic_event_log(trans, true, &buf, true); - if (buf) { - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - } - return ret; -} - -static ssize_t iwl_dbgfs_log_event_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - u32 event_log_flag; - char buf[8]; - int buf_size; - - 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", &event_log_flag) != 1) - return -EFAULT; - if (event_log_flag == 1) - iwl_dump_nic_event_log(trans, true, NULL, false); - - return count; -} - -static ssize_t iwl_dbgfs_interrupt_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) { - - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - struct isr_statistics *isr_stats = &trans_pcie->isr_stats; - - int pos = 0; - char *buf; - int bufsz = 24 * 64; /* 24 items * 64 char per item */ - ssize_t ret; - - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) { - IWL_ERR(trans, "Can not allocate Buffer\n"); - return -ENOMEM; - } - - pos += scnprintf(buf + pos, bufsz - pos, - "Interrupt Statistics Report:\n"); - - pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", - isr_stats->hw); - pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", - isr_stats->sw); - if (isr_stats->sw || isr_stats->hw) { - pos += scnprintf(buf + pos, bufsz - pos, - "\tLast Restarting Code: 0x%X\n", - isr_stats->err_code); - } -#ifdef CONFIG_IWLWIFI_DEBUG - pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", - isr_stats->sch); - pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", - isr_stats->alive); -#endif - pos += scnprintf(buf + pos, bufsz - pos, - "HW RF KILL switch toggled:\t %u\n", isr_stats->rfkill); - - pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", - isr_stats->ctkill); - - pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", - isr_stats->wakeup); - - pos += scnprintf(buf + pos, bufsz - pos, - "Rx command responses:\t\t %u\n", isr_stats->rx); - - pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", - isr_stats->tx); - - pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", - isr_stats->unhandled); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); - kfree(buf); - return ret; -} - -static ssize_t iwl_dbgfs_interrupt_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - struct isr_statistics *isr_stats = &trans_pcie->isr_stats; - - char buf[8]; - int buf_size; - u32 reset_flag; - - 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, "%x", &reset_flag) != 1) - return -EFAULT; - if (reset_flag == 0) - memset(isr_stats, 0, sizeof(*isr_stats)); - - return count; -} - -static ssize_t iwl_dbgfs_csr_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - char buf[8]; - int buf_size; - int csr; - - 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", &csr) != 1) - return -EFAULT; - - iwl_dump_csr(trans); - - return count; -} - -static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct iwl_trans *trans = file->private_data; - char *buf; - int pos = 0; - ssize_t ret = -EFAULT; - - ret = pos = iwl_dump_fh(trans, &buf, true); - if (buf) { - ret = simple_read_from_buffer(user_buf, - count, ppos, buf, pos); - kfree(buf); - } - - return ret; -} - -DEBUGFS_READ_WRITE_FILE_OPS(log_event); -DEBUGFS_READ_WRITE_FILE_OPS(interrupt); -DEBUGFS_READ_FILE_OPS(fh_reg); -DEBUGFS_READ_FILE_OPS(rx_queue); -DEBUGFS_READ_FILE_OPS(tx_queue); -DEBUGFS_WRITE_FILE_OPS(csr); - -/* - * Create the debugfs files and directories - * - */ -static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) -{ - DEBUGFS_ADD_FILE(rx_queue, dir, S_IRUSR); - DEBUGFS_ADD_FILE(tx_queue, dir, S_IRUSR); - DEBUGFS_ADD_FILE(log_event, dir, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(interrupt, dir, S_IWUSR | S_IRUSR); - DEBUGFS_ADD_FILE(csr, dir, S_IWUSR); - DEBUGFS_ADD_FILE(fh_reg, dir, S_IRUSR); - return 0; -} -#else -static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, - struct dentry *dir) -{ return 0; } - -#endif /*CONFIG_IWLWIFI_DEBUGFS */ - -const struct iwl_trans_ops trans_ops_pcie = { - .alloc = iwl_trans_pcie_alloc, - .request_irq = iwl_trans_pcie_request_irq, - .start_device = iwl_trans_pcie_start_device, - .prepare_card_hw = iwl_trans_pcie_prepare_card_hw, - .stop_device = iwl_trans_pcie_stop_device, - - .tx_start = iwl_trans_pcie_tx_start, - .wake_any_queue = iwl_trans_pcie_wake_any_queue, - - .send_cmd = iwl_trans_pcie_send_cmd, - .send_cmd_pdu = iwl_trans_pcie_send_cmd_pdu, - - .tx = iwl_trans_pcie_tx, - .reclaim = iwl_trans_pcie_reclaim, - - .tx_agg_disable = iwl_trans_pcie_tx_agg_disable, - .tx_agg_alloc = iwl_trans_pcie_tx_agg_alloc, - .tx_agg_setup = iwl_trans_pcie_tx_agg_setup, - - .kick_nic = iwl_trans_pcie_kick_nic, - - .free = iwl_trans_pcie_free, - .stop_queue = iwl_trans_pcie_stop_queue, - - .dbgfs_register = iwl_trans_pcie_dbgfs_register, - - .wait_tx_queue_empty = iwl_trans_pcie_wait_tx_queue_empty, - .check_stuck_queue = iwl_trans_pcie_check_stuck_queue, - - .suspend = iwl_trans_pcie_suspend, - .resume = iwl_trans_pcie_resume, -}; - diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 71a6fb0..c592312 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -97,15 +97,7 @@ enum { */ struct iwl_device_cmd { struct iwl_cmd_header hdr; /* uCode API */ - union { - u32 flags; - u8 val8; - u16 val16; - u32 val32; - struct iwl_tx_cmd tx; - struct iwl6000_channel_switch_cmd chswitch; - u8 payload[DEF_CMD_PAYLOAD_SIZE]; - } __packed cmd; + u8 payload[DEF_CMD_PAYLOAD_SIZE]; } __packed; #define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) @@ -120,6 +112,8 @@ enum iwl_hcmd_dataflag { * struct iwl_host_cmd - Host command to the uCode * @data: array of chunks that composes the data of the host command * @reply_page: pointer to the page that holds the response to the host command + * @handler_status: return value of the handler of the command + * (put in setup_rx_handlers) - valid for SYNC mode only * @callback: * @flags: can be CMD_* note CMD_WANT_SKB is incompatible withe CMD_ASYNC * @len: array of the lenths of the chunks in data @@ -129,9 +123,8 @@ enum iwl_hcmd_dataflag { struct iwl_host_cmd { const void *data[IWL_MAX_CMD_TFDS]; unsigned long reply_page; - void (*callback)(struct iwl_shared *shrd, - struct iwl_device_cmd *cmd, - struct iwl_rx_packet *pkt); + int handler_status; + u32 flags; u16 len[IWL_MAX_CMD_TFDS]; u8 dataflags[IWL_MAX_CMD_TFDS]; @@ -151,7 +144,6 @@ struct iwl_host_cmd { * @wake_any_queue: wake all the queues of a specfic context IWL_RXON_CTX_* * @stop_device:stops the whole device (embedded CPU put to reset) * @send_cmd:send a host command - * @send_cmd_pdu:send a host command: flags can be CMD_* * @tx: send an skb * @reclaim: free packet until ssn. Returns a list of freed packets. * @tx_agg_alloc: allocate resources for a TX BA session @@ -178,14 +170,14 @@ struct iwl_trans_ops { void (*stop_device)(struct iwl_trans *trans); void (*tx_start)(struct iwl_trans *trans); - void (*wake_any_queue)(struct iwl_trans *trans, u8 ctx); + void (*wake_any_queue)(struct iwl_trans *trans, + enum iwl_rxon_context_id ctx); int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd); - int (*send_cmd_pdu)(struct iwl_trans *trans, u8 id, u32 flags, u16 len, - const void *data); int (*tx)(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_device_cmd *dev_cmd, u8 ctx, u8 sta_id); + struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx, + u8 sta_id); void (*reclaim)(struct iwl_trans *trans, int sta_id, int tid, int txq_id, int ssn, u32 status, struct sk_buff_head *skbs); @@ -209,9 +201,10 @@ struct iwl_trans_ops { int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); int (*check_stuck_queue)(struct iwl_trans *trans, int q); int (*wait_tx_queue_empty)(struct iwl_trans *trans); - +#ifdef CONFIG_PM_SLEEP int (*suspend)(struct iwl_trans *trans); int (*resume)(struct iwl_trans *trans); +#endif }; /** @@ -255,7 +248,8 @@ static inline void iwl_trans_tx_start(struct iwl_trans *trans) trans->ops->tx_start(trans); } -static inline void iwl_trans_wake_any_queue(struct iwl_trans *trans, u8 ctx) +static inline void iwl_trans_wake_any_queue(struct iwl_trans *trans, + enum iwl_rxon_context_id ctx) { trans->ops->wake_any_queue(trans, ctx); } @@ -267,14 +261,12 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans, return trans->ops->send_cmd(trans, cmd); } -static inline int iwl_trans_send_cmd_pdu(struct iwl_trans *trans, u8 id, - u32 flags, u16 len, const void *data) -{ - return trans->ops->send_cmd_pdu(trans, id, flags, len, data); -} +int iwl_trans_send_cmd_pdu(struct iwl_trans *trans, u8 id, + u32 flags, u16 len, const void *data); static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_device_cmd *dev_cmd, u8 ctx, u8 sta_id) + struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx, + u8 sta_id) { return trans->ops->tx(trans, skb, dev_cmd, ctx, sta_id); } @@ -339,6 +331,7 @@ static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans, return trans->ops->dbgfs_register(trans, dir); } +#ifdef CONFIG_PM_SLEEP static inline int iwl_trans_suspend(struct iwl_trans *trans) { return trans->ops->suspend(trans); @@ -348,6 +341,7 @@ static inline int iwl_trans_resume(struct iwl_trans *trans) { return trans->ops->resume(trans); } +#endif /***************************************************** * Transport layers implementations diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 85b3169..610bfce 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -695,7 +695,7 @@ static void lbs_scan_worker(struct work_struct *work) tlv = scan_cmd->tlvbuffer; /* add SSID TLV */ - if (priv->scan_req->n_ssids) + if (priv->scan_req->n_ssids && priv->scan_req->ssids[0].ssid_len > 0) tlv += lbs_add_ssid_tlv(tlv, priv->scan_req->ssids[0].ssid, priv->scan_req->ssids[0].ssid_len); @@ -736,7 +736,6 @@ static void lbs_scan_worker(struct work_struct *work) cfg80211_scan_done(priv->scan_req, false); priv->scan_req = NULL; - priv->last_scan = jiffies; } /* Restart network */ @@ -1302,24 +1301,26 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev, lbs_deb_enter(LBS_DEB_CFG80211); if (!sme->bssid) { - /* Run a scan if one isn't in-progress already and if the last - * scan was done more than 2 seconds ago. - */ - if (priv->scan_req == NULL && - time_after(jiffies, priv->last_scan + (2 * HZ))) { - struct cfg80211_scan_request *creq; + struct cfg80211_scan_request *creq; - creq = _new_connect_scan_req(wiphy, sme); - if (!creq) { - ret = -EINVAL; - goto done; - } + /* + * Scan for the requested network after waiting for existing + * scans to finish. + */ + lbs_deb_assoc("assoc: waiting for existing scans\n"); + wait_event_interruptible_timeout(priv->scan_q, + (priv->scan_req == NULL), + (15 * HZ)); - lbs_deb_assoc("assoc: scanning for compatible AP\n"); - _internal_start_scan(priv, true, creq); + creq = _new_connect_scan_req(wiphy, sme); + if (!creq) { + ret = -EINVAL; + goto done; } - /* Wait for any in-progress scan to complete */ + lbs_deb_assoc("assoc: scanning for compatible AP\n"); + _internal_start_scan(priv, true, creq); + lbs_deb_assoc("assoc: waiting for scan to complete\n"); wait_event_interruptible_timeout(priv->scan_q, (priv->scan_req == NULL), diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index b9ff0dc..fb3e40b 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h @@ -179,7 +179,6 @@ struct lbs_private { wait_queue_head_t scan_q; /* Whether the scan was initiated internally and not by cfg80211 */ bool internal_scan; - unsigned long last_scan; }; extern struct cmd_confirm_sleep confirm_sleep; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 6fd53e4..0ddcdca 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -768,6 +768,7 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) struct mwifiex_bss_info bss_info; int ie_len; u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)]; + enum ieee80211_band band; if (mwifiex_get_bss_info(priv, &bss_info)) return -1; @@ -780,9 +781,10 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) bss_info.ssid.ssid_len); ie_len = ie_buf[1] + sizeof(struct ieee_types_header); + band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); chan = __ieee80211_get_channel(priv->wdev->wiphy, ieee80211_channel_to_frequency(bss_info.bss_chan, - priv->curr_bss_params.band)); + band)); cfg80211_inform_bss(priv->wdev->wiphy, chan, bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, @@ -1219,6 +1221,9 @@ int mwifiex_register_cfg80211(struct net_device *dev, u8 *mac, /* We are using custom domains */ wdev->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; + /* Reserve space for bss band information */ + wdev->wiphy->bss_priv_size = sizeof(u8); + wdev->wiphy->reg_notifier = mwifiex_reg_notifier; /* Set struct mwifiex_private pointer in wiphy_priv */ diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index b5352af..d12e25d 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -90,6 +90,9 @@ mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter, cmd_node->data_buf = NULL; cmd_node->wait_q_enabled = false; + if (cmd_node->cmd_skb) + skb_trim(cmd_node->cmd_skb, 0); + if (cmd_node->resp_skb) { dev_kfree_skb_any(cmd_node->resp_skb); cmd_node->resp_skb = NULL; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index e6b6c0c..1e80132 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -958,7 +958,7 @@ int mwifiex_get_bss_info(struct mwifiex_private *, int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, u8 *bssid, s32 rssi, u8 *ie_buf, size_t ie_len, u16 beacon_period, - u16 cap_info_bitmap, + u16 cap_info_bitmap, u8 band, struct mwifiex_bssdescriptor *bss_desc); int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, struct mwifiex_bssdescriptor *bss_entry, diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 8d8588d..ca37619 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -532,7 +532,7 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv, sband = priv->wdev->wiphy->bands[band]; - for (i = 0; (i < sband->n_channels) ; i++, chan_idx++) { + for (i = 0; (i < sband->n_channels) ; i++) { ch = &sband->channels[i]; if (ch->flags & IEEE80211_CHAN_DISABLED) continue; @@ -563,6 +563,7 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv, scan_chan_list[chan_idx].chan_scan_mode_bitmap |= MWIFIEX_DISABLE_CHAN_FILT; } + chan_idx++; } } @@ -1463,9 +1464,9 @@ int mwifiex_check_network_compatibility(struct mwifiex_private *priv, } static int -mwifiex_update_curr_bss_params(struct mwifiex_private *priv, - u8 *bssid, s32 rssi, const u8 *ie_buf, - size_t ie_len, u16 beacon_period, u16 cap_info_bitmap) +mwifiex_update_curr_bss_params(struct mwifiex_private *priv, u8 *bssid, + s32 rssi, const u8 *ie_buf, size_t ie_len, + u16 beacon_period, u16 cap_info_bitmap, u8 band) { struct mwifiex_bssdescriptor *bss_desc = NULL; int ret; @@ -1488,7 +1489,7 @@ mwifiex_update_curr_bss_params(struct mwifiex_private *priv, ret = mwifiex_fill_new_bss_desc(priv, bssid, rssi, beacon_ie, ie_len, beacon_period, - cap_info_bitmap, bss_desc); + cap_info_bitmap, band, bss_desc); if (ret) goto done; @@ -1532,6 +1533,11 @@ done: return 0; } +static void mwifiex_free_bss_priv(struct cfg80211_bss *bss) +{ + kfree(bss->priv); +} + /* * This function handles the command response of scan. * @@ -1570,6 +1576,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, struct chan_band_param_set *chan_band; u8 is_bgscan_resp; unsigned long flags; + struct cfg80211_bss *bss; is_bgscan_resp = (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_BG_SCAN_QUERY); @@ -1751,10 +1758,12 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, chan = ieee80211_get_channel(priv->wdev->wiphy, freq); if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { - cfg80211_inform_bss(priv->wdev->wiphy, chan, - bssid, network_tsf, cap_info_bitmap, - beacon_period, ie_buf, ie_len, rssi, - GFP_KERNEL); + bss = cfg80211_inform_bss(priv->wdev->wiphy, + chan, bssid, network_tsf, + cap_info_bitmap, beacon_period, + ie_buf, ie_len, rssi, GFP_KERNEL); + *(u8 *)bss->priv = band; + bss->free_priv = mwifiex_free_bss_priv; if (priv->media_connected && !memcmp(bssid, priv->curr_bss_params.bss_descriptor @@ -1762,7 +1771,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, mwifiex_update_curr_bss_params(priv, bssid, rssi, ie_buf, ie_len, beacon_period, - cap_info_bitmap); + cap_info_bitmap, band); } } else { dev_dbg(adapter->dev, "missing BSS channel IE\n"); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index eb569fa..a9dfeb1 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -148,7 +148,7 @@ int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, u8 *bssid, s32 rssi, u8 *ie_buf, size_t ie_len, u16 beacon_period, - u16 cap_info_bitmap, + u16 cap_info_bitmap, u8 band, struct mwifiex_bssdescriptor *bss_desc) { int ret; @@ -159,6 +159,7 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, bss_desc->beacon_buf_size = ie_len; bss_desc->beacon_period = beacon_period; bss_desc->cap_info_bitmap = cap_info_bitmap; + bss_desc->bss_band = band; if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) { dev_dbg(priv->adapter->dev, "info: InterpretIE: AP WEP enabled\n"); bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; @@ -203,6 +204,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, beacon_ie = kmemdup(bss->information_elements, bss->len_beacon_ies, GFP_KERNEL); if (!beacon_ie) { + kfree(bss_desc); dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); return -ENOMEM; } @@ -210,7 +212,8 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, ret = mwifiex_fill_new_bss_desc(priv, bss->bssid, bss->signal, beacon_ie, bss->len_beacon_ies, bss->beacon_interval, - bss->capability, bss_desc); + bss->capability, + *(u8 *)bss->priv, bss_desc); if (ret) goto done; } @@ -652,6 +655,7 @@ mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, int channel) u16 curr_chan = 0; struct cfg80211_bss *bss = NULL; struct ieee80211_channel *chan; + enum ieee80211_band band; memset(&bss_info, 0, sizeof(bss_info)); @@ -688,9 +692,9 @@ mwifiex_drv_change_adhoc_chan(struct mwifiex_private *priv, int channel) goto done; } + band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); chan = __ieee80211_get_channel(priv->wdev->wiphy, - ieee80211_channel_to_frequency(channel, - priv->curr_bss_params.band)); + ieee80211_channel_to_frequency(channel, band)); /* Find the BSS we want using available scan results */ bss = cfg80211_get_bss(priv->wdev->wiphy, chan, bss_info.bssid, @@ -867,10 +871,10 @@ int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, ret = mwifiex_rate_ioctl_cfg(priv, rate); if (!ret) { - if (rate && rate->is_rate_auto) + if (rate->is_rate_auto) rate->rate = mwifiex_index_to_data_rate(priv->tx_rate, priv->tx_htinfo); - else if (rate) + else rate->rate = priv->data_rate; } else { ret = -1; diff --git a/drivers/net/wireless/rt2x00/rt2400pci.c b/drivers/net/wireless/rt2x00/rt2400pci.c index daa32fc..7e9272b 100644 --- a/drivers/net/wireless/rt2x00/rt2400pci.c +++ b/drivers/net/wireless/rt2x00/rt2400pci.c @@ -1239,7 +1239,7 @@ static void rt2400pci_fill_rxdone(struct queue_entry *entry, * call, we must decrease the higher 32bits with 1 to get * to correct value. */ - tsf = rt2x00dev->ops->hw->get_tsf(rt2x00dev->hw); + tsf = rt2x00dev->ops->hw->get_tsf(rt2x00dev->hw, NULL); rx_low = rt2x00_get_field32(word4, RXD_W4_RX_END_TIME); rx_high = upper_32_bits(tsf); @@ -1673,7 +1673,8 @@ static int rt2400pci_conf_tx(struct ieee80211_hw *hw, u16 queue, return 0; } -static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw) +static u64 rt2400pci_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index b46c3b8..dcc0e1f 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -1966,7 +1966,8 @@ static int rt2500pci_probe_hw(struct rt2x00_dev *rt2x00dev) /* * IEEE80211 stack callback functions. */ -static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw) +static u64 rt2500pci_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 31c9850..9688dd0 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -4466,7 +4466,7 @@ int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, } EXPORT_SYMBOL_GPL(rt2800_conf_tx); -u64 rt2800_get_tsf(struct ieee80211_hw *hw) +u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index 7a2511f6..6de128e 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -199,7 +199,7 @@ void rt2800_get_tkip_seq(struct ieee80211_hw *hw, u8 hw_key_idx, u32 *iv32, 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); +u64 rt2800_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif); 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, diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c index 058ef4b..6b6a8e2 100644 --- a/drivers/net/wireless/rt2x00/rt61pci.c +++ b/drivers/net/wireless/rt2x00/rt61pci.c @@ -2940,7 +2940,7 @@ static int rt61pci_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, return 0; } -static u64 rt61pci_get_tsf(struct ieee80211_hw *hw) +static u64 rt61pci_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c index 0baeb89..6f51e39 100644 --- a/drivers/net/wireless/rt2x00/rt73usb.c +++ b/drivers/net/wireless/rt2x00/rt73usb.c @@ -2279,7 +2279,7 @@ static int rt73usb_conf_tx(struct ieee80211_hw *hw, u16 queue_idx, return 0; } -static u64 rt73usb_get_tsf(struct ieee80211_hw *hw) +static u64 rt73usb_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rt2x00_dev *rt2x00dev = hw->priv; u64 tsf; diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 66b29dc..0082015 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -669,7 +669,8 @@ static void rtl8180_stop(struct ieee80211_hw *dev) rtl8180_free_tx_ring(dev, i); } -static u64 rtl8180_get_tsf(struct ieee80211_hw *dev) +static u64 rtl8180_get_tsf(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) { struct rtl8180_priv *priv = dev->priv; @@ -701,7 +702,7 @@ static void rtl8180_beacon_work(struct work_struct *work) * TODO: make hardware update beacon timestamp */ mgmt = (struct ieee80211_mgmt *)skb->data; - mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev)); + mgmt->u.beacon.timestamp = cpu_to_le64(rtl8180_get_tsf(dev, vif)); /* TODO: use actual beacon queue */ skb_set_queue_mapping(skb, 0); diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 1e0be14..f5afa15 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -1277,7 +1277,7 @@ static int rtl8187_conf_tx(struct ieee80211_hw *dev, u16 queue, return 0; } -static u64 rtl8187_get_tsf(struct ieee80211_hw *dev) +static u64 rtl8187_get_tsf(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { struct rtl8187_priv *priv = dev->priv; diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 04c4e9e..21e40f6 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -775,7 +775,7 @@ out: mutex_unlock(&rtlpriv->locks.conf_mutex); } -static u64 rtl_op_get_tsf(struct ieee80211_hw *hw) +static u64 rtl_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); u64 tsf; @@ -784,7 +784,8 @@ static u64 rtl_op_get_tsf(struct ieee80211_hw *hw) return tsf; } -static void rtl_op_set_tsf(struct ieee80211_hw *hw, u64 tsf) +static void rtl_op_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 tsf) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); @@ -794,7 +795,8 @@ static void rtl_op_set_tsf(struct ieee80211_hw *hw, u64 tsf) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_CORRECT_TSF, (u8 *) (&bibss)); } -static void rtl_op_reset_tsf(struct ieee80211_hw *hw) +static void rtl_op_reset_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct rtl_priv *rtlpriv = rtl_priv(hw); u8 tmp = 0; diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c index 4c34c4c..b7ecb9e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c @@ -133,6 +133,10 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; + if (!rtlpriv->psc.inactiveps) + pr_info("rtl8192ce: Power Save off (module option)\n"); + if (!rtlpriv->psc.fwctrl_lps) + pr_info("rtl8192ce: FW Power Save off (module option)\n"); rtlpriv->psc.reg_fwctrl_lps = 3; rtlpriv->psc.reg_max_lps_awakeintvl = 5; /* for ASPM, you can close aspm through @@ -356,10 +360,10 @@ module_param_named(swenc, rtl92ce_mod_params.sw_crypto, bool, 0444); module_param_named(ips, rtl92ce_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl92ce_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl92ce_mod_params.fwctrl_lps, bool, 0444); -MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n"); -MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n"); -MODULE_PARM_DESC(fwlps, "using linked fw control power save " - "(default 1 is open)\n"); +MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); +MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); +MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); +MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); static struct pci_driver rtl92ce_driver = { .name = KBUILD_MODNAME, diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h index 81ae642..c8977a5 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.h @@ -537,12 +537,6 @@ do { \ memset(__pdesc, 0, _size); \ } while (0); -#define RX_HAL_IS_CCK_RATE(_pdesc)\ - (_pdesc->rxmcs == DESC92_RATE1M || \ - _pdesc->rxmcs == DESC92_RATE2M || \ - _pdesc->rxmcs == DESC92_RATE5_5M || \ - _pdesc->rxmcs == DESC92_RATE11M) - struct rx_fwinfo_92c { u8 gain_trsw[4]; u8 pwdb_all; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.h b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.h index 35529f7..626d88e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.h @@ -87,12 +87,6 @@ void rtl92c_set_data_filter(struct ieee80211_hw *hw, u16 filter); u32 rtl92c_get_txdma_status(struct ieee80211_hw *hw); -#define RX_HAL_IS_CCK_RATE(_pdesc)\ - (GET_RX_DESC_RX_MCS(_pdesc) == DESC92_RATE1M ||\ - GET_RX_DESC_RX_MCS(_pdesc) == DESC92_RATE2M ||\ - GET_RX_DESC_RX_MCS(_pdesc) == DESC92_RATE5_5M ||\ - GET_RX_DESC_RX_MCS(_pdesc) == DESC92_RATE11M) - struct rx_fwinfo_92c { u8 gain_trsw[4]; u8 pwdb_all; diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c index f6419b7..c681597 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c @@ -150,6 +150,10 @@ static int rtl92d_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; + if (!rtlpriv->psc.inactiveps) + pr_info("rtl8192ce: Power Save off (module option)\n"); + if (!rtlpriv->psc.fwctrl_lps) + pr_info("rtl8192ce: FW Power Save off (module option)\n"); rtlpriv->psc.reg_fwctrl_lps = 3; rtlpriv->psc.reg_max_lps_awakeintvl = 5; /* for ASPM, you can close aspm through @@ -376,10 +380,10 @@ module_param_named(swenc, rtl92de_mod_params.sw_crypto, bool, 0444); module_param_named(ips, rtl92de_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl92de_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl92de_mod_params.fwctrl_lps, bool, 0444); -MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n"); -MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n"); -MODULE_PARM_DESC(swlps, "using linked sw control power save (default 1" - " is open)\n"); +MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); +MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); +MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); +MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); static struct pci_driver rtl92de_driver = { .name = KBUILD_MODNAME, diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h index 6c22368..4d55d0b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.h @@ -537,12 +537,6 @@ do { \ memset((void *)__pdesc, 0, _size); \ } while (0); -#define RX_HAL_IS_CCK_RATE(_pdesc)\ - (_pdesc->rxmcs == DESC92_RATE1M || \ - _pdesc->rxmcs == DESC92_RATE2M || \ - _pdesc->rxmcs == DESC92_RATE5_5M || \ - _pdesc->rxmcs == DESC92_RATE11M) - /* For 92D early mode */ #define SET_EARLYMODE_PKTNUM(__paddr, __value) \ SET_BITS_OFFSET_LE(__paddr, 0, 3, __value) diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/def.h b/drivers/net/wireless/rtlwifi/rtl8192se/def.h index 68204ea..c6c0448 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/def.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/def.h @@ -459,7 +459,7 @@ do { \ #define SET_RX_STATUS__DESC_BUFF_ADDR(__pdesc, __val) \ SET_BITS_OFFSET_LE(__pdesc + 24, 0, 32, __val) -#define RX_HAL_IS_CCK_RATE(_pdesc)\ +#define SE_RX_HAL_IS_CCK_RATE(_pdesc)\ (GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE1M || \ GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE2M || \ GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE5_5M ||\ diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c index 0055a1c..24bd331 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c @@ -164,6 +164,10 @@ static int rtl92s_init_sw_vars(struct ieee80211_hw *hw) rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; + if (!rtlpriv->psc.inactiveps) + pr_info("rtl8192ce: Power Save off (module option)\n"); + if (!rtlpriv->psc.fwctrl_lps) + pr_info("rtl8192ce: FW Power Save off (module option)\n"); rtlpriv->psc.reg_fwctrl_lps = 3; rtlpriv->psc.reg_max_lps_awakeintvl = 5; /* for ASPM, you can close aspm through @@ -378,6 +382,7 @@ MODULE_DEVICE_TABLE(pci, rtl92se_pci_ids); MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>"); MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); +MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek 8192S/8191S 802.11n PCI wireless"); MODULE_FIRMWARE("rtlwifi/rtl8192sefw.bin"); @@ -386,11 +391,10 @@ module_param_named(swenc, rtl92se_mod_params.sw_crypto, bool, 0444); module_param_named(ips, rtl92se_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl92se_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl92se_mod_params.fwctrl_lps, bool, 0444); -MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n"); -MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n"); -MODULE_PARM_DESC(swlps, "using linked sw control power save (default 1 is " - "open)\n"); - +MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); +MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); +MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); +MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); static struct pci_driver rtl92se_driver = { .name = KBUILD_MODNAME, diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index d9aeae7..ba137da 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -126,7 +126,7 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw, bool in_powersavemode = false; bool is_cck_rate; - is_cck_rate = RX_HAL_IS_CCK_RATE(pdesc); + is_cck_rate = SE_RX_HAL_IS_CCK_RATE(pdesc); pstats->packet_matchbssid = packet_match_bssid; pstats->packet_toself = packet_toself; pstats->is_cck = is_cck_rate; diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index 8b1cef0..4bf3cf4 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -863,6 +863,7 @@ static void _rtl_usb_tx_preprocess(struct ieee80211_hw *hw, struct sk_buff *skb, u8 tid = 0; u16 seq_number = 0; + memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); if (ieee80211_is_auth(fc)) { RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, ("MAC80211_LINKING\n")); rtl_ips_nic_on(hw); diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 8a90919..615f6b4 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -165,6 +165,12 @@ enum hardware_type { #define IS_HARDWARE_TYPE_8723U(rtlhal) \ (rtlhal->hw_type == HARDWARE_TYPE_RTL8723U) +#define RX_HAL_IS_CCK_RATE(_pdesc)\ + (_pdesc->rxmcs == DESC92_RATE1M || \ + _pdesc->rxmcs == DESC92_RATE2M || \ + _pdesc->rxmcs == DESC92_RATE5_5M || \ + _pdesc->rxmcs == DESC92_RATE11M) + enum scan_operation_backup_opt { SCAN_OPT_BACKUP = 0, SCAN_OPT_RESTORE, diff --git a/drivers/net/wireless/wl12xx/Kconfig b/drivers/net/wireless/wl12xx/Kconfig index 07bcb15..3fe388b 100644 --- a/drivers/net/wireless/wl12xx/Kconfig +++ b/drivers/net/wireless/wl12xx/Kconfig @@ -19,16 +19,6 @@ config WL12XX If you choose to build a module, it will be called wl12xx. Say N if unsure. -config WL12XX_HT - bool "TI wl12xx 802.11 HT support (EXPERIMENTAL)" - depends on WL12XX && EXPERIMENTAL - default n - ---help--- - This will enable 802.11 HT support in the wl12xx module. - - That configuration is temporary due to the code incomplete and - still in testing process. - config WL12XX_SPI tristate "TI wl12xx SPI support" depends on WL12XX && SPI_MASTER diff --git a/drivers/net/wireless/wl12xx/Makefile b/drivers/net/wireless/wl12xx/Makefile index 521c041..621b348 100644 --- a/drivers/net/wireless/wl12xx/Makefile +++ b/drivers/net/wireless/wl12xx/Makefile @@ -1,16 +1,16 @@ wl12xx-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \ boot.o init.o debugfs.o scan.o -wl12xx_spi-objs = spi.o +wl12xx_spi-objs = spi.o wl12xx_sdio-objs = sdio.o -wl12xx_sdio_test-objs = sdio_test.o +wl12xx_sdio_test-objs = sdio_test.o wl12xx-$(CONFIG_NL80211_TESTMODE) += testmode.o obj-$(CONFIG_WL12XX) += wl12xx.o obj-$(CONFIG_WL12XX_SPI) += wl12xx_spi.o obj-$(CONFIG_WL12XX_SDIO) += wl12xx_sdio.o -obj-$(CONFIG_WL12XX_SDIO_TEST) += wl12xx_sdio_test.o +obj-$(CONFIG_WL12XX_SDIO_TEST) += wl12xx_sdio_test.o # small builtin driver bit obj-$(CONFIG_WL12XX_PLATFORM_DATA) += wl12xx_platform_data.o diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index f2838ae..399849e 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1687,3 +1687,43 @@ out: kfree(acx); return ret; } + +int wl12xx_acx_config_hangover(struct wl1271 *wl) +{ + struct wl12xx_acx_config_hangover *acx; + struct conf_hangover_settings *conf = &wl->conf.hangover; + int ret; + + wl1271_debug(DEBUG_ACX, "acx config hangover"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->recover_time = cpu_to_le32(conf->recover_time); + acx->hangover_period = conf->hangover_period; + acx->dynamic_mode = conf->dynamic_mode; + acx->early_termination_mode = conf->early_termination_mode; + acx->max_period = conf->max_period; + acx->min_period = conf->min_period; + acx->increase_delta = conf->increase_delta; + acx->decrease_delta = conf->decrease_delta; + acx->quiet_time = conf->quiet_time; + acx->increase_time = conf->increase_time; + acx->window_size = acx->window_size; + + ret = wl1271_cmd_configure(wl, ACX_CONFIG_HANGOVER, acx, + sizeof(*acx)); + + if (ret < 0) { + wl1271_warning("acx config hangover failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; + +} diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 758c596..556ee4e 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1144,6 +1144,23 @@ struct wl12xx_acx_set_rate_mgmt_params { u8 padding2[2]; } __packed; +struct wl12xx_acx_config_hangover { + struct acx_header header; + + __le32 recover_time; + u8 hangover_period; + u8 dynamic_mode; + u8 early_termination_mode; + u8 max_period; + u8 min_period; + u8 increase_delta; + u8 decrease_delta; + u8 quiet_time; + u8 increase_time; + u8 window_size; + u8 padding[2]; +} __packed; + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1281,5 +1298,6 @@ int wl1271_acx_config_ps(struct wl1271 *wl); int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); int wl1271_acx_fm_coex(struct wl1271 *wl); int wl12xx_acx_set_rate_mgmt_params(struct wl1271 *wl); +int wl12xx_acx_config_hangover(struct wl1271 *wl); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/cmd.c b/drivers/net/wireless/wl12xx/cmd.c index 817bc18..287fe95 100644 --- a/drivers/net/wireless/wl12xx/cmd.c +++ b/drivers/net/wireless/wl12xx/cmd.c @@ -661,12 +661,9 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl) wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wl->role_id); - /* - * We currently do not support hidden SSID. The real SSID - * should be fetched from mac80211 first. - */ - if (wl->ssid_len == 0) { - wl1271_warning("Hidden SSID currently not supported for AP"); + /* trying to use hidden SSID with an old hostapd version */ + if (wl->ssid_len == 0 && !bss_conf->hidden_ssid) { + wl1271_error("got a null SSID from beacon/bss"); ret = -EINVAL; goto out; } @@ -695,9 +692,18 @@ int wl12xx_cmd_role_start_ap(struct wl1271 *wl) cmd->ap.dtim_interval = bss_conf->dtim_period; cmd->ap.beacon_expiry = WL1271_AP_DEF_BEACON_EXP; cmd->channel = wl->channel; - cmd->ap.ssid_len = wl->ssid_len; - cmd->ap.ssid_type = WL12XX_SSID_TYPE_PUBLIC; - memcpy(cmd->ap.ssid, wl->ssid, wl->ssid_len); + + if (!bss_conf->hidden_ssid) { + /* take the SSID from the beacon for backward compatibility */ + cmd->ap.ssid_type = WL12XX_SSID_TYPE_PUBLIC; + cmd->ap.ssid_len = wl->ssid_len; + memcpy(cmd->ap.ssid, wl->ssid, wl->ssid_len); + } else { + cmd->ap.ssid_type = WL12XX_SSID_TYPE_HIDDEN; + cmd->ap.ssid_len = bss_conf->ssid_len; + memcpy(cmd->ap.ssid, bss_conf->ssid, bss_conf->ssid_len); + } + cmd->ap.local_rates = cpu_to_le32(0xffffffff); switch (wl->band) { @@ -895,7 +901,7 @@ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len) struct acx_header *acx = buf; int ret; - wl1271_debug(DEBUG_CMD, "cmd configure"); + wl1271_debug(DEBUG_CMD, "cmd configure (%d)", id); acx->id = cpu_to_le16(id); @@ -1106,6 +1112,7 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, { struct sk_buff *skb; int ret; + u32 rate; skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, ie, ie_len); @@ -1116,14 +1123,13 @@ int wl1271_cmd_build_probe_req(struct wl1271 *wl, wl1271_dump(DEBUG_SCAN, "PROBE REQ: ", skb->data, skb->len); + rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]); if (band == IEEE80211_BAND_2GHZ) ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, - skb->data, skb->len, 0, - wl->conf.tx.basic_rate); + skb->data, skb->len, 0, rate); else ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, - skb->data, skb->len, 0, - wl->conf.tx.basic_rate_5); + skb->data, skb->len, 0, rate); out: dev_kfree_skb(skb); @@ -1134,6 +1140,7 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, struct sk_buff *skb) { int ret; + u32 rate; if (!skb) skb = ieee80211_ap_probereq_get(wl->hw, wl->vif); @@ -1142,14 +1149,13 @@ struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl, wl1271_dump(DEBUG_SCAN, "AP PROBE REQ: ", skb->data, skb->len); + rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[wl->band]); if (wl->band == IEEE80211_BAND_2GHZ) ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_2_4, - skb->data, skb->len, 0, - wl->conf.tx.basic_rate); + skb->data, skb->len, 0, rate); else ret = wl1271_cmd_template_set(wl, CMD_TEMPL_CFG_PROBE_REQ_5, - skb->data, skb->len, 0, - wl->conf.tx.basic_rate_5); + skb->data, skb->len, 0, rate); if (ret < 0) wl1271_error("Unable to set ap probe request template."); @@ -1413,7 +1419,7 @@ out: int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid) { struct wl12xx_cmd_add_peer *cmd; - int ret; + int i, ret; u32 sta_rates; wl1271_debug(DEBUG_CMD, "cmd add peer %d", (int)hlid); @@ -1424,23 +1430,29 @@ int wl12xx_cmd_add_peer(struct wl1271 *wl, struct ieee80211_sta *sta, u8 hlid) goto out; } - /* currently we don't support UAPSD */ - cmd->sp_len = 0; - memcpy(cmd->addr, sta->addr, ETH_ALEN); cmd->bss_index = WL1271_AP_BSS_INDEX; cmd->aid = sta->aid; cmd->hlid = hlid; + cmd->sp_len = sta->max_sp; cmd->wmm = sta->wme ? 1 : 0; + for (i = 0; i < NUM_ACCESS_CATEGORIES_COPY; i++) + if (sta->wme && (sta->uapsd_queues & BIT(i))) + cmd->psd_type[i] = WL1271_PSD_UPSD_TRIGGER; + else + cmd->psd_type[i] = WL1271_PSD_LEGACY; + sta_rates = sta->supp_rates[wl->band]; if (sta->ht_cap.ht_supported) sta_rates |= sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET; cmd->supported_rates = - cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates)); + cpu_to_le32(wl1271_tx_enabled_rates_get(wl, sta_rates, + wl->band)); - wl1271_debug(DEBUG_CMD, "new peer rates: 0x%x", cmd->supported_rates); + wl1271_debug(DEBUG_CMD, "new peer rates=0x%x queues=0x%x", + cmd->supported_rates, sta->uapsd_queues); ret = wl1271_cmd_send(wl, CMD_ADD_PEER, cmd, sizeof(*cmd), 0); if (ret < 0) { diff --git a/drivers/net/wireless/wl12xx/cmd.h b/drivers/net/wireless/wl12xx/cmd.h index 22c2f37..8e4d11e 100644 --- a/drivers/net/wireless/wl12xx/cmd.h +++ b/drivers/net/wireless/wl12xx/cmd.h @@ -591,6 +591,13 @@ enum wl12xx_ssid_type { WL12XX_SSID_TYPE_ANY = 2, }; +enum wl1271_psd_type { + WL1271_PSD_LEGACY = 0, + WL1271_PSD_UPSD_TRIGGER = 1, + WL1271_PSD_LEGACY_PSPOLL = 2, + WL1271_PSD_SAPSD = 3 +}; + struct wl12xx_cmd_add_peer { struct wl1271_cmd_header header; diff --git a/drivers/net/wireless/wl12xx/conf.h b/drivers/net/wireless/wl12xx/conf.h index 82f205c..6a6805c 100644 --- a/drivers/net/wireless/wl12xx/conf.h +++ b/drivers/net/wireless/wl12xx/conf.h @@ -454,12 +454,10 @@ struct conf_rx_settings { #define CONF_TX_AP_DEFAULT_MGMT_RATES (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS) -/* - * Default rates for working as IBSS. use 11b rates - */ +/* default rates for working as IBSS (11b and OFDM) */ #define CONF_TX_IBSS_DEFAULT_RATES (CONF_HW_BIT_RATE_1MBPS | \ CONF_HW_BIT_RATE_2MBPS | CONF_HW_BIT_RATE_5_5MBPS | \ - CONF_HW_BIT_RATE_11MBPS); + CONF_HW_BIT_RATE_11MBPS | CONF_TX_OFDM_RATES); struct conf_tx_rate_class { @@ -916,14 +914,6 @@ struct conf_conn_settings { u8 psm_entry_nullfunc_retries; /* - * Specifies the time to linger in active mode after successfully - * transmitting the PSM entry null-func frame. - * - * Range 0 - 255 TU's - */ - u8 psm_entry_hangover_period; - - /* * * Specifies the interval of the connection keep-alive null-func * frame in ms. @@ -1236,6 +1226,20 @@ struct conf_rate_policy_settings { u8 rate_retry_policy[ACX_RATE_MGMT_NUM_OF_RATES]; }; +struct conf_hangover_settings { + u32 recover_time; + u8 hangover_period; + u8 dynamic_mode; + u8 early_termination_mode; + u8 max_period; + u8 min_period; + u8 increase_delta; + u8 decrease_delta; + u8 quiet_time; + u8 increase_time; + u8 window_size; +}; + struct conf_drv_settings { struct conf_sg_settings sg; struct conf_rx_settings rx; @@ -1254,6 +1258,7 @@ struct conf_drv_settings { struct conf_rx_streaming_settings rx_streaming; struct conf_fwlog fwlog; struct conf_rate_policy_settings rate; + struct conf_hangover_settings hangover; u8 hci_io_ds; }; diff --git a/drivers/net/wireless/wl12xx/debugfs.c b/drivers/net/wireless/wl12xx/debugfs.c index d59354f..3999fd5 100644 --- a/drivers/net/wireless/wl12xx/debugfs.c +++ b/drivers/net/wireless/wl12xx/debugfs.c @@ -265,18 +265,10 @@ static ssize_t gpio_power_write(struct file *file, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; - char buf[10]; - size_t len; unsigned long value; int ret; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) { - return -EFAULT; - } - buf[len] = '\0'; - - ret = kstrtoul(buf, 0, &value); + ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in gpio_power"); return -EINVAL; @@ -427,17 +419,10 @@ static ssize_t dtim_interval_write(struct file *file, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; - char buf[10]; - size_t len; unsigned long value; int ret; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - ret = kstrtoul(buf, 0, &value); + ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value for dtim_interval"); return -EINVAL; @@ -492,17 +477,10 @@ static ssize_t beacon_interval_write(struct file *file, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; - char buf[10]; - size_t len; unsigned long value; int ret; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - ret = kstrtoul(buf, 0, &value); + ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value for beacon_interval"); return -EINVAL; @@ -542,17 +520,10 @@ static ssize_t rx_streaming_interval_write(struct file *file, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; - char buf[10]; - size_t len; unsigned long value; int ret; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - ret = kstrtoul(buf, 0, &value); + ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in rx_streaming_interval!"); return -EINVAL; @@ -601,17 +572,10 @@ static ssize_t rx_streaming_always_write(struct file *file, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; - char buf[10]; - size_t len; unsigned long value; int ret; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - ret = kstrtoul(buf, 0, &value); + ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in rx_streaming_write!"); return -EINVAL; @@ -655,6 +619,47 @@ static const struct file_operations rx_streaming_always_ops = { .llseek = default_llseek, }; +static ssize_t beacon_filtering_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + char buf[10]; + size_t len; + unsigned long value; + int ret; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + ret = kstrtoul(buf, 0, &value); + if (ret < 0) { + wl1271_warning("illegal value for beacon_filtering!"); + return -EINVAL; + } + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl1271_acx_beacon_filter_opt(wl, !!value); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations beacon_filtering_ops = { + .write = beacon_filtering_write, + .open = wl1271_open_file_generic, + .llseek = default_llseek, +}; + static int wl1271_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -767,6 +772,7 @@ static int wl1271_debugfs_add_files(struct wl1271 *wl, DEBUGFS_ADD(driver_state, rootdir); DEBUGFS_ADD(dtim_interval, rootdir); DEBUGFS_ADD(beacon_interval, rootdir); + DEBUGFS_ADD(beacon_filtering, rootdir); streaming = debugfs_create_dir("rx_streaming", rootdir); if (!streaming || IS_ERR(streaming)) diff --git a/drivers/net/wireless/wl12xx/event.c b/drivers/net/wireless/wl12xx/event.c index 0bd7b02..e66db69 100644 --- a/drivers/net/wireless/wl12xx/event.c +++ b/drivers/net/wireless/wl12xx/event.c @@ -171,19 +171,26 @@ static void wl1271_event_rssi_trigger(struct wl1271 *wl, wl->last_rssi_event = event; } -static void wl1271_stop_ba_event(struct wl1271 *wl, u8 ba_allowed) +static void wl1271_stop_ba_event(struct wl1271 *wl) { - /* Convert the value to bool */ - wl->ba_allowed = !!ba_allowed; - - /* - * Return in case: - * there are not BA open or the event indication is to allowed BA - */ - if ((!wl->ba_rx_bitmap) || (wl->ba_allowed)) - return; + if (wl->bss_type != BSS_TYPE_AP_BSS) { + if (!wl->ba_rx_bitmap) + return; + ieee80211_stop_rx_ba_session(wl->vif, wl->ba_rx_bitmap, + wl->bssid); + } else { + int i; + struct wl1271_link *lnk; + for (i = WL1271_AP_STA_HLID_START; i < AP_MAX_LINKS; i++) { + lnk = &wl->links[i]; + if (!wl1271_is_active_sta(wl, i) || !lnk->ba_bitmap) + continue; - ieee80211_stop_rx_ba_session(wl->vif, wl->ba_rx_bitmap, wl->bssid); + ieee80211_stop_rx_ba_session(wl->vif, + lnk->ba_bitmap, + lnk->addr); + } + } } static void wl12xx_event_soft_gemini_sense(struct wl1271 *wl, @@ -283,12 +290,14 @@ static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) wl1271_event_rssi_trigger(wl, mbox); } - if ((vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) && !is_ap) { + if ((vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)) { wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. " "ba_allowed = 0x%x", mbox->rx_ba_allowed); - if (wl->vif) - wl1271_stop_ba_event(wl, mbox->rx_ba_allowed); + wl->ba_allowed = !!mbox->rx_ba_allowed; + + if (wl->vif && !wl->ba_allowed) + wl1271_stop_ba_event(wl); } if ((vector & DUMMY_PACKET_EVENT_ID)) { diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index b13bebe..04db64c 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -103,6 +103,7 @@ static int wl1271_ap_init_deauth_template(struct wl1271 *wl) { struct wl12xx_disconn_template *tmpl; int ret; + u32 rate; tmpl = kzalloc(sizeof(*tmpl), GFP_KERNEL); if (!tmpl) { @@ -113,9 +114,9 @@ static int wl1271_ap_init_deauth_template(struct wl1271 *wl) tmpl->header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); + rate = wl1271_tx_min_rate_get(wl, wl->basic_rate_set); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_DEAUTH_AP, - tmpl, sizeof(*tmpl), 0, - wl1271_tx_min_rate_get(wl)); + tmpl, sizeof(*tmpl), 0, rate); out: kfree(tmpl); @@ -126,6 +127,7 @@ static int wl1271_ap_init_null_template(struct wl1271 *wl) { struct ieee80211_hdr_3addr *nullfunc; int ret; + u32 rate; nullfunc = kzalloc(sizeof(*nullfunc), GFP_KERNEL); if (!nullfunc) { @@ -142,9 +144,9 @@ static int wl1271_ap_init_null_template(struct wl1271 *wl) memcpy(nullfunc->addr2, wl->mac_addr, ETH_ALEN); memcpy(nullfunc->addr3, wl->mac_addr, ETH_ALEN); + rate = wl1271_tx_min_rate_get(wl, wl->basic_rate_set); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_NULL_DATA, nullfunc, - sizeof(*nullfunc), 0, - wl1271_tx_min_rate_get(wl)); + sizeof(*nullfunc), 0, rate); out: kfree(nullfunc); @@ -155,6 +157,7 @@ static int wl1271_ap_init_qos_null_template(struct wl1271 *wl) { struct ieee80211_qos_hdr *qosnull; int ret; + u32 rate; qosnull = kzalloc(sizeof(*qosnull), GFP_KERNEL); if (!qosnull) { @@ -171,9 +174,9 @@ static int wl1271_ap_init_qos_null_template(struct wl1271 *wl) memcpy(qosnull->addr2, wl->mac_addr, ETH_ALEN); memcpy(qosnull->addr3, wl->mac_addr, ETH_ALEN); + rate = wl1271_tx_min_rate_get(wl, wl->basic_rate_set); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_QOS_NULL_DATA, qosnull, - sizeof(*qosnull), 0, - wl1271_tx_min_rate_get(wl)); + sizeof(*qosnull), 0, rate); out: kfree(qosnull); @@ -498,7 +501,7 @@ int wl1271_init_ap_rates(struct wl1271 *wl) return ret; /* use the min basic rate for AP broadcast/multicast */ - rc.enabled_rates = wl1271_tx_min_rate_get(wl); + rc.enabled_rates = wl1271_tx_min_rate_get(wl, wl->basic_rate_set); rc.short_retry_limit = 10; rc.long_retry_limit = 10; rc.aflags = 0; @@ -707,6 +710,11 @@ int wl1271_hw_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + /* configure hangover */ + ret = wl12xx_acx_config_hangover(wl); + if (ret < 0) + goto out_free_memmap; + return 0; out_free_memmap: diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index bde8402..7d409b0 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -239,7 +239,6 @@ static struct conf_drv_settings default_conf = { .psm_entry_retries = 8, .psm_exit_retries = 16, .psm_entry_nullfunc_retries = 3, - .psm_entry_hangover_period = 1, .keep_alive_interval = 55000, .max_listen_interval = 20, }, @@ -267,8 +266,8 @@ static struct conf_drv_settings default_conf = { }, .sched_scan = { /* sched_scan requires dwell times in TU instead of TU/1000 */ - .min_dwell_time_active = 8, - .max_dwell_time_active = 30, + .min_dwell_time_active = 30, + .max_dwell_time_active = 60, .dwell_time_passive = 100, .dwell_time_dfs = 150, .num_probe_reqs = 2, @@ -359,9 +358,23 @@ static struct conf_drv_settings default_conf = { 0x00, 0x00, 0x00, }, }, + .hangover = { + .recover_time = 0, + .hangover_period = 20, + .dynamic_mode = 1, + .early_termination_mode = 1, + .max_period = 20, + .min_period = 1, + .increase_delta = 1, + .decrease_delta = 2, + .quiet_time = 4, + .increase_time = 1, + .window_size = 16, + }, }; static char *fwlog_param; +static bool bug_on_recovery; static void __wl1271_op_remove_interface(struct wl1271 *wl, bool reset_tx_queues); @@ -757,13 +770,14 @@ static int wl1271_plt_init(struct wl1271 *wl) static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_pkts) { - bool fw_ps; + bool fw_ps, single_sta; /* only regulate station links */ if (hlid < WL1271_AP_STA_HLID_START) return; fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); + single_sta = (wl->active_sta_count == 1); /* * Wake up from high level PS if the STA is asleep with too little @@ -772,8 +786,12 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_pkts) if (!fw_ps || tx_pkts < WL1271_PS_STA_MAX_PACKETS) wl1271_ps_link_end(wl, hlid); - /* Start high-level PS if the STA is asleep with enough blocks in FW */ - else if (fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) + /* + * Start high-level PS if the STA is asleep with enough blocks in FW. + * Make an exception if this is the only connected station. In this + * case FW-memory congestion is not a problem. + */ + else if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) wl1271_ps_link_start(wl, hlid, true); } @@ -1213,6 +1231,8 @@ static void wl1271_recovery_work(struct work_struct *work) wl1271_info("Hardware recovery in progress. FW ver: %s pc: 0x%x", wl->chip.fw_ver_str, wl1271_read32(wl, SCR_PAD4)); + BUG_ON(bug_on_recovery); + /* * Advance security sequence number to overcome potential progress * in the firmware during recovery. This doens't hurt if the network is @@ -1222,9 +1242,6 @@ static void wl1271_recovery_work(struct work_struct *work) test_bit(WL1271_FLAG_AP_STARTED, &wl->flags)) wl->tx_security_seq += WL1271_TX_SQN_POST_RECOVERY_PADDING; - if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) - ieee80211_connection_loss(wl->vif); - /* Prevent spurious TX during FW restart */ ieee80211_stop_queues(wl->hw); @@ -1528,7 +1545,13 @@ out: int wl1271_tx_dummy_packet(struct wl1271 *wl) { unsigned long flags; - int q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet)); + int q; + + /* no need to queue a new dummy packet if one is already pending */ + if (test_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags)) + return 0; + + q = wl1271_tx_get_queue(skb_get_queue_mapping(wl->dummy_packet)); spin_lock_irqsave(&wl->wl_lock, flags); set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags); @@ -1802,10 +1825,16 @@ static u8 wl12xx_get_role_type(struct wl1271 *wl) { switch (wl->bss_type) { case BSS_TYPE_AP_BSS: - return WL1271_ROLE_AP; + if (wl->p2p) + return WL1271_ROLE_P2P_GO; + else + return WL1271_ROLE_AP; case BSS_TYPE_STA_BSS: - return WL1271_ROLE_STA; + if (wl->p2p) + return WL1271_ROLE_P2P_CL; + else + return WL1271_ROLE_STA; case BSS_TYPE_IBSS: return WL1271_ROLE_IBSS; @@ -1827,7 +1856,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, bool booted = false; wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", - vif->type, vif->addr); + ieee80211_vif_type_p2p(vif), vif->addr); mutex_lock(&wl->mutex); if (wl->vif) { @@ -1847,7 +1876,10 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, goto out; } - switch (vif->type) { + switch (ieee80211_vif_type_p2p(vif)) { + case NL80211_IFTYPE_P2P_CLIENT: + wl->p2p = 1; + /* fall-through */ case NL80211_IFTYPE_STATION: wl->bss_type = BSS_TYPE_STA_BSS; wl->set_bss_type = BSS_TYPE_STA_BSS; @@ -1856,6 +1888,9 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, wl->bss_type = BSS_TYPE_IBSS; wl->set_bss_type = BSS_TYPE_STA_BSS; break; + case NL80211_IFTYPE_P2P_GO: + wl->p2p = 1; + /* fall-through */ case NL80211_IFTYPE_AP: wl->bss_type = BSS_TYPE_AP_BSS; break; @@ -2051,6 +2086,7 @@ deinit: wl->ssid_len = 0; wl->bss_type = MAX_BSS_TYPE; wl->set_bss_type = MAX_BSS_TYPE; + wl->p2p = 0; wl->band = IEEE80211_BAND_2GHZ; wl->rx_counter = 0; @@ -2063,6 +2099,8 @@ deinit: wl->time_offset = 0; wl->session_counter = 0; wl->rate_set = CONF_TX_RATE_MASK_BASIC; + wl->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate; + wl->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5; wl->vif = NULL; wl->tx_spare_blocks = TX_HW_BLOCK_SPARE_DEFAULT; wl1271_free_ap_keys(wl); @@ -2075,6 +2113,7 @@ deinit: memset(wl->roles_map, 0, sizeof(wl->roles_map)); memset(wl->links_map, 0, sizeof(wl->links_map)); memset(wl->roc_map, 0, sizeof(wl->roc_map)); + wl->active_sta_count = 0; /* The system link is always allocated */ __set_bit(WL12XX_SYSTEM_HLID, wl->links_map); @@ -2200,14 +2239,8 @@ out: static void wl1271_set_band_rate(struct wl1271 *wl) { - if (wl->band == IEEE80211_BAND_2GHZ) { - wl->basic_rate_set = wl->conf.tx.basic_rate; - wl->rate_set = wl->conf.tx.basic_rate; - } else { - wl->basic_rate_set = wl->conf.tx.basic_rate_5; - wl->rate_set = wl->conf.tx.basic_rate_5; - } - + wl->basic_rate_set = wl->bitrate_masks[wl->band]; + wl->rate_set = wl->basic_rate_set; } static bool wl12xx_is_roc(struct wl1271 *wl) @@ -2236,7 +2269,7 @@ static int wl1271_sta_handle_idle(struct wl1271 *wl, bool idle) if (ret < 0) goto out; } - wl->rate_set = wl1271_tx_min_rate_get(wl); + wl->rate_set = wl1271_tx_min_rate_get(wl, wl->basic_rate_set); ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out; @@ -2318,6 +2351,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) if (changed & IEEE80211_CONF_CHANGE_CHANNEL && ((wl->band != conf->channel->band) || (wl->channel != channel))) { + /* send all pending packets */ + wl1271_tx_work_locked(wl); wl->band = conf->channel->band; wl->channel = channel; @@ -2331,7 +2366,8 @@ static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) if (!test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) wl1271_set_band_rate(wl); - wl->basic_rate = wl1271_tx_min_rate_get(wl); + wl->basic_rate = + wl1271_tx_min_rate_get(wl, wl->basic_rate_set); ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) wl1271_warning("rate policy for channel " @@ -3032,6 +3068,93 @@ static int wl1271_ssid_set(struct wl1271 *wl, struct sk_buff *skb, return 0; } +static void wl12xx_remove_ie(struct sk_buff *skb, u8 eid, int ieoffset) +{ + int len; + const u8 *next, *end = skb->data + skb->len; + u8 *ie = (u8 *)cfg80211_find_ie(eid, skb->data + ieoffset, + skb->len - ieoffset); + if (!ie) + return; + len = ie[1] + 2; + next = ie + len; + memmove(ie, next, end - next); + skb_trim(skb, skb->len - len); +} + +static void wl12xx_remove_vendor_ie(struct sk_buff *skb, + unsigned int oui, u8 oui_type, + int ieoffset) +{ + int len; + const u8 *next, *end = skb->data + skb->len; + u8 *ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type, + skb->data + ieoffset, + skb->len - ieoffset); + if (!ie) + return; + len = ie[1] + 2; + next = ie + len; + memmove(ie, next, end - next); + skb_trim(skb, skb->len - len); +} + +static int wl1271_ap_set_probe_resp_tmpl(struct wl1271 *wl, + u8 *probe_rsp_data, + size_t probe_rsp_len, + u32 rates) +{ + struct ieee80211_bss_conf *bss_conf = &wl->vif->bss_conf; + u8 probe_rsp_templ[WL1271_CMD_TEMPL_MAX_SIZE]; + int ssid_ie_offset, ie_offset, templ_len; + const u8 *ptr; + + /* no need to change probe response if the SSID is set correctly */ + if (wl->ssid_len > 0) + return wl1271_cmd_template_set(wl, + CMD_TEMPL_AP_PROBE_RESPONSE, + probe_rsp_data, + probe_rsp_len, 0, + rates); + + if (probe_rsp_len + bss_conf->ssid_len > WL1271_CMD_TEMPL_MAX_SIZE) { + wl1271_error("probe_rsp template too big"); + return -EINVAL; + } + + /* start searching from IE offset */ + ie_offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + + ptr = cfg80211_find_ie(WLAN_EID_SSID, probe_rsp_data + ie_offset, + probe_rsp_len - ie_offset); + if (!ptr) { + wl1271_error("No SSID in beacon!"); + return -EINVAL; + } + + ssid_ie_offset = ptr - probe_rsp_data; + ptr += (ptr[1] + 2); + + memcpy(probe_rsp_templ, probe_rsp_data, ssid_ie_offset); + + /* insert SSID from bss_conf */ + probe_rsp_templ[ssid_ie_offset] = WLAN_EID_SSID; + probe_rsp_templ[ssid_ie_offset + 1] = bss_conf->ssid_len; + memcpy(probe_rsp_templ + ssid_ie_offset + 2, + bss_conf->ssid, bss_conf->ssid_len); + templ_len = ssid_ie_offset + 2 + bss_conf->ssid_len; + + memcpy(probe_rsp_templ + ssid_ie_offset + 2 + bss_conf->ssid_len, + ptr, probe_rsp_len - (ptr - probe_rsp_data)); + templ_len += probe_rsp_len - (ptr - probe_rsp_data); + + return wl1271_cmd_template_set(wl, + CMD_TEMPL_AP_PROBE_RESPONSE, + probe_rsp_templ, + templ_len, 0, + rates); +} + static int wl1271_bss_erp_info_changed(struct wl1271 *wl, struct ieee80211_bss_conf *bss_conf, u32 changed) @@ -3088,6 +3211,7 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, if ((changed & BSS_CHANGED_BEACON)) { struct ieee80211_hdr *hdr; + u32 min_rate; int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable); struct sk_buff *beacon = ieee80211_beacon_get(wl->hw, vif); @@ -3103,28 +3227,46 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, dev_kfree_skb(beacon); goto out; } + min_rate = wl1271_tx_min_rate_get(wl, wl->basic_rate_set); tmpl_id = is_ap ? CMD_TEMPL_AP_BEACON : CMD_TEMPL_BEACON; ret = wl1271_cmd_template_set(wl, tmpl_id, beacon->data, beacon->len, 0, - wl1271_tx_min_rate_get(wl)); + min_rate); if (ret < 0) { dev_kfree_skb(beacon); goto out; } + /* remove TIM ie from probe response */ + wl12xx_remove_ie(beacon, WLAN_EID_TIM, ieoffset); + + /* + * remove p2p ie from probe response. + * the fw reponds to probe requests that don't include + * the p2p ie. probe requests with p2p ie will be passed, + * and will be responded by the supplicant (the spec + * forbids including the p2p ie when responding to probe + * requests that didn't include it). + */ + wl12xx_remove_vendor_ie(beacon, WLAN_OUI_WFA, + WLAN_OUI_TYPE_WFA_P2P, ieoffset); + hdr = (struct ieee80211_hdr *) beacon->data; hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); - - tmpl_id = is_ap ? CMD_TEMPL_AP_PROBE_RESPONSE : - CMD_TEMPL_PROBE_RESPONSE; - ret = wl1271_cmd_template_set(wl, - tmpl_id, - beacon->data, - beacon->len, 0, - wl1271_tx_min_rate_get(wl)); + if (is_ap) + ret = wl1271_ap_set_probe_resp_tmpl(wl, + beacon->data, + beacon->len, + min_rate); + else + ret = wl1271_cmd_template_set(wl, + CMD_TEMPL_PROBE_RESPONSE, + beacon->data, + beacon->len, 0, + min_rate); dev_kfree_skb(beacon); if (ret < 0) goto out; @@ -3145,8 +3287,10 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, if ((changed & BSS_CHANGED_BASIC_RATES)) { u32 rates = bss_conf->basic_rates; - wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates); - wl->basic_rate = wl1271_tx_min_rate_get(wl); + wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, rates, + wl->band); + wl->basic_rate = wl1271_tx_min_rate_get(wl, + wl->basic_rate_set); ret = wl1271_init_ap_rates(wl); if (ret < 0) { @@ -3328,12 +3472,15 @@ sta_not_found: * to use with control frames. */ rates = bss_conf->basic_rates; - wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, - rates); - wl->basic_rate = wl1271_tx_min_rate_get(wl); + wl->basic_rate_set = + wl1271_tx_enabled_rates_get(wl, rates, + wl->band); + wl->basic_rate = + wl1271_tx_min_rate_get(wl, wl->basic_rate_set); if (sta_rate_set) wl->rate_set = wl1271_tx_enabled_rates_get(wl, - sta_rate_set); + sta_rate_set, + wl->band); ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out; @@ -3380,7 +3527,8 @@ sta_not_found: /* revert back to minimum rates for the current band */ wl1271_set_band_rate(wl); - wl->basic_rate = wl1271_tx_min_rate_get(wl); + wl->basic_rate = + wl1271_tx_min_rate_get(wl, wl->basic_rate_set); ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) goto out; @@ -3431,11 +3579,13 @@ sta_not_found: if (bss_conf->ibss_joined) { u32 rates = bss_conf->basic_rates; - wl->basic_rate_set = wl1271_tx_enabled_rates_get(wl, - rates); - wl->basic_rate = wl1271_tx_min_rate_get(wl); + wl->basic_rate_set = + wl1271_tx_enabled_rates_get(wl, rates, + wl->band); + wl->basic_rate = + wl1271_tx_min_rate_get(wl, wl->basic_rate_set); - /* by default, use 11b rates */ + /* by default, use 11b + OFDM rates */ wl->rate_set = CONF_TX_IBSS_DEFAULT_RATES; ret = wl1271_acx_sta_rate_policies(wl); if (ret < 0) @@ -3665,7 +3815,8 @@ out: return ret; } -static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw) +static u64 wl1271_op_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct wl1271 *wl = hw->priv; @@ -3729,14 +3880,18 @@ static int wl1271_allocate_sta(struct wl1271 *wl, wl_sta->hlid = WL1271_AP_STA_HLID_START + id; *hlid = wl_sta->hlid; memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN); + wl->active_sta_count++; return 0; } -static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) +void wl1271_free_sta(struct wl1271 *wl, u8 hlid) { int id = hlid - WL1271_AP_STA_HLID_START; - if (WARN_ON(!test_bit(id, wl->ap_hlid_map))) + if (hlid < WL1271_AP_STA_HLID_START) + return; + + if (!test_bit(id, wl->ap_hlid_map)) return; clear_bit(id, wl->ap_hlid_map); @@ -3745,6 +3900,7 @@ static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) wl1271_tx_reset_link_queues(wl, hlid); __clear_bit(hlid, &wl->ap_ps_map); __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); + wl->active_sta_count--; } static int wl1271_op_sta_add(struct ieee80211_hw *hw, @@ -3950,6 +4106,29 @@ out: return ret; } +static int wl12xx_set_bitrate_mask(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const struct cfg80211_bitrate_mask *mask) +{ + struct wl1271 *wl = hw->priv; + int i; + + wl1271_debug(DEBUG_MAC80211, "mac80211 set_bitrate_mask 0x%x 0x%x", + mask->control[NL80211_BAND_2GHZ].legacy, + mask->control[NL80211_BAND_5GHZ].legacy); + + mutex_lock(&wl->mutex); + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + wl->bitrate_masks[i] = + wl1271_tx_enabled_rates_get(wl, + mask->control[i].legacy, + i); + mutex_unlock(&wl->mutex); + + return 0; +} + static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; @@ -4066,7 +4245,6 @@ static const u8 wl1271_rate_to_idx_2ghz[] = { /* 11n STA capabilities */ #define HW_RX_HIGHEST_RATE 72 -#ifdef CONFIG_WL12XX_HT #define WL12XX_HT_CAP { \ .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | \ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), \ @@ -4079,11 +4257,6 @@ static const u8 wl1271_rate_to_idx_2ghz[] = { .tx_params = IEEE80211_HT_MCS_TX_DEFINED, \ }, \ } -#else -#define WL12XX_HT_CAP { \ - .ht_supported = false, \ -} -#endif /* can't be const, mac80211 writes to this */ static struct ieee80211_supported_band wl1271_band_2ghz = { @@ -4231,6 +4404,7 @@ static const struct ieee80211_ops wl1271_ops = { .sta_remove = wl1271_op_sta_remove, .ampdu_action = wl1271_op_ampdu_action, .tx_frames_pending = wl1271_tx_frames_pending, + .set_bitrate_mask = wl12xx_set_bitrate_mask, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; @@ -4483,15 +4657,19 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_SUPPORTS_CQM_RSSI | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_SPECTRUM_MGMT | - IEEE80211_HW_AP_LINK_PS; + IEEE80211_HW_AP_LINK_PS | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_TX_AMPDU_SETUP_IN_HW; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP); + BIT(NL80211_IFTYPE_ADHOC) | BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); wl->hw->wiphy->max_scan_ssids = 1; - wl->hw->wiphy->max_sched_scan_ssids = 8; + wl->hw->wiphy->max_sched_scan_ssids = 16; + wl->hw->wiphy->max_match_sets = 16; /* * Maximum length of elements in scanning probe request templates * should be the maximum length possible for a template, without @@ -4500,6 +4678,8 @@ int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->max_scan_ie_len = WL1271_CMD_TEMPL_DFLT_SIZE - sizeof(struct ieee80211_header); + wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; + /* make sure all our channels fit in the scanned_ch bitmask */ BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + ARRAY_SIZE(wl1271_channels_5ghz) > @@ -4543,6 +4723,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void) int i, j, ret; unsigned int order; + BUILD_BUG_ON(AP_MAX_LINKS > WL12XX_MAX_LINKS); + hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); if (!hw) { wl1271_error("could not alloc ieee80211_hw"); @@ -4625,6 +4807,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->session_counter = 0; wl->ap_bcast_hlid = WL12XX_INVALID_LINK_ID; wl->ap_global_hlid = WL12XX_INVALID_LINK_ID; + wl->active_sta_count = 0; setup_timer(&wl->rx_streaming_timer, wl1271_rx_streaming_timer, (unsigned long) wl); wl->fwlog_size = 0; @@ -4644,6 +4827,8 @@ struct ieee80211_hw *wl1271_alloc_hw(void) /* Apply default driver configuration. */ wl1271_conf_init(wl); + wl->bitrate_masks[IEEE80211_BAND_2GHZ] = wl->conf.tx.basic_rate; + wl->bitrate_masks[IEEE80211_BAND_5GHZ] = wl->conf.tx.basic_rate_5; order = get_order(WL1271_AGGR_BUFFER_SIZE); wl->aggr_buf = (u8 *)__get_free_pages(GFP_KERNEL, order); @@ -4776,6 +4961,9 @@ module_param_named(fwlog, fwlog_param, charp, 0); MODULE_PARM_DESC(keymap, "FW logger options: continuous, ondemand, dbgpins or disable"); +module_param(bug_on_recovery, bool, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(bug_on_recovery, "BUG() on fw recovery"); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index 4b720b1..c15ebf2 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -199,15 +199,19 @@ static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) unsigned long flags; int filtered[NUM_TX_QUEUES]; - /* filter all frames currently the low level queus for this hlid */ + /* filter all frames currently in the low level queues for this hlid */ for (i = 0; i < NUM_TX_QUEUES; i++) { filtered[i] = 0; while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { + filtered[i]++; + + if (WARN_ON(wl12xx_is_dummy_packet(wl, skb))) + continue; + info = IEEE80211_SKB_CB(skb); info->flags |= IEEE80211_TX_STAT_TX_FILTERED; info->status.rates[0].idx = -1; ieee80211_tx_status_ni(wl->hw, skb); - filtered[i]++; } } diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 78d8410..dee4cfe 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -66,11 +66,9 @@ static void wl1271_rx_status(struct wl1271 *wl, status->rate_idx = wl1271_rate_to_idx(desc->rate, status->band); -#ifdef CONFIG_WL12XX_HT /* 11n support */ if (desc->rate <= CONF_HW_RXTX_RATE_MCS0) status->flag |= RX_FLAG_HT; -#endif status->signal = desc->rssi; @@ -107,6 +105,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, u8 beacon = 0; u8 is_data = 0; u8 reserved = unaligned ? NET_IP_ALIGN : 0; + u16 seq_num; /* * In PLT mode we seem to get frames and mac80211 warns about them, @@ -169,9 +168,11 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length, wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); - wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s", skb, + seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; + wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d", skb, skb->len - desc->pad_len, - beacon ? "beacon" : ""); + beacon ? "beacon" : "", + seq_num); skb_trim(skb, skb->len - desc->pad_len); diff --git a/drivers/net/wireless/wl12xx/scan.c b/drivers/net/wireless/wl12xx/scan.c index af4ad23..128ccb7 100644 --- a/drivers/net/wireless/wl12xx/scan.c +++ b/drivers/net/wireless/wl12xx/scan.c @@ -28,6 +28,7 @@ #include "scan.h" #include "acx.h" #include "ps.h" +#include "tx.h" void wl1271_scan_complete_work(struct work_struct *work) { @@ -65,8 +66,9 @@ void wl1271_scan_complete_work(struct work_struct *work) /* return to ROC if needed */ is_sta = (wl->bss_type == BSS_TYPE_STA_BSS); is_ibss = (wl->bss_type == BSS_TYPE_IBSS); - if ((is_sta && !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) || - (is_ibss && !test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags))) { + if (((is_sta && !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) || + (is_ibss && !test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags))) && + !test_bit(wl->dev_role_id, wl->roc_map)) { /* restore remain on channel */ wl12xx_cmd_role_start_dev(wl); wl12xx_roc(wl, wl->dev_role_id); @@ -98,14 +100,18 @@ static int wl1271_get_scan_channels(struct wl1271 *wl, for (i = 0, j = 0; i < req->n_channels && j < WL1271_SCAN_MAX_CHANNELS; i++) { - flags = req->channels[i]->flags; if (!test_bit(i, wl->scan.scanned_ch) && !(flags & IEEE80211_CHAN_DISABLED) && - ((!!(flags & IEEE80211_CHAN_PASSIVE_SCAN)) == passive) && - (req->channels[i]->band == band)) { - + (req->channels[i]->band == band) && + /* + * In passive scans, we scan all remaining + * channels, even if not marked as such. + * In active scans, we only scan channels not + * marked as passive. + */ + (passive || !(flags & IEEE80211_CHAN_PASSIVE_SCAN))) { wl1271_debug(DEBUG_SCAN, "band %d, center_freq %d ", req->channels[i]->band, req->channels[i]->center_freq); @@ -157,6 +163,10 @@ static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band, int ret; u16 scan_options = 0; + /* skip active scans if we don't have SSIDs */ + if (!passive && wl->scan.req->n_ssids == 0) + return WL1271_NOTHING_TO_SCAN; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); if (!cmd || !trigger) { @@ -164,11 +174,7 @@ static int wl1271_scan_send(struct wl1271 *wl, enum ieee80211_band band, goto out; } - /* We always use high priority scans */ - scan_options = WL1271_SCAN_OPT_PRIORITY_HIGH; - - /* No SSIDs means that we have a forced passive scan */ - if (passive || wl->scan.req->n_ssids == 0) + if (passive) scan_options |= WL1271_SCAN_OPT_PASSIVE; if (WARN_ON(wl->role_id == WL12XX_INVALID_ROLE_ID)) { @@ -238,14 +244,17 @@ out: void wl1271_scan_stm(struct wl1271 *wl) { int ret = 0; + enum ieee80211_band band; + u32 rate; 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); + band = IEEE80211_BAND_2GHZ; + rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]); + ret = wl1271_scan_send(wl, band, false, rate); if (ret == WL1271_NOTHING_TO_SCAN) { wl->scan.state = WL1271_SCAN_STATE_2GHZ_PASSIVE; wl1271_scan_stm(wl); @@ -254,8 +263,9 @@ void wl1271_scan_stm(struct wl1271 *wl) break; case WL1271_SCAN_STATE_2GHZ_PASSIVE: - ret = wl1271_scan_send(wl, IEEE80211_BAND_2GHZ, true, - wl->conf.tx.basic_rate); + band = IEEE80211_BAND_2GHZ; + rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]); + ret = wl1271_scan_send(wl, band, true, rate); if (ret == WL1271_NOTHING_TO_SCAN) { if (wl->enable_11a) wl->scan.state = WL1271_SCAN_STATE_5GHZ_ACTIVE; @@ -267,8 +277,9 @@ void wl1271_scan_stm(struct wl1271 *wl) break; case WL1271_SCAN_STATE_5GHZ_ACTIVE: - ret = wl1271_scan_send(wl, IEEE80211_BAND_5GHZ, false, - wl->conf.tx.basic_rate_5); + band = IEEE80211_BAND_5GHZ; + rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]); + ret = wl1271_scan_send(wl, band, false, rate); if (ret == WL1271_NOTHING_TO_SCAN) { wl->scan.state = WL1271_SCAN_STATE_5GHZ_PASSIVE; wl1271_scan_stm(wl); @@ -277,8 +288,9 @@ void wl1271_scan_stm(struct wl1271 *wl) break; case WL1271_SCAN_STATE_5GHZ_PASSIVE: - ret = wl1271_scan_send(wl, IEEE80211_BAND_5GHZ, true, - wl->conf.tx.basic_rate_5); + band = IEEE80211_BAND_5GHZ; + rate = wl1271_tx_min_rate_get(wl, wl->bitrate_masks[band]); + ret = wl1271_scan_send(wl, band, true, rate); if (ret == WL1271_NOTHING_TO_SCAN) { wl->scan.state = WL1271_SCAN_STATE_DONE; wl1271_scan_stm(wl); @@ -473,34 +485,86 @@ wl1271_scan_sched_scan_channels(struct wl1271 *wl, cfg->passive[2] || cfg->active[2]; } -/* Returns 0 if no wildcard is used, 1 if wildcard is used or a - * negative value on error */ +/* Returns the scan type to be used or a negative value on error */ static int wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl, struct cfg80211_sched_scan_request *req) { struct wl1271_cmd_sched_scan_ssid_list *cmd = NULL; - struct cfg80211_ssid *ssid = req->ssids; - int ret, wildcard = 0; + struct cfg80211_match_set *sets = req->match_sets; + struct cfg80211_ssid *ssids = req->ssids; + int ret = 0, type, i, j, n_match_ssids = 0; wl1271_debug(DEBUG_CMD, "cmd sched scan ssid list"); + /* count the match sets that contain SSIDs */ + for (i = 0; i < req->n_match_sets; i++) + if (sets[i].ssid.ssid_len > 0) + n_match_ssids++; + + /* No filter, no ssids or only bcast ssid */ + if (!n_match_ssids && + (!req->n_ssids || + (req->n_ssids == 1 && req->ssids[0].ssid_len == 0))) { + type = SCAN_SSID_FILTER_ANY; + goto out; + } + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); - if (!cmd) - return -ENOMEM; + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + if (!n_match_ssids) { + /* No filter, with ssids */ + type = SCAN_SSID_FILTER_DISABLED; + + for (i = 0; i < req->n_ssids; i++) { + cmd->ssids[cmd->n_ssids].type = (ssids[i].ssid_len) ? + SCAN_SSID_TYPE_HIDDEN : SCAN_SSID_TYPE_PUBLIC; + cmd->ssids[cmd->n_ssids].len = ssids[i].ssid_len; + memcpy(cmd->ssids[cmd->n_ssids].ssid, ssids[i].ssid, + ssids[i].ssid_len); + cmd->n_ssids++; + } + } else { + type = SCAN_SSID_FILTER_LIST; + + /* Add all SSIDs from the filters */ + for (i = 0; i < req->n_match_sets; i++) { + /* ignore sets without SSIDs */ + if (!sets[i].ssid.ssid_len) + continue; - while ((cmd->n_ssids < req->n_ssids) && ssid) { - if (ssid->ssid_len == 0) { - wildcard = 1; cmd->ssids[cmd->n_ssids].type = SCAN_SSID_TYPE_PUBLIC; - } else { - cmd->ssids[cmd->n_ssids].type = SCAN_SSID_TYPE_HIDDEN; + cmd->ssids[cmd->n_ssids].len = sets[i].ssid.ssid_len; + memcpy(cmd->ssids[cmd->n_ssids].ssid, + sets[i].ssid.ssid, sets[i].ssid.ssid_len); + cmd->n_ssids++; + } + if ((req->n_ssids > 1) || + (req->n_ssids == 1 && req->ssids[0].ssid_len > 0)) { + /* + * Mark all the SSIDs passed in the SSID list as HIDDEN, + * so they're used in probe requests. + */ + for (i = 0; i < req->n_ssids; i++) { + for (j = 0; j < cmd->n_ssids; j++) + if (!memcmp(req->ssids[i].ssid, + cmd->ssids[j].ssid, + req->ssids[i].ssid_len)) { + cmd->ssids[j].type = + SCAN_SSID_TYPE_HIDDEN; + break; + } + /* Fail if SSID isn't present in the filters */ + if (j == req->n_ssids) { + ret = -EINVAL; + goto out_free; + } + } } - cmd->ssids[cmd->n_ssids].len = ssid->ssid_len; - memcpy(cmd->ssids[cmd->n_ssids].ssid, ssid->ssid, - ssid->ssid_len); - ssid++; - cmd->n_ssids++; } wl1271_dump(DEBUG_SCAN, "SSID_LIST: ", cmd, sizeof(*cmd)); @@ -509,13 +573,15 @@ wl12xx_scan_sched_scan_ssid_list(struct wl1271 *wl, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("cmd sched scan ssid list failed"); - goto out; + goto out_free; } - ret = wildcard; -out: +out_free: kfree(cmd); - return ret; +out: + if (ret < 0) + return ret; + return type; } int wl1271_scan_sched_scan_config(struct wl1271 *wl, @@ -550,21 +616,13 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl, cfg->intervals[i] = cpu_to_le32(req->interval); cfg->ssid_len = 0; - if (req->n_ssids == 0) { - wl1271_debug(DEBUG_SCAN, "using SCAN_SSID_FILTER_ANY"); - cfg->filter_type = SCAN_SSID_FILTER_ANY; - } else { - ret = wl12xx_scan_sched_scan_ssid_list(wl, req); - if (ret < 0) - goto out; - if (ret) { - wl1271_debug(DEBUG_SCAN, "using SCAN_SSID_FILTER_DISABLED"); - cfg->filter_type = SCAN_SSID_FILTER_DISABLED; - } else { - wl1271_debug(DEBUG_SCAN, "using SCAN_SSID_FILTER_LIST"); - cfg->filter_type = SCAN_SSID_FILTER_LIST; - } - } + ret = wl12xx_scan_sched_scan_ssid_list(wl, req); + if (ret < 0) + goto out; + + cfg->filter_type = ret; + + wl1271_debug(DEBUG_SCAN, "filter_type = %d", cfg->filter_type); if (!wl1271_scan_sched_scan_channels(wl, req, cfg)) { wl1271_error("scan channel list is empty"); diff --git a/drivers/net/wireless/wl12xx/sdio_test.c b/drivers/net/wireless/wl12xx/sdio_test.c index c361049..f25d5d9 100644 --- a/drivers/net/wireless/wl12xx/sdio_test.c +++ b/drivers/net/wireless/wl12xx/sdio_test.c @@ -30,6 +30,7 @@ #include <linux/mmc/sdio_func.h> #include <linux/mmc/sdio_ids.h> #include <linux/mmc/card.h> +#include <linux/mmc/host.h> #include <linux/gpio.h> #include <linux/wl12xx.h> #include <linux/kthread.h> @@ -142,14 +143,23 @@ static int wl1271_sdio_set_power(struct wl1271 *wl, bool enable) ret = pm_runtime_get_sync(&func->dev); if (ret < 0) goto out; + + /* Runtime PM might be disabled, power up the card manually */ + ret = mmc_power_restore_host(func->card->host); + if (ret < 0) + goto out; + sdio_claim_host(func); sdio_enable_func(func); - sdio_release_host(func); } else { - sdio_claim_host(func); sdio_disable_func(func); sdio_release_host(func); + /* Runtime PM might be disabled, power off the card manually */ + ret = mmc_power_save_host(func->card->host); + if (ret < 0) + goto out; + /* Power down the card */ ret = pm_runtime_put_sync(&func->dev); } @@ -433,7 +443,6 @@ static int __devinit wl1271_probe(struct sdio_func *func, sdio_set_drvdata(func, wl_test); - /* power up the device */ ret = wl1271_chip_wakeup(wl); if (ret) { diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 08227e6..bad9e29 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -30,6 +30,7 @@ #include "reg.h" #include "ps.h" #include "tx.h" +#include "event.h" static int wl1271_set_default_wep_key(struct wl1271 *wl, u8 id) { @@ -80,8 +81,7 @@ static int wl1271_tx_update_filters(struct wl1271 *wl, struct ieee80211_hdr *hdr; int ret; - hdr = (struct ieee80211_hdr *)(skb->data + - sizeof(struct wl1271_tx_hw_descr)); + hdr = (struct ieee80211_hdr *)skb->data; /* * stop bssid-based filtering before transmitting authentication @@ -125,25 +125,31 @@ static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid) { - bool fw_ps; + bool fw_ps, single_sta; u8 tx_pkts; /* only regulate station links */ if (hlid < WL1271_AP_STA_HLID_START) return; + if (WARN_ON(!wl1271_is_active_sta(wl, hlid))) + return; + fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); tx_pkts = wl->links[hlid].allocated_pkts; + single_sta = (wl->active_sta_count == 1); /* * if in FW PS and there is enough data in FW we can put the link * into high-level PS and clean out its TX queues. + * Make an exception if this is the only connected station. In this + * case FW-memory congestion is not a problem. */ - if (fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) + if (!single_sta && fw_ps && tx_pkts >= WL1271_PS_STA_MAX_PACKETS) wl1271_ps_link_start(wl, hlid, true); } -static bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb) +bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb) { return wl->dummy_packet == skb; } @@ -174,14 +180,20 @@ u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct sk_buff *skb) static u8 wl1271_tx_get_hlid(struct wl1271 *wl, struct sk_buff *skb) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + if (wl12xx_is_dummy_packet(wl, skb)) return wl->system_hlid; if (wl->bss_type == BSS_TYPE_AP_BSS) return wl12xx_tx_get_hlid_ap(wl, skb); - if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) || - test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags)) + wl1271_tx_update_filters(wl, skb); + + if ((test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags) || + test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags)) && + !ieee80211_is_auth(hdr->frame_control) && + !ieee80211_is_assoc_req(hdr->frame_control)) return wl->sta_hlid; else return wl->dev_hlid; @@ -416,8 +428,6 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, if (wl->bss_type == BSS_TYPE_AP_BSS) { wl1271_tx_ap_update_inconnection_sta(wl, skb); wl1271_tx_regulate_link(wl, hlid); - } else { - wl1271_tx_update_filters(wl, skb); } /* @@ -440,20 +450,20 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, return total_len; } -u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set) +u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, + enum ieee80211_band rate_band) { struct ieee80211_supported_band *band; u32 enabled_rates = 0; int bit; - band = wl->hw->wiphy->bands[wl->band]; + band = wl->hw->wiphy->bands[rate_band]; for (bit = 0; bit < band->n_bitrates; bit++) { if (rate_set & 0x1) enabled_rates |= band->bitrates[bit].hw_value; rate_set >>= 1; } -#ifdef CONFIG_WL12XX_HT /* MCS rates indication are on bits 16 - 23 */ rate_set >>= HW_HT_RATES_OFFSET - band->n_bitrates; @@ -462,7 +472,6 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set) enabled_rates |= (CONF_HW_BIT_RATE_MCS_0 << bit); rate_set >>= 1; } -#endif return enabled_rates; } @@ -886,6 +895,7 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues) /* TX failure */ if (wl->bss_type == BSS_TYPE_AP_BSS) { for (i = 0; i < AP_MAX_LINKS; i++) { + wl1271_free_sta(wl, i); wl1271_tx_reset_link_queues(wl, i); wl->links[i].allocated_pkts = 0; wl->links[i].prev_freed_pkts = 0; @@ -905,10 +915,14 @@ void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues) ieee80211_tx_status_ni(wl->hw, skb); } } - wl->tx_queue_count[i] = 0; } + + wl->ba_rx_bitmap = 0; } + for (i = 0; i < NUM_TX_QUEUES; i++) + wl->tx_queue_count[i] = 0; + wl->stopped_queues_map = 0; /* @@ -976,20 +990,10 @@ void wl1271_tx_flush(struct wl1271 *wl) wl1271_warning("Unable to flush all TX buffers, timed out."); } -u32 wl1271_tx_min_rate_get(struct wl1271 *wl) +u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set) { - int i; - u32 rate = 0; - - if (!wl->basic_rate_set) { - WARN_ON(1); - wl->basic_rate_set = wl->conf.tx.basic_rate; - } - - for (i = 0; !rate; i++) { - if ((wl->basic_rate_set >> i) & 0x1) - rate = 1 << i; - } + if (WARN_ON(!rate_set)) + return 0; - return rate; + return BIT(__ffs(rate_set)); } diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 6519be4b..dc4f09a 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -209,10 +209,15 @@ void wl1271_tx_complete(struct wl1271 *wl); void wl1271_tx_reset(struct wl1271 *wl, bool reset_tx_queues); void wl1271_tx_flush(struct wl1271 *wl); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); -u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set); -u32 wl1271_tx_min_rate_get(struct wl1271 *wl); +u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set, + enum ieee80211_band rate_band); +u32 wl1271_tx_min_rate_get(struct wl1271 *wl, u32 rate_set); u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct sk_buff *skb); void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid); void wl1271_handle_tx_low_watermark(struct wl1271 *wl); +bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb); + +/* from main.c */ +void wl1271_free_sta(struct wl1271 *wl, u8 hlid); #endif diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index fb2753c..997f532 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -138,7 +138,7 @@ extern u32 wl12xx_debug_level; #define WL1271_DEFAULT_DTIM_PERIOD 1 #define WL12XX_MAX_ROLES 4 -#define WL12XX_MAX_LINKS 8 +#define WL12XX_MAX_LINKS 12 #define WL12XX_INVALID_ROLE_ID 0xff #define WL12XX_INVALID_LINK_ID 0xff @@ -234,14 +234,14 @@ struct wl1271_stats { #define NUM_TX_QUEUES 4 #define NUM_RX_PKT_DESC 8 -#define AP_MAX_STATIONS 5 +#define AP_MAX_STATIONS 8 /* Broadcast and Global links + system link + links to stations */ /* * TODO: when WL1271_AP_STA_HLID_START is no longer constant, change all * the places that use this. */ -#define AP_MAX_LINKS (AP_MAX_STATIONS + 3) +#define AP_MAX_LINKS (AP_MAX_STATIONS + WL1271_AP_STA_HLID_START) /* FW status registers */ struct wl12xx_fw_status { @@ -279,7 +279,7 @@ struct wl12xx_fw_status { /* Cumulative counter of released Voice memory blocks */ u8 tx_voice_released_blks; - u8 padding_1[7]; + u8 padding_1[3]; __le32 log_start_addr; } __packed; @@ -402,6 +402,7 @@ struct wl1271 { u8 mac_addr[ETH_ALEN]; u8 bss_type; u8 set_bss_type; + u8 p2p; /* we are using p2p role */ u8 ssid[IEEE80211_MAX_SSID_LEN + 1]; u8 ssid_len; int channel; @@ -525,6 +526,7 @@ struct wl1271 { u32 basic_rate_set; u32 basic_rate; u32 rate_set; + u32 bitrate_masks[IEEE80211_NUM_BANDS]; /* The current band */ enum ieee80211_band band; @@ -626,6 +628,9 @@ struct wl1271 { /* number of currently active RX BA sessions */ int ba_rx_session_count; + + /* AP-mode - number of currently connected stations */ + int active_sta_count; }; struct wl1271_station { diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index 6bc7c92..98fbf54 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -1781,7 +1781,7 @@ static int wl3501_get_encode(struct net_device *dev, keys, len_keys); if (rc) goto out; - tocopy = min_t(u8, len_keys, wrqu->encoding.length); + tocopy = min_t(u16, len_keys, wrqu->encoding.length); tocopy = min_t(u8, tocopy, 100); wrqu->encoding.length = tocopy; memcpy(extra, keys, tocopy); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index cabfae1..0a70149 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -1332,7 +1332,7 @@ static void zd_op_bss_info_changed(struct ieee80211_hw *hw, } } -static u64 zd_op_get_tsf(struct ieee80211_hw *hw) +static u64 zd_op_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct zd_mac *mac = zd_hw_mac(hw); return zd_chip_get_tsf(&mac->chip); diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 2acff43..5af95927 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -27,4 +27,15 @@ config NFC_PN533 Say Y here to compile support for PN533 devices into the kernel or say M to compile it as module (pn533). +config NFC_WILINK + tristate "Texas Instruments NFC WiLink driver" + depends on TI_ST && NFC_NCI + help + This enables the NFC driver for Texas Instrument's BT/FM/GPS/NFC + combo devices. This makes use of shared transport line discipline + core driver to communicate with the NFC core of the combo chip. + + Say Y here to compile support for Texas Instrument's NFC WiLink driver + into the kernel or say M to compile it as module. + endmenu diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 8ef446d..ab99e85 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile @@ -4,5 +4,6 @@ obj-$(CONFIG_PN544_NFC) += pn544.o obj-$(CONFIG_NFC_PN533) += pn533.o +obj-$(CONFIG_NFC_WILINK) += nfcwilink.o ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG diff --git a/drivers/nfc/nfcwilink.c b/drivers/nfc/nfcwilink.c new file mode 100644 index 0000000..5b0f1ff --- /dev/null +++ b/drivers/nfc/nfcwilink.c @@ -0,0 +1,342 @@ +/* + * Texas Instrument's NFC Driver For Shared Transport. + * + * NFC Driver acts as interface between NCI core and + * TI Shared Transport Layer. + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias <ilane@ti.com> + * + * Acknowledgements: + * This file is based on btwilink.c, which was written + * by Raja Mani and Pavan Savoy. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <linux/platform_device.h> +#include <linux/nfc.h> +#include <net/nfc/nci.h> +#include <net/nfc/nci_core.h> +#include <linux/ti_wilink_st.h> + +#define NFCWILINK_CHNL 12 +#define NFCWILINK_OPCODE 7 +#define NFCWILINK_MAX_FRAME_SIZE 300 +#define NFCWILINK_HDR_LEN 4 +#define NFCWILINK_OFFSET_LEN_IN_HDR 1 +#define NFCWILINK_LEN_SIZE 2 +#define NFCWILINK_REGISTER_TIMEOUT 8000 /* 8 sec */ + +struct nfcwilink_hdr { + u8 chnl; + u8 opcode; + u16 len; +} __packed; + +struct nfcwilink { + struct platform_device *pdev; + struct nci_dev *ndev; + unsigned long flags; + + char st_register_cb_status; + long (*st_write) (struct sk_buff *); + struct completion st_register_completed; +}; + +/* NFCWILINK driver flags */ +enum { + NFCWILINK_RUNNING, +}; + +/* Called by ST when registration is complete */ +static void nfcwilink_register_complete(void *priv_data, char data) +{ + struct nfcwilink *drv = priv_data; + + nfc_dev_dbg(&drv->pdev->dev, "register_complete entry"); + + /* store ST registration status */ + drv->st_register_cb_status = data; + + /* complete the wait in nfc_st_open() */ + complete(&drv->st_register_completed); +} + +/* Called by ST when receive data is available */ +static long nfcwilink_receive(void *priv_data, struct sk_buff *skb) +{ + struct nfcwilink *drv = priv_data; + int rc; + + nfc_dev_dbg(&drv->pdev->dev, "receive entry, len %d", skb->len); + + if (!skb) + return -EFAULT; + + if (!drv) { + kfree_skb(skb); + return -EFAULT; + } + + /* strip the ST header + (apart for the chnl byte, which is not received in the hdr) */ + skb_pull(skb, (NFCWILINK_HDR_LEN-1)); + + skb->dev = (void *) drv->ndev; + + /* Forward skb to NCI core layer */ + rc = nci_recv_frame(skb); + if (rc < 0) { + nfc_dev_err(&drv->pdev->dev, "nci_recv_frame failed %d", rc); + return rc; + } + + return 0; +} + +/* protocol structure registered with ST */ +static struct st_proto_s nfcwilink_proto = { + .chnl_id = NFCWILINK_CHNL, + .max_frame_size = NFCWILINK_MAX_FRAME_SIZE, + .hdr_len = (NFCWILINK_HDR_LEN-1), /* not including chnl byte */ + .offset_len_in_hdr = NFCWILINK_OFFSET_LEN_IN_HDR, + .len_size = NFCWILINK_LEN_SIZE, + .reserve = 0, + .recv = nfcwilink_receive, + .reg_complete_cb = nfcwilink_register_complete, + .write = NULL, +}; + +static int nfcwilink_open(struct nci_dev *ndev) +{ + struct nfcwilink *drv = nci_get_drvdata(ndev); + unsigned long comp_ret; + int rc; + + nfc_dev_dbg(&drv->pdev->dev, "open entry"); + + if (test_and_set_bit(NFCWILINK_RUNNING, &drv->flags)) { + rc = -EBUSY; + goto exit; + } + + nfcwilink_proto.priv_data = drv; + + init_completion(&drv->st_register_completed); + drv->st_register_cb_status = -EINPROGRESS; + + rc = st_register(&nfcwilink_proto); + if (rc < 0) { + if (rc == -EINPROGRESS) { + comp_ret = wait_for_completion_timeout( + &drv->st_register_completed, + msecs_to_jiffies(NFCWILINK_REGISTER_TIMEOUT)); + + nfc_dev_dbg(&drv->pdev->dev, + "wait_for_completion_timeout returned %ld", + comp_ret); + + if (comp_ret == 0) { + /* timeout */ + rc = -ETIMEDOUT; + goto clear_exit; + } else if (drv->st_register_cb_status != 0) { + rc = drv->st_register_cb_status; + nfc_dev_err(&drv->pdev->dev, + "st_register_cb failed %d", rc); + goto clear_exit; + } + } else { + nfc_dev_err(&drv->pdev->dev, + "st_register failed %d", rc); + goto clear_exit; + } + } + + /* st_register MUST fill the write callback */ + BUG_ON(nfcwilink_proto.write == NULL); + drv->st_write = nfcwilink_proto.write; + + goto exit; + +clear_exit: + clear_bit(NFCWILINK_RUNNING, &drv->flags); + +exit: + return rc; +} + +static int nfcwilink_close(struct nci_dev *ndev) +{ + struct nfcwilink *drv = nci_get_drvdata(ndev); + int rc; + + nfc_dev_dbg(&drv->pdev->dev, "close entry"); + + if (!test_and_clear_bit(NFCWILINK_RUNNING, &drv->flags)) + return 0; + + rc = st_unregister(&nfcwilink_proto); + if (rc) + nfc_dev_err(&drv->pdev->dev, "st_unregister failed %d", rc); + + drv->st_write = NULL; + + return rc; +} + +static int nfcwilink_send(struct sk_buff *skb) +{ + struct nci_dev *ndev = (struct nci_dev *)skb->dev; + struct nfcwilink *drv = nci_get_drvdata(ndev); + struct nfcwilink_hdr hdr = {NFCWILINK_CHNL, NFCWILINK_OPCODE, 0x0000}; + long len; + + nfc_dev_dbg(&drv->pdev->dev, "send entry, len %d", skb->len); + + if (!test_bit(NFCWILINK_RUNNING, &drv->flags)) + return -EBUSY; + + /* add the ST hdr to the start of the buffer */ + hdr.len = skb->len; + memcpy(skb_push(skb, NFCWILINK_HDR_LEN), &hdr, NFCWILINK_HDR_LEN); + + /* Insert skb to shared transport layer's transmit queue. + * Freeing skb memory is taken care in shared transport layer, + * so don't free skb memory here. + */ + len = drv->st_write(skb); + if (len < 0) { + kfree_skb(skb); + nfc_dev_err(&drv->pdev->dev, "st_write failed %ld", len); + return -EFAULT; + } + + return 0; +} + +static struct nci_ops nfcwilink_ops = { + .open = nfcwilink_open, + .close = nfcwilink_close, + .send = nfcwilink_send, +}; + +static int nfcwilink_probe(struct platform_device *pdev) +{ + static struct nfcwilink *drv; + int rc; + u32 protocols; + + nfc_dev_dbg(&pdev->dev, "probe entry"); + + drv = kzalloc(sizeof(struct nfcwilink), GFP_KERNEL); + if (!drv) { + rc = -ENOMEM; + goto exit; + } + + drv->pdev = pdev; + + protocols = NFC_PROTO_JEWEL_MASK + | NFC_PROTO_MIFARE_MASK | NFC_PROTO_FELICA_MASK + | NFC_PROTO_ISO14443_MASK + | NFC_PROTO_NFC_DEP_MASK; + + drv->ndev = nci_allocate_device(&nfcwilink_ops, + protocols, + NFCWILINK_HDR_LEN, + 0); + if (!drv->ndev) { + nfc_dev_err(&pdev->dev, "nci_allocate_device failed"); + rc = -ENOMEM; + goto free_exit; + } + + nci_set_parent_dev(drv->ndev, &pdev->dev); + nci_set_drvdata(drv->ndev, drv); + + rc = nci_register_device(drv->ndev); + if (rc < 0) { + nfc_dev_err(&pdev->dev, "nci_register_device failed %d", rc); + goto free_dev_exit; + } + + dev_set_drvdata(&pdev->dev, drv); + + goto exit; + +free_dev_exit: + nci_free_device(drv->ndev); + +free_exit: + kfree(drv); + +exit: + return rc; +} + +static int nfcwilink_remove(struct platform_device *pdev) +{ + struct nfcwilink *drv = dev_get_drvdata(&pdev->dev); + struct nci_dev *ndev; + + nfc_dev_dbg(&pdev->dev, "remove entry"); + + if (!drv) + return -EFAULT; + + ndev = drv->ndev; + + nci_unregister_device(ndev); + nci_free_device(ndev); + + kfree(drv); + + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static struct platform_driver nfcwilink_driver = { + .probe = nfcwilink_probe, + .remove = nfcwilink_remove, + .driver = { + .name = "nfcwilink", + .owner = THIS_MODULE, + }, +}; + +/* ------- Module Init/Exit interfaces ------ */ +static int __init nfcwilink_init(void) +{ + printk(KERN_INFO "NFC Driver for TI WiLink"); + + return platform_driver_register(&nfcwilink_driver); +} + +static void __exit nfcwilink_exit(void) +{ + platform_driver_unregister(&nfcwilink_driver); +} + +module_init(nfcwilink_init); +module_exit(nfcwilink_exit); + +/* ------ Module Info ------ */ + +MODULE_AUTHOR("Ilan Elias <ilane@ti.com>"); +MODULE_DESCRIPTION("NFC Driver for TI Shared Transport"); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index f81a93e..7bcb1fe 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c @@ -28,7 +28,7 @@ #include <linux/usb.h> #include <linux/nfc.h> #include <linux/netdevice.h> -#include <net/nfc.h> +#include <net/nfc/nfc.h> #define VERSION "0.1" @@ -1432,6 +1432,8 @@ static int pn533_set_configuration(struct pn533 *dev, u8 cfgitem, u8 *cfgdata, } struct nfc_ops pn533_nfc_ops = { + .dev_up = NULL, + .dev_down = NULL, .start_poll = pn533_start_poll, .stop_poll = pn533_stop_poll, .activate_target = pn533_activate_target, diff --git a/drivers/staging/brcm80211/brcmsmac/mac80211_if.c b/drivers/staging/brcm80211/brcmsmac/mac80211_if.c index d6de44e..315dd91 100644 --- a/drivers/staging/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/staging/brcm80211/brcmsmac/mac80211_if.c @@ -133,7 +133,8 @@ static int brcms_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw); static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw); -static void brcms_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf); +static void brcms_ops_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf); static int brcms_ops_get_stats(struct ieee80211_hw *hw, struct ieee80211_low_level_stats *stats); static void brcms_ops_sta_notify(struct ieee80211_hw *hw, @@ -142,7 +143,8 @@ static void brcms_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_sta *sta); static int brcms_ops_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params); -static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw); +static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); static int brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); static int brcms_ops_sta_remove(struct ieee80211_hw *hw, @@ -516,7 +518,8 @@ static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw) return; } -static void brcms_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf) +static void brcms_ops_set_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u64 tsf) { wiphy_err(hw->wiphy, "%s: Enter\n", __func__); return; @@ -565,7 +568,8 @@ brcms_ops_conf_tx(struct ieee80211_hw *hw, u16 queue, return 0; } -static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw) +static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { wiphy_err(hw->wiphy, "%s: Enter\n", __func__); return 0; diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index 3724e1e..a2e8bd4 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -277,7 +277,7 @@ static int wbsoft_config(struct ieee80211_hw *dev, u32 changed) return 0; } -static u64 wbsoft_get_tsf(struct ieee80211_hw *dev) +static u64 wbsoft_get_tsf(struct ieee80211_hw *dev, struct ieee80211_vif *vif) { printk("wbsoft_get_tsf called\n"); return 0; diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h index a7ae33d..1526d96 100644 --- a/include/linux/bcma/bcma_driver_chipcommon.h +++ b/include/linux/bcma/bcma_driver_chipcommon.h @@ -378,4 +378,13 @@ u32 bcma_chipco_gpio_polarity(struct bcma_drv_cc *cc, u32 mask, u32 value); /* PMU support */ extern void bcma_pmu_init(struct bcma_drv_cc *cc); +extern void bcma_chipco_pll_write(struct bcma_drv_cc *cc, u32 offset, + u32 value); +extern void bcma_chipco_pll_maskset(struct bcma_drv_cc *cc, u32 offset, + u32 mask, u32 set); +extern void bcma_chipco_chipctl_maskset(struct bcma_drv_cc *cc, + u32 offset, u32 mask, u32 set); +extern void bcma_chipco_regctl_maskset(struct bcma_drv_cc *cc, + u32 offset, u32 mask, u32 set); + #endif /* LINUX_BCMA_DRIVER_CC_H_ */ diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 72f3933..b5e0a5c 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -777,6 +777,13 @@ struct ieee80211_mmie { u8 mic[8]; } __attribute__ ((packed)); +struct ieee80211_vendor_ie { + u8 element_id; + u8 len; + u8 oui[3]; + u8 oui_type; +} __packed; + /* Control frames */ struct ieee80211_rts { __le16 frame_control; @@ -1470,6 +1477,9 @@ enum ieee80211_sa_query_action { #define WLAN_PMKID_LEN 16 +#define WLAN_OUI_WFA 0x506f9a +#define WLAN_OUI_TYPE_WFA_P2P 9 + /* * WMM/802.11e Tspec Element */ diff --git a/include/linux/nfc.h b/include/linux/nfc.h index c525e0b..36cb955 100644 --- a/include/linux/nfc.h +++ b/include/linux/nfc.h @@ -39,6 +39,10 @@ * * @NFC_CMD_GET_DEVICE: request information about a device (requires * %NFC_ATTR_DEVICE_INDEX) or dump request to get a list of all nfc devices + * @NFC_CMD_DEV_UP: turn on the nfc device + * (requires %NFC_ATTR_DEVICE_INDEX) + * @NFC_CMD_DEV_DOWN: turn off the nfc device + * (requires %NFC_ATTR_DEVICE_INDEX) * @NFC_CMD_START_POLL: start polling for targets using the given protocols * (requires %NFC_ATTR_DEVICE_INDEX and %NFC_ATTR_PROTOCOLS) * @NFC_CMD_STOP_POLL: stop polling for targets (requires @@ -56,6 +60,8 @@ enum nfc_commands { NFC_CMD_UNSPEC, NFC_CMD_GET_DEVICE, + NFC_CMD_DEV_UP, + NFC_CMD_DEV_DOWN, NFC_CMD_START_POLL, NFC_CMD_STOP_POLL, NFC_CMD_GET_TARGET, diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index f173075..c73582f 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -238,6 +238,8 @@ * * @NL80211_CMD_GET_SCAN: get scan results * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * probe requests at CCK rate or not. * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to * NL80211_CMD_GET_SCAN and on the "scan" multicast group) * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, @@ -432,6 +434,8 @@ * specified using %NL80211_ATTR_DURATION. When called, this operation * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the * TX status event pertaining to the TX request. + * %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the + * management frames at CCK rate or not in 2GHz band. * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this * command may be used with the corresponding cookie to cancel the wait * time if it is known that it is no longer necessary. @@ -499,6 +503,9 @@ * this command may also be sent by the driver as an MLME event to * inform userspace of the new replay counter. * + * @NL80211_CMD_PMKSA_CANDIDATE: This is used as an event to inform userspace + * of PMKSA caching dandidates. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -623,6 +630,8 @@ enum nl80211_commands { NL80211_CMD_SET_REKEY_OFFLOAD, + NL80211_CMD_PMKSA_CANDIDATE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1070,6 +1079,16 @@ enum nl80211_commands { * @NL80211_ATTR_ROAM_SUPPORT: Indicates whether the firmware is capable of * roaming to another AP in the same ESS if the signal lever is low. * + * @NL80211_ATTR_PMKSA_CANDIDATE: Nested attribute containing the PMKSA caching + * candidate information, see &enum nl80211_pmksa_candidate_attr. + * + * @NL80211_ATTR_TX_NO_CCK_RATE: Indicates whether to use CCK rate or not + * for management frames transmission. In order to avoid p2p probe/action + * frames are being transmitted at CCK rate in 2GHz band, the user space + * applications use this attribute. + * This attribute is used with %NL80211_CMD_TRIGGER_SCAN and + * %NL80211_CMD_FRAME commands. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1288,6 +1307,10 @@ enum nl80211_attrs { NL80211_ATTR_SCHED_SCAN_MATCH, NL80211_ATTR_MAX_MATCH_SETS, + NL80211_ATTR_PMKSA_CANDIDATE, + + NL80211_ATTR_TX_NO_CCK_RATE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -2558,4 +2581,27 @@ enum nl80211_sta_wme_attr { NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1 }; +/** + * enum nl80211_pmksa_candidate_attr - attributes for PMKSA caching candidates + * @__NL80211_PMKSA_CANDIDATE_INVALID: invalid number for nested attributes + * @NL80211_PMKSA_CANDIDATE_INDEX: candidate index (u32; the smaller, the higher + * priority) + * @NL80211_PMKSA_CANDIDATE_BSSID: candidate BSSID (6 octets) + * @NL80211_PMKSA_CANDIDATE_PREAUTH: RSN pre-authentication supported (flag) + * @NUM_NL80211_PMKSA_CANDIDATE: number of PMKSA caching candidate attributes + * (internal) + * @MAX_NL80211_PMKSA_CANDIDATE: highest PMKSA caching candidate attribute + * (internal) + */ +enum nl80211_pmksa_candidate_attr { + __NL80211_PMKSA_CANDIDATE_INVALID, + NL80211_PMKSA_CANDIDATE_INDEX, + NL80211_PMKSA_CANDIDATE_BSSID, + NL80211_PMKSA_CANDIDATE_PREAUTH, + + /* keep last */ + NUM_NL80211_PMKSA_CANDIDATE, + MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1 +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index be30aab..aaf79af 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -716,6 +716,16 @@ struct hci_rp_read_bd_addr { bdaddr_t bdaddr; } __packed; +#define HCI_OP_WRITE_PAGE_SCAN_ACTIVITY 0x0c1c +struct hci_cp_write_page_scan_activity { + __le16 interval; + __le16 window; +} __packed; + +#define HCI_OP_WRITE_PAGE_SCAN_TYPE 0x0c47 + #define PAGE_SCAN_TYPE_STANDARD 0x00 + #define PAGE_SCAN_TYPE_INTERLACED 0x01 + #define HCI_OP_LE_SET_EVENT_MASK 0x2001 struct hci_cp_le_set_event_mask { __u8 mask[8]; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8f441b8..5b92442 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -195,8 +195,6 @@ struct hci_dev { __u16 init_last_cmd; - struct crypto_blkcipher *tfm; - struct inquiry_cache inq_cache; struct hci_conn_hash conn_hash; struct list_head blacklist; @@ -348,6 +346,7 @@ enum { HCI_CONN_RSWITCH_PEND, HCI_CONN_MODE_CHANGE_PEND, HCI_CONN_SCO_SETUP_PEND, + HCI_CONN_LE_SMP_PEND, }; static inline void hci_conn_hash_init(struct hci_dev *hdev) @@ -395,6 +394,22 @@ static inline void hci_conn_hash_del(struct hci_dev *hdev, struct hci_conn *c) } } +static inline unsigned int hci_conn_num(struct hci_dev *hdev, __u8 type) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + switch (type) { + case ACL_LINK: + return h->acl_num; + case LE_LINK: + return h->le_num; + case SCO_LINK: + case ESCO_LINK: + return h->sco_num; + default: + return 0; + } +} + static inline struct hci_conn *hci_conn_hash_lookup_handle(struct hci_dev *hdev, __u16 handle) { @@ -475,7 +490,7 @@ static inline void hci_conn_put(struct hci_conn *conn) { if (atomic_dec_and_test(&conn->refcnt)) { unsigned long timeo; - if (conn->type == ACL_LINK) { + if (conn->type == ACL_LINK || conn->type == LE_LINK) { del_timer(&conn->idle_timer); if (conn->state == BT_CONNECTED) { timeo = msecs_to_jiffies(conn->disc_timeout); @@ -838,7 +853,7 @@ int mgmt_powered(u16 index, u8 powered); int mgmt_discoverable(u16 index, u8 discoverable); int mgmt_connectable(u16 index, u8 connectable); int mgmt_new_key(u16 index, struct link_key *key, u8 persistent); -int mgmt_connected(u16 index, bdaddr_t *bdaddr); +int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type); int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); int mgmt_disconnect_failed(u16 index); int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status); @@ -858,6 +873,8 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, u8 *eir); int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name); int mgmt_discovering(u16 index, u8 discovering); +int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr); +int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 4f34ad2..7f878b9 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -409,14 +409,8 @@ struct l2cap_conn { __u8 disc_reason; - __u8 preq[7]; /* SMP Pairing Request */ - __u8 prsp[7]; /* SMP Pairing Response */ - __u8 prnd[16]; /* SMP Pairing Random */ - __u8 pcnf[16]; /* SMP Pairing Confirm */ - __u8 tk[16]; /* SMP Temporary Key */ - __u8 smp_key_size; - struct timer_list security_timer; + struct smp_chan *smp_chan; struct list_head chan_l; rwlock_t chan_lock; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 5428fd3..d66da0f 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -211,6 +211,11 @@ struct mgmt_cp_unblock_device { bdaddr_t bdaddr; } __packed; +#define MGMT_OP_SET_FAST_CONNECTABLE 0x001F +struct mgmt_cp_set_fast_connectable { + __u8 enable; +} __packed; + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; @@ -249,6 +254,7 @@ struct mgmt_ev_new_key { #define MGMT_EV_CONNECTED 0x000B struct mgmt_ev_connected { bdaddr_t bdaddr; + __u8 link_type; } __packed; #define MGMT_EV_DISCONNECTED 0x000C @@ -301,3 +307,13 @@ struct mgmt_ev_remote_name { } __packed; #define MGMT_EV_DISCOVERING 0x0014 + +#define MGMT_EV_DEVICE_BLOCKED 0x0015 +struct mgmt_ev_device_blocked { + bdaddr_t bdaddr; +} __packed; + +#define MGMT_EV_DEVICE_UNBLOCKED 0x0016 +struct mgmt_ev_device_unblocked { + bdaddr_t bdaddr; +} __packed; diff --git a/include/net/bluetooth/smp.h b/include/net/bluetooth/smp.h index 46c4576..15b97d5 100644 --- a/include/net/bluetooth/smp.h +++ b/include/net/bluetooth/smp.h @@ -115,9 +115,26 @@ struct smp_cmd_security_req { #define SMP_MIN_ENC_KEY_SIZE 7 #define SMP_MAX_ENC_KEY_SIZE 16 +struct smp_chan { + struct l2cap_conn *conn; + u8 preq[7]; /* SMP Pairing Request */ + u8 prsp[7]; /* SMP Pairing Response */ + u8 prnd[16]; /* SMP Pairing Random (local) */ + u8 rrnd[16]; /* SMP Pairing Random (remote) */ + u8 pcnf[16]; /* SMP Pairing Confirm */ + u8 tk[16]; /* SMP Temporary Key */ + u8 smp_key_size; + struct crypto_blkcipher *tfm; + struct work_struct confirm; + struct work_struct random; + +}; + /* SMP Commands */ int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level); int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb); int smp_distribute_keys(struct l2cap_conn *conn, __u8 force); +void smp_chan_destroy(struct l2cap_conn *conn); + #endif /* __SMP_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index f4b19b2..31d823a 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -860,6 +860,7 @@ struct cfg80211_ssid { * @wiphy: the wiphy this was for * @dev: the interface * @aborted: (internal) scan request was notified as aborted + * @no_cck: used to send probe requests at non CCK rate in 2GHz band */ struct cfg80211_scan_request { struct cfg80211_ssid *ssids; @@ -874,6 +875,7 @@ struct cfg80211_scan_request { struct wiphy *wiphy; struct net_device *dev; bool aborted; + bool no_cck; /* keep last */ struct ieee80211_channel *channels[0]; @@ -1484,7 +1486,7 @@ struct cfg80211_ops { int (*change_bss)(struct wiphy *wiphy, struct net_device *dev, struct bss_parameters *params); - int (*set_txq_params)(struct wiphy *wiphy, + int (*set_txq_params)(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_txq_params *params); int (*set_channel)(struct wiphy *wiphy, struct net_device *dev, @@ -1560,7 +1562,8 @@ struct cfg80211_ops { struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, u64 *cookie); + const u8 *buf, size_t len, bool no_cck, + u64 *cookie); int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy, struct net_device *dev, u64 cookie); @@ -2462,6 +2465,24 @@ unsigned int cfg80211_classify8021d(struct sk_buff *skb); const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len); /** + * cfg80211_find_vendor_ie - find vendor specific information element in data + * + * @oui: vendor OUI + * @oui_type: vendor-specific OUI type + * @ies: data consisting of IEs + * @len: length of data + * + * This function will return %NULL if the vendor specific element ID + * could not be found or if the element is invalid (claims to be + * longer than the given data), or a pointer to the first byte + * of the requested element, that is the byte containing the + * element ID. There are no checks on the element length + * other than having to fit into the given data. + */ +const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, + const u8 *ies, int len); + +/** * DOC: Regulatory enforcement infrastructure * * TODO @@ -3121,6 +3142,17 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev, void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp); +/** + * cfg80211_pmksa_candidate_notify - notify about PMKSA caching candidate + * @dev: network device + * @index: candidate index (the smaller the index, the higher the priority) + * @bssid: BSSID of AP + * @preauth: Whether AP advertises support for RSN pre-authentication + * @gfp: allocation flags + */ +void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, + const u8 *bssid, bool preauth, gfp_t gfp); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9edba09..1e83afa 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -171,7 +171,7 @@ enum ieee80211_bss_change { BSS_CHANGED_ERP_CTS_PROT = 1<<1, BSS_CHANGED_ERP_PREAMBLE = 1<<2, BSS_CHANGED_ERP_SLOT = 1<<3, - BSS_CHANGED_HT = 1<<4, + BSS_CHANGED_HT = 1<<4, BSS_CHANGED_BASIC_RATES = 1<<5, BSS_CHANGED_BEACON_INT = 1<<6, BSS_CHANGED_BSSID = 1<<7, @@ -363,6 +363,9 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_INTFL_TKIP_MIC_FAILURE: Marks this packet to be used for TKIP * testing. It will be sent out with incorrect Michael MIC key to allow * TKIP countermeasures to be tested. + * @IEEE80211_TX_CTL_NO_CCK_RATE: This frame will be sent at non CCK rate. + * This flag is actually used for management frame especially for P2P + * frames not being sent at CCK rate in 2GHz band. * * Note: If you have to add new flags to the enumeration, then don't * forget to update %IEEE80211_TX_TEMPORARY_FLAGS when necessary. @@ -393,6 +396,7 @@ enum mac80211_tx_control_flags { IEEE80211_TX_CTL_STBC = BIT(23) | BIT(24), IEEE80211_TX_CTL_TX_OFFCHAN = BIT(25), IEEE80211_TX_INTFL_TKIP_MIC_FAILURE = BIT(26), + IEEE80211_TX_CTL_NO_CCK_RATE = BIT(27), }; #define IEEE80211_TX_CTL_STBC_SHIFT 23 @@ -2000,9 +2004,10 @@ struct ieee80211_ops { enum sta_notify_cmd, struct ieee80211_sta *sta); int (*conf_tx)(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params); - u64 (*get_tsf)(struct ieee80211_hw *hw); - void (*set_tsf)(struct ieee80211_hw *hw, u64 tsf); - void (*reset_tsf)(struct ieee80211_hw *hw); + u64 (*get_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + void (*set_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 tsf); + void (*reset_tsf)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); int (*tx_last_beacon)(struct ieee80211_hw *hw); int (*ampdu_action)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/include/net/nfc/nci.h b/include/net/nfc/nci.h new file mode 100644 index 0000000..39b85bc --- /dev/null +++ b/include/net/nfc/nci.h @@ -0,0 +1,313 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias <ilane@ti.com> + * + * Acknowledgements: + * This file is based on hci.h, which was written + * by Maxim Krasnyansky. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __NCI_H +#define __NCI_H + +/* NCI constants */ +#define NCI_MAX_NUM_MAPPING_CONFIGS 10 +#define NCI_MAX_NUM_RF_CONFIGS 10 +#define NCI_MAX_NUM_CONN 10 + +/* NCI Status Codes */ +#define NCI_STATUS_OK 0x00 +#define NCI_STATUS_REJECTED 0x01 +#define NCI_STATUS_MESSAGE_CORRUPTED 0x02 +#define NCI_STATUS_BUFFER_FULL 0x03 +#define NCI_STATUS_FAILED 0x04 +#define NCI_STATUS_NOT_INITIALIZED 0x05 +#define NCI_STATUS_SYNTAX_ERROR 0x06 +#define NCI_STATUS_SEMANTIC_ERROR 0x07 +#define NCI_STATUS_UNKNOWN_GID 0x08 +#define NCI_STATUS_UNKNOWN_OID 0x09 +#define NCI_STATUS_INVALID_PARAM 0x0a +#define NCI_STATUS_MESSAGE_SIZE_EXCEEDED 0x0b +/* Discovery Specific Status Codes */ +#define NCI_STATUS_DISCOVERY_ALREADY_STARTED 0xa0 +#define NCI_STATUS_DISCOVERY_TARGET_ACTIVATION_FAILED 0xa1 +/* RF Interface Specific Status Codes */ +#define NCI_STATUS_RF_TRANSMISSION_ERROR 0xb0 +#define NCI_STATUS_RF_PROTOCOL_ERROR 0xb1 +#define NCI_STATUS_RF_TIMEOUT_ERROR 0xb2 +#define NCI_STATUS_RF_LINK_LOSS_ERROR 0xb3 +/* NFCEE Interface Specific Status Codes */ +#define NCI_STATUS_MAX_ACTIVE_NFCEE_INTERFACES_REACHED 0xc0 +#define NCI_STATUS_NFCEE_INTERFACE_ACTIVATION_FAILED 0xc1 +#define NCI_STATUS_NFCEE_TRANSMISSION_ERROR 0xc2 +#define NCI_STATUS_NFCEE_PROTOCOL_ERROR 0xc3 +#define NCI_STATUS_NFCEE_TIMEOUT_ERROR 0xc4 + +/* NCI RF Technology and Mode */ +#define NCI_NFC_A_PASSIVE_POLL_MODE 0x00 +#define NCI_NFC_B_PASSIVE_POLL_MODE 0x01 +#define NCI_NFC_F_PASSIVE_POLL_MODE 0x02 +#define NCI_NFC_A_ACTIVE_POLL_MODE 0x03 +#define NCI_NFC_F_ACTIVE_POLL_MODE 0x05 +#define NCI_NFC_A_PASSIVE_LISTEN_MODE 0x80 +#define NCI_NFC_B_PASSIVE_LISTEN_MODE 0x81 +#define NCI_NFC_F_PASSIVE_LISTEN_MODE 0x82 +#define NCI_NFC_A_ACTIVE_LISTEN_MODE 0x83 +#define NCI_NFC_F_ACTIVE_LISTEN_MODE 0x85 + +/* NCI RF Protocols */ +#define NCI_RF_PROTOCOL_UNKNOWN 0x00 +#define NCI_RF_PROTOCOL_T1T 0x01 +#define NCI_RF_PROTOCOL_T2T 0x02 +#define NCI_RF_PROTOCOL_T3T 0x03 +#define NCI_RF_PROTOCOL_ISO_DEP 0x04 +#define NCI_RF_PROTOCOL_NFC_DEP 0x05 + +/* NCI RF Interfaces */ +#define NCI_RF_INTERFACE_RFU 0x00 +#define NCI_RF_INTERFACE_FRAME 0x01 +#define NCI_RF_INTERFACE_ISO_DEP 0x02 +#define NCI_RF_INTERFACE_NFC_DEP 0x03 + +/* NCI RF_DISCOVER_MAP_CMD modes */ +#define NCI_DISC_MAP_MODE_POLL 0x01 +#define NCI_DISC_MAP_MODE_LISTEN 0x02 +#define NCI_DISC_MAP_MODE_BOTH 0x03 + +/* NCI Discovery Types */ +#define NCI_DISCOVERY_TYPE_POLL_A_PASSIVE 0x00 +#define NCI_DISCOVERY_TYPE_POLL_B_PASSIVE 0x01 +#define NCI_DISCOVERY_TYPE_POLL_F_PASSIVE 0x02 +#define NCI_DISCOVERY_TYPE_POLL_A_ACTIVE 0x03 +#define NCI_DISCOVERY_TYPE_POLL_F_ACTIVE 0x05 +#define NCI_DISCOVERY_TYPE_WAKEUP_A_PASSIVE 0x06 +#define NCI_DISCOVERY_TYPE_WAKEUP_B_PASSIVE 0x07 +#define NCI_DISCOVERY_TYPE_WAKEUP_A_ACTIVE 0x09 +#define NCI_DISCOVERY_TYPE_LISTEN_A_PASSIVE 0x80 +#define NCI_DISCOVERY_TYPE_LISTEN_B_PASSIVE 0x81 +#define NCI_DISCOVERY_TYPE_LISTEN_F_PASSIVE 0x82 +#define NCI_DISCOVERY_TYPE_LISTEN_A_ACTIVE 0x83 +#define NCI_DISCOVERY_TYPE_LISTEN_F_ACTIVE 0x85 + +/* NCI Deactivation Type */ +#define NCI_DEACTIVATE_TYPE_IDLE_MODE 0x00 +#define NCI_DEACTIVATE_TYPE_SLEEP_MODE 0x01 +#define NCI_DEACTIVATE_TYPE_SLEEP_AF_MODE 0x02 +#define NCI_DEACTIVATE_TYPE_RF_LINK_LOSS 0x03 +#define NCI_DEACTIVATE_TYPE_DISCOVERY_ERROR 0x04 + +/* Message Type (MT) */ +#define NCI_MT_DATA_PKT 0x00 +#define NCI_MT_CMD_PKT 0x01 +#define NCI_MT_RSP_PKT 0x02 +#define NCI_MT_NTF_PKT 0x03 + +#define nci_mt(hdr) (((hdr)[0]>>5)&0x07) +#define nci_mt_set(hdr, mt) ((hdr)[0] |= (__u8)(((mt)&0x07)<<5)) + +/* Packet Boundary Flag (PBF) */ +#define NCI_PBF_LAST 0x00 +#define NCI_PBF_CONT 0x01 + +#define nci_pbf(hdr) (__u8)(((hdr)[0]>>4)&0x01) +#define nci_pbf_set(hdr, pbf) ((hdr)[0] |= (__u8)(((pbf)&0x01)<<4)) + +/* Control Opcode manipulation */ +#define nci_opcode_pack(gid, oid) (__u16)((((__u16)((gid)&0x0f))<<8)|\ + ((__u16)((oid)&0x3f))) +#define nci_opcode(hdr) nci_opcode_pack(hdr[0], hdr[1]) +#define nci_opcode_gid(op) (__u8)(((op)&0x0f00)>>8) +#define nci_opcode_oid(op) (__u8)((op)&0x003f) + +/* Payload Length */ +#define nci_plen(hdr) (__u8)((hdr)[2]) + +/* Connection ID */ +#define nci_conn_id(hdr) (__u8)(((hdr)[0])&0x0f) + +/* GID values */ +#define NCI_GID_CORE 0x0 +#define NCI_GID_RF_MGMT 0x1 +#define NCI_GID_NFCEE_MGMT 0x2 +#define NCI_GID_PROPRIETARY 0xf + +/* ---- NCI Packet structures ---- */ +#define NCI_CTRL_HDR_SIZE 3 +#define NCI_DATA_HDR_SIZE 3 + +struct nci_ctrl_hdr { + __u8 gid; /* MT & PBF & GID */ + __u8 oid; + __u8 plen; +} __packed; + +struct nci_data_hdr { + __u8 conn_id; /* MT & PBF & ConnID */ + __u8 rfu; + __u8 plen; +} __packed; + +/* ------------------------ */ +/* ----- NCI Commands ---- */ +/* ------------------------ */ +#define NCI_OP_CORE_RESET_CMD nci_opcode_pack(NCI_GID_CORE, 0x00) + +#define NCI_OP_CORE_INIT_CMD nci_opcode_pack(NCI_GID_CORE, 0x01) + +#define NCI_OP_CORE_SET_CONFIG_CMD nci_opcode_pack(NCI_GID_CORE, 0x02) + +#define NCI_OP_CORE_CONN_CREATE_CMD nci_opcode_pack(NCI_GID_CORE, 0x04) +struct nci_core_conn_create_cmd { + __u8 target_handle; + __u8 num_target_specific_params; +} __packed; + +#define NCI_OP_CORE_CONN_CLOSE_CMD nci_opcode_pack(NCI_GID_CORE, 0x06) + +#define NCI_OP_RF_DISCOVER_MAP_CMD nci_opcode_pack(NCI_GID_RF_MGMT, 0x00) +struct disc_map_config { + __u8 rf_protocol; + __u8 mode; + __u8 rf_interface_type; +} __packed; + +struct nci_rf_disc_map_cmd { + __u8 num_mapping_configs; + struct disc_map_config mapping_configs + [NCI_MAX_NUM_MAPPING_CONFIGS]; +} __packed; + +#define NCI_OP_RF_DISCOVER_CMD nci_opcode_pack(NCI_GID_RF_MGMT, 0x03) +struct disc_config { + __u8 type; + __u8 frequency; +} __packed; + +struct nci_rf_disc_cmd { + __u8 num_disc_configs; + struct disc_config disc_configs[NCI_MAX_NUM_RF_CONFIGS]; +} __packed; + +#define NCI_OP_RF_DEACTIVATE_CMD nci_opcode_pack(NCI_GID_RF_MGMT, 0x06) +struct nci_rf_deactivate_cmd { + __u8 type; +} __packed; + +/* ----------------------- */ +/* ---- NCI Responses ---- */ +/* ----------------------- */ +#define NCI_OP_CORE_RESET_RSP nci_opcode_pack(NCI_GID_CORE, 0x00) +struct nci_core_reset_rsp { + __u8 status; + __u8 nci_ver; +} __packed; + +#define NCI_OP_CORE_INIT_RSP nci_opcode_pack(NCI_GID_CORE, 0x01) +struct nci_core_init_rsp_1 { + __u8 status; + __le32 nfcc_features; + __u8 num_supported_rf_interfaces; + __u8 supported_rf_interfaces[0]; /* variable size array */ + /* continuted in nci_core_init_rsp_2 */ +} __packed; + +struct nci_core_init_rsp_2 { + __u8 max_logical_connections; + __le16 max_routing_table_size; + __u8 max_control_packet_payload_length; + __le16 rf_sending_buffer_size; + __le16 rf_receiving_buffer_size; + __le16 manufacturer_id; +} __packed; + +#define NCI_OP_CORE_SET_CONFIG_RSP nci_opcode_pack(NCI_GID_CORE, 0x02) + +#define NCI_OP_CORE_CONN_CREATE_RSP nci_opcode_pack(NCI_GID_CORE, 0x04) +struct nci_core_conn_create_rsp { + __u8 status; + __u8 max_pkt_payload_size; + __u8 initial_num_credits; + __u8 conn_id; +} __packed; + +#define NCI_OP_CORE_CONN_CLOSE_RSP nci_opcode_pack(NCI_GID_CORE, 0x06) + +#define NCI_OP_RF_DISCOVER_MAP_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x00) + +#define NCI_OP_RF_DISCOVER_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x03) + +#define NCI_OP_RF_DEACTIVATE_RSP nci_opcode_pack(NCI_GID_RF_MGMT, 0x06) + +/* --------------------------- */ +/* ---- NCI Notifications ---- */ +/* --------------------------- */ +#define NCI_OP_CORE_CONN_CREDITS_NTF nci_opcode_pack(NCI_GID_CORE, 0x07) +struct conn_credit_entry { + __u8 conn_id; + __u8 credits; +} __packed; + +struct nci_core_conn_credit_ntf { + __u8 num_entries; + struct conn_credit_entry conn_entries[NCI_MAX_NUM_CONN]; +} __packed; + +#define NCI_OP_RF_FIELD_INFO_NTF nci_opcode_pack(NCI_GID_CORE, 0x08) +struct nci_rf_field_info_ntf { + __u8 rf_field_status; +} __packed; + +#define NCI_OP_RF_ACTIVATE_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x05) +struct rf_tech_specific_params_nfca_poll { + __u16 sens_res; + __u8 nfcid1_len; /* 0, 4, 7, or 10 Bytes */ + __u8 nfcid1[10]; + __u8 sel_res_len; /* 0 or 1 Bytes */ + __u8 sel_res; +} __packed; + +struct activation_params_nfca_poll_iso_dep { + __u8 rats_res_len; + __u8 rats_res[20]; +}; + +struct nci_rf_activate_ntf { + __u8 target_handle; + __u8 rf_protocol; + __u8 rf_tech_and_mode; + __u8 rf_tech_specific_params_len; + + union { + struct rf_tech_specific_params_nfca_poll nfca_poll; + } rf_tech_specific_params; + + __u8 rf_interface_type; + __u8 activation_params_len; + + union { + struct activation_params_nfca_poll_iso_dep nfca_poll_iso_dep; + } activation_params; + +} __packed; + +#define NCI_OP_RF_DEACTIVATE_NTF nci_opcode_pack(NCI_GID_RF_MGMT, 0x06) + +#endif /* __NCI_H */ diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h new file mode 100644 index 0000000..b8b4bbd --- /dev/null +++ b/include/net/nfc/nci_core.h @@ -0,0 +1,184 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias <ilane@ti.com> + * + * Acknowledgements: + * This file is based on hci_core.h, which was written + * by Maxim Krasnyansky. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __NCI_CORE_H +#define __NCI_CORE_H + +#include <linux/interrupt.h> +#include <linux/skbuff.h> + +#include <net/nfc/nfc.h> +#include <net/nfc/nci.h> + +/* NCI device state */ +enum { + NCI_INIT, + NCI_UP, + NCI_DISCOVERY, + NCI_POLL_ACTIVE, + NCI_DATA_EXCHANGE, +}; + +/* NCI timeouts */ +#define NCI_RESET_TIMEOUT 5000 +#define NCI_INIT_TIMEOUT 5000 +#define NCI_RF_DISC_TIMEOUT 5000 +#define NCI_RF_DEACTIVATE_TIMEOUT 5000 +#define NCI_CMD_TIMEOUT 5000 + +struct nci_dev; + +struct nci_ops { + int (*open)(struct nci_dev *ndev); + int (*close)(struct nci_dev *ndev); + int (*send)(struct sk_buff *skb); +}; + +#define NCI_MAX_SUPPORTED_RF_INTERFACES 4 + +/* NCI Core structures */ +struct nci_dev { + struct nfc_dev *nfc_dev; + struct nci_ops *ops; + + int tx_headroom; + int tx_tailroom; + + unsigned long flags; + + atomic_t cmd_cnt; + atomic_t credits_cnt; + + struct timer_list cmd_timer; + + struct workqueue_struct *cmd_wq; + struct work_struct cmd_work; + + struct workqueue_struct *rx_wq; + struct work_struct rx_work; + + struct workqueue_struct *tx_wq; + struct work_struct tx_work; + + struct sk_buff_head cmd_q; + struct sk_buff_head rx_q; + struct sk_buff_head tx_q; + + struct mutex req_lock; + struct completion req_completion; + __u32 req_status; + __u32 req_result; + + void *driver_data; + + __u32 poll_prots; + __u32 target_available_prots; + __u32 target_active_prot; + + /* received during NCI_OP_CORE_RESET_RSP */ + __u8 nci_ver; + + /* received during NCI_OP_CORE_INIT_RSP */ + __u32 nfcc_features; + __u8 num_supported_rf_interfaces; + __u8 supported_rf_interfaces + [NCI_MAX_SUPPORTED_RF_INTERFACES]; + __u8 max_logical_connections; + __u16 max_routing_table_size; + __u8 max_control_packet_payload_length; + __u16 rf_sending_buffer_size; + __u16 rf_receiving_buffer_size; + __u16 manufacturer_id; + + /* received during NCI_OP_CORE_CONN_CREATE_RSP for static conn 0 */ + __u8 max_pkt_payload_size; + __u8 initial_num_credits; + __u8 conn_id; + + /* stored during nci_data_exchange */ + data_exchange_cb_t data_exchange_cb; + void *data_exchange_cb_context; + struct sk_buff *rx_data_reassembly; +}; + +/* ----- NCI Devices ----- */ +struct nci_dev *nci_allocate_device(struct nci_ops *ops, + __u32 supported_protocols, + int tx_headroom, + int tx_tailroom); +void nci_free_device(struct nci_dev *ndev); +int nci_register_device(struct nci_dev *ndev); +void nci_unregister_device(struct nci_dev *ndev); +int nci_recv_frame(struct sk_buff *skb); + +static inline struct sk_buff *nci_skb_alloc(struct nci_dev *ndev, + unsigned int len, + gfp_t how) +{ + struct sk_buff *skb; + + skb = alloc_skb(len + ndev->tx_headroom + ndev->tx_tailroom, how); + if (skb) + skb_reserve(skb, ndev->tx_headroom); + + return skb; +} + +static inline void nci_set_parent_dev(struct nci_dev *ndev, struct device *dev) +{ + nfc_set_parent_dev(ndev->nfc_dev, dev); +} + +static inline void nci_set_drvdata(struct nci_dev *ndev, void *data) +{ + ndev->driver_data = data; +} + +static inline void *nci_get_drvdata(struct nci_dev *ndev) +{ + return ndev->driver_data; +} + +void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb); +void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb); +void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb); +int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload); +int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb); +void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb, + int err); + +/* ----- NCI requests ----- */ +#define NCI_REQ_DONE 0 +#define NCI_REQ_PEND 1 +#define NCI_REQ_CANCELED 2 + +void nci_req_complete(struct nci_dev *ndev, int result); + +/* ----- NCI status code ----- */ +int nci_to_errno(__u8 code); + +#endif /* __NCI_CORE_H */ diff --git a/include/net/nfc.h b/include/net/nfc/nfc.h index 87b51fe..6a7f602 100644 --- a/include/net/nfc.h +++ b/include/net/nfc/nfc.h @@ -48,6 +48,8 @@ typedef void (*data_exchange_cb_t)(void *context, struct sk_buff *skb, int err); struct nfc_ops { + int (*dev_up)(struct nfc_dev *dev); + int (*dev_down)(struct nfc_dev *dev); int (*start_poll)(struct nfc_dev *dev, u32 protocols); void (*stop_poll)(struct nfc_dev *dev); int (*activate_target)(struct nfc_dev *dev, u32 target_idx, @@ -78,7 +80,9 @@ struct nfc_dev { int targets_generation; spinlock_t targets_lock; struct device dev; + bool dev_up; bool polling; + bool remote_activated; struct nfc_genl_data genl_data; u32 supported_protocols; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ea7f031..c2df7bf 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -218,7 +218,7 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], cp.handle = cpu_to_le16(conn->handle); memcpy(cp.ltk, ltk, sizeof(cp.ltk)); cp.ediv = ediv; - memcpy(cp.rand, rand, sizeof(rand)); + memcpy(cp.rand, rand, sizeof(cp.rand)); hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp); } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 56943ad..b84458d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1312,59 +1312,41 @@ int hci_blacklist_clear(struct hci_dev *hdev) int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *entry; - int err; if (bacmp(bdaddr, BDADDR_ANY) == 0) return -EBADF; - hci_dev_lock_bh(hdev); - - if (hci_blacklist_lookup(hdev, bdaddr)) { - err = -EEXIST; - goto err; - } + if (hci_blacklist_lookup(hdev, bdaddr)) + return -EEXIST; entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); - if (!entry) { - err = -ENOMEM; - goto err; - } + if (!entry) + return -ENOMEM; bacpy(&entry->bdaddr, bdaddr); list_add(&entry->list, &hdev->blacklist); - err = 0; - -err: - hci_dev_unlock_bh(hdev); - return err; + return mgmt_device_blocked(hdev->id, bdaddr); } int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct bdaddr_list *entry; - int err = 0; - - hci_dev_lock_bh(hdev); if (bacmp(bdaddr, BDADDR_ANY) == 0) { - hci_blacklist_clear(hdev); - goto done; + return hci_blacklist_clear(hdev); } entry = hci_blacklist_lookup(hdev, bdaddr); if (!entry) { - err = -ENOENT; - goto done; + return -ENOENT; } list_del(&entry->list); kfree(entry); -done: - hci_dev_unlock_bh(hdev); - return err; + return mgmt_device_unblocked(hdev->id, bdaddr); } static void hci_clear_adv_cache(unsigned long arg) @@ -1523,11 +1505,6 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->workqueue) goto nomem; - hdev->tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(hdev->tfm)) - BT_INFO("Failed to load transform for ecb(aes): %ld", - PTR_ERR(hdev->tfm)); - hci_register_sysfs(hdev); hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, @@ -1576,9 +1553,6 @@ int hci_unregister_dev(struct hci_dev *hdev) !test_bit(HCI_SETUP, &hdev->flags)) mgmt_index_removed(hdev->id); - if (!IS_ERR(hdev->tfm)) - crypto_free_blkcipher(hdev->tfm); - hci_notify(hdev, HCI_DEV_UNREG); if (hdev->rfkill) { @@ -2074,6 +2048,9 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int min = c->sent; conn = c; } + + if (hci_conn_num(hdev, type) == num) + break; } if (conn) { @@ -2131,6 +2108,9 @@ static inline void hci_sched_acl(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, ACL_LINK)) + return; + if (!test_bit(HCI_RAW, &hdev->flags)) { /* ACL tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ @@ -2162,6 +2142,9 @@ static inline void hci_sched_sco(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, SCO_LINK)) + return; + while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); @@ -2182,6 +2165,9 @@ static inline void hci_sched_esco(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, ESCO_LINK)) + return; + while (hdev->sco_cnt && (conn = hci_low_sent(hdev, ESCO_LINK, "e))) { while (quote-- && (skb = skb_dequeue(&conn->data_q))) { BT_DBG("skb %p len %d", skb, skb->len); @@ -2202,6 +2188,9 @@ static inline void hci_sched_le(struct hci_dev *hdev) BT_DBG("%s", hdev->name); + if (!hci_conn_num(hdev, LE_LINK)) + return; + if (!test_bit(HCI_RAW, &hdev->flags)) { /* LE tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7ef4eb4..35083f2 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -898,16 +898,15 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, if (!cp) return; - hci_dev_lock(hdev); - if (cp->enable == 0x01) { del_timer(&hdev->adv_timer); + + hci_dev_lock(hdev); hci_adv_entries_clear(hdev); + hci_dev_unlock(hdev); } else if (cp->enable == 0x00) { mod_timer(&hdev->adv_timer, jiffies + ADV_CLEAR_TIMEOUT); } - - hci_dev_unlock(hdev); } static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -1103,9 +1102,10 @@ static int hci_outgoing_auth_needed(struct hci_dev *hdev, return 0; /* Only request authentication for SSP connections or non-SSP - * devices with sec_level HIGH */ + * devices with sec_level HIGH or if MITM protection is requested */ if (!(hdev->ssp_mode > 0 && conn->ssp_mode > 0) && - conn->pending_sec_level != BT_SECURITY_HIGH) + conn->pending_sec_level != BT_SECURITY_HIGH && + !(conn->auth_type & 0x01)) return 0; return 1; @@ -1412,7 +1412,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->state = BT_CONFIG; hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; - mgmt_connected(hdev->id, &ev->bdaddr); + mgmt_connected(hdev->id, &ev->bdaddr, conn->type); } else conn->state = BT_CONNECTED; @@ -2816,7 +2816,7 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff goto unlock; } - mgmt_connected(hdev->id, &ev->bdaddr); + mgmt_connected(hdev->id, &ev->bdaddr, conn->type); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index ff02cf5..f6afe3d 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -183,21 +183,35 @@ static int hci_sock_release(struct socket *sock) static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; + int err; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; - return hci_blacklist_add(hdev, &bdaddr); + hci_dev_lock_bh(hdev); + + err = hci_blacklist_add(hdev, &bdaddr); + + hci_dev_unlock_bh(hdev); + + return err; } static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; + int err; if (copy_from_user(&bdaddr, arg, sizeof(bdaddr))) return -EFAULT; - return hci_blacklist_del(hdev, &bdaddr); + hci_dev_lock_bh(hdev); + + err = hci_blacklist_del(hdev, &bdaddr); + + hci_dev_unlock_bh(hdev); + + return err; } /* Ioctls that require bound socket */ diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index a6c3aa8..22f1a6c 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -23,6 +23,8 @@ static inline char *link_typetostr(int type) return "SCO"; case ESCO_LINK: return "eSCO"; + case LE_LINK: + return "LE"; default: return "UNKNOWN"; } diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index fb68f34..b83979c 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -872,6 +872,9 @@ static int hidp_start(struct hid_device *hid) struct hidp_session *session = hid->driver_data; struct hid_report *report; + if (hid->quirks & HID_QUIRK_NO_INIT_REPORTS) + return 0; + list_for_each_entry(report, &hid->report_enum[HID_INPUT_REPORT]. report_list, list) hidp_send_report(session, report); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b3bdb48..1611b35 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -907,6 +907,9 @@ static void l2cap_conn_ready(struct l2cap_conn *conn) if (!conn->hcon->out && conn->hcon->type == LE_LINK) l2cap_le_conn_ready(conn); + if (conn->hcon->out && conn->hcon->type == LE_LINK) + smp_conn_security(conn, conn->hcon->pending_sec_level); + read_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { @@ -986,8 +989,10 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) del_timer_sync(&conn->info_timer); - if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + if (test_and_clear_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) { del_timer(&conn->security_timer); + smp_chan_destroy(conn); + } hcon->l2cap_data = NULL; kfree(conn); @@ -1519,7 +1524,9 @@ struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *m return skb; } -struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, u16 control, u16 sdulen) +static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, + struct msghdr *msg, size_t len, + u16 control, u16 sdulen) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -4093,6 +4100,11 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) BT_DBG("conn %p", conn); + if (hcon->type == LE_LINK) { + smp_distribute_keys(conn, 0); + del_timer(&conn->security_timer); + } + read_lock(&conn->chan_lock); list_for_each_entry(chan, &conn->chan_l, list) { @@ -4105,9 +4117,7 @@ static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) if (chan->scid == L2CAP_CID_LE_DATA) { if (!status && encrypt) { chan->sec_level = hcon->sec_level; - del_timer(&conn->security_timer); l2cap_chan_ready(sk); - smp_distribute_keys(conn, 0); } bh_unlock_sock(sk); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 53e109e..5a94eec 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -908,7 +908,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) struct hci_dev *hdev; struct mgmt_cp_load_keys *cp; u16 key_count, expected_len; - int i, err; + int i; cp = (void *) data; @@ -918,9 +918,9 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) key_count = get_unaligned_le16(&cp->key_count); expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); - if (expected_len > len) { - BT_ERR("load_keys: expected at least %u bytes, got %u bytes", - expected_len, len); + if (expected_len != len) { + BT_ERR("load_keys: expected %u bytes, got %u bytes", + len, expected_len); return -EINVAL; } @@ -942,36 +942,17 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) else clear_bit(HCI_DEBUG_KEYS, &hdev->flags); - len -= sizeof(*cp); - i = 0; - - while (i < len) { - struct mgmt_key_info *key = (void *) cp->keys + i; - - i += sizeof(*key) + key->dlen; - - if (key->type == HCI_LK_SMP_LTK) { - struct key_master_id *id = (void *) key->data; - - if (key->dlen != sizeof(struct key_master_id)) - continue; - - hci_add_ltk(hdev, 0, &key->bdaddr, key->pin_len, - id->ediv, id->rand, key->val); - - continue; - } + for (i = 0; i < key_count; i++) { + struct mgmt_key_info *key = &cp->keys[i]; hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type, key->pin_len); } - err = cmd_complete(sk, index, MGMT_OP_LOAD_KEYS, NULL, 0); - hci_dev_unlock_bh(hdev); hci_dev_put(hdev); - return err; + return 0; } static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) @@ -1347,6 +1328,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) struct hci_dev *hdev; struct mgmt_cp_pair_device *cp; struct pending_cmd *cmd; + struct adv_entry *entry; u8 sec_level, auth_type; struct hci_conn *conn; int err; @@ -1364,15 +1346,20 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) hci_dev_lock_bh(hdev); - if (cp->io_cap == 0x03) { - sec_level = BT_SECURITY_MEDIUM; + sec_level = BT_SECURITY_MEDIUM; + if (cp->io_cap == 0x03) auth_type = HCI_AT_DEDICATED_BONDING; - } else { - sec_level = BT_SECURITY_HIGH; + else auth_type = HCI_AT_DEDICATED_BONDING_MITM; - } - conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, auth_type); + entry = hci_find_adv_entry(hdev, &cp->bdaddr); + if (entry) + conn = hci_connect(hdev, LE_LINK, &cp->bdaddr, sec_level, + auth_type); + else + conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, + auth_type); + if (IS_ERR(conn)) { err = PTR_ERR(conn); goto unlock; @@ -1391,7 +1378,10 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) goto unlock; } - conn->connect_cfm_cb = pairing_complete_cb; + /* For LE, just connecting isn't a proof that the pairing finished */ + if (!entry) + conn->connect_cfm_cb = pairing_complete_cb; + conn->security_cfm_cb = pairing_complete_cb; conn->disconn_cfm_cb = pairing_complete_cb; conn->io_capability = cp->io_cap; @@ -1689,13 +1679,12 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; - struct mgmt_cp_block_device *cp; + struct pending_cmd *cmd; + struct mgmt_cp_block_device *cp = (void *) data; int err; BT_DBG("hci%u", index); - cp = (void *) data; - if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, EINVAL); @@ -1705,6 +1694,14 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, ENODEV); + hci_dev_lock_bh(hdev); + + cmd = mgmt_pending_add(sk, MGMT_OP_BLOCK_DEVICE, index, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + err = hci_blacklist_add(hdev, &cp->bdaddr); if (err < 0) @@ -1712,6 +1709,11 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, else err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, NULL, 0); + + mgmt_pending_remove(cmd); + +failed: + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); return err; @@ -1721,13 +1723,12 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; - struct mgmt_cp_unblock_device *cp; + struct pending_cmd *cmd; + struct mgmt_cp_unblock_device *cp = (void *) data; int err; BT_DBG("hci%u", index); - cp = (void *) data; - if (len != sizeof(*cp)) return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, EINVAL); @@ -1737,6 +1738,14 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, ENODEV); + hci_dev_lock_bh(hdev); + + cmd = mgmt_pending_add(sk, MGMT_OP_UNBLOCK_DEVICE, index, NULL, 0); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + err = hci_blacklist_del(hdev, &cp->bdaddr); if (err < 0) @@ -1744,6 +1753,67 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, else err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, NULL, 0); + + mgmt_pending_remove(cmd); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + +static int set_fast_connectable(struct sock *sk, u16 index, + unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_set_fast_connectable *cp = (void *) data; + struct hci_cp_write_page_scan_activity acp; + u8 type; + int err; + + BT_DBG("hci%u", index); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + ENODEV); + + hci_dev_lock(hdev); + + if (cp->enable) { + type = PAGE_SCAN_TYPE_INTERLACED; + acp.interval = 0x0024; /* 22.5 msec page scan interval */ + } else { + type = PAGE_SCAN_TYPE_STANDARD; /* default */ + acp.interval = 0x0800; /* default 1.28 sec page scan */ + } + + acp.window = 0x0012; /* default 11.25 msec page scan window */ + + err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, + sizeof(acp), &acp); + if (err < 0) { + err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + -err); + goto done; + } + + err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); + if (err < 0) { + err = cmd_status(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + -err); + goto done; + } + + err = cmd_complete(sk, index, MGMT_OP_SET_FAST_CONNECTABLE, + NULL, 0); +done: + hci_dev_unlock(hdev); hci_dev_put(hdev); return err; @@ -1869,6 +1939,10 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_UNBLOCK_DEVICE: err = unblock_device(sk, index, buf + sizeof(*hdr), len); break; + case MGMT_OP_SET_FAST_CONNECTABLE: + err = set_fast_connectable(sk, index, buf + sizeof(*hdr), + len); + break; default: BT_DBG("Unknown op %u", opcode); err = cmd_status(sk, index, opcode, 0x01); @@ -1977,35 +2051,25 @@ int mgmt_connectable(u16 index, u8 connectable) int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) { - struct mgmt_ev_new_key *ev; - int err, total; - - total = sizeof(struct mgmt_ev_new_key) + key->dlen; - ev = kzalloc(total, GFP_ATOMIC); - if (!ev) - return -ENOMEM; - - bacpy(&ev->key.bdaddr, &key->bdaddr); - ev->key.type = key->type; - memcpy(ev->key.val, key->val, 16); - ev->key.pin_len = key->pin_len; - ev->key.dlen = key->dlen; - ev->store_hint = persistent; + struct mgmt_ev_new_key ev; - memcpy(ev->key.data, key->data, key->dlen); - - err = mgmt_event(MGMT_EV_NEW_KEY, index, ev, total, NULL); + memset(&ev, 0, sizeof(ev)); - kfree(ev); + ev.store_hint = persistent; + bacpy(&ev.key.bdaddr, &key->bdaddr); + ev.key.type = key->type; + memcpy(ev.key.val, key->val, 16); + ev.key.pin_len = key->pin_len; - return err; + return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL); } -int mgmt_connected(u16 index, bdaddr_t *bdaddr) +int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type) { struct mgmt_ev_connected ev; bacpy(&ev.bdaddr, bdaddr); + ev.link_type = link_type; return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL); } @@ -2260,12 +2324,14 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, memset(&ev, 0, sizeof(ev)); bacpy(&ev.bdaddr, bdaddr); - memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class)); ev.rssi = rssi; if (eir) memcpy(ev.eir, eir, sizeof(ev.eir)); + if (dev_class) + memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class)); + return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL); } @@ -2286,3 +2352,29 @@ int mgmt_discovering(u16 index, u8 discovering) return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering, sizeof(discovering), NULL); } + +int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr) +{ + struct pending_cmd *cmd; + struct mgmt_ev_device_blocked ev; + + cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, index); + + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_DEVICE_BLOCKED, index, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); +} + +int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr) +{ + struct pending_cmd *cmd; + struct mgmt_ev_device_unblocked ev; + + cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, index); + + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, index, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); +} diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 391888b..759b635 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -182,18 +182,9 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) return; hci_send_acl(conn->hcon, skb, 0); -} - -static __u8 seclevel_to_authreq(__u8 level) -{ - switch (level) { - case BT_SECURITY_HIGH: - /* Right now we don't support bonding */ - return SMP_AUTH_MITM; - default: - return SMP_AUTH_NONE; - } + mod_timer(&conn->security_timer, jiffies + + msecs_to_jiffies(SMP_TIMEOUT)); } static void build_pairing_cmd(struct l2cap_conn *conn, @@ -205,7 +196,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn, dist_keys = 0; if (test_bit(HCI_PAIRABLE, &conn->hcon->hdev->flags)) { - dist_keys = SMP_DIST_ENC_KEY | SMP_DIST_ID_KEY | SMP_DIST_SIGN; + dist_keys = SMP_DIST_ENC_KEY; authreq |= SMP_AUTH_BONDING; } @@ -229,24 +220,184 @@ static void build_pairing_cmd(struct l2cap_conn *conn, static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size) { + struct smp_chan *smp = conn->smp_chan; + if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) || (max_key_size < SMP_MIN_ENC_KEY_SIZE)) return SMP_ENC_KEY_SIZE; - conn->smp_key_size = max_key_size; + smp->smp_key_size = max_key_size; return 0; } +static void confirm_work(struct work_struct *work) +{ + struct smp_chan *smp = container_of(work, struct smp_chan, confirm); + struct l2cap_conn *conn = smp->conn; + struct crypto_blkcipher *tfm; + struct smp_cmd_pairing_confirm cp; + int ret; + u8 res[16], reason; + + BT_DBG("conn %p", conn); + + tfm = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(tfm)) { + reason = SMP_UNSPECIFIED; + goto error; + } + + smp->tfm = tfm; + + if (conn->hcon->out) + ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, 0, + conn->src, conn->hcon->dst_type, conn->dst, + res); + else + ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, + conn->hcon->dst_type, conn->dst, 0, conn->src, + res); + if (ret) { + reason = SMP_UNSPECIFIED; + goto error; + } + + swap128(res, cp.confirm_val); + smp_send_cmd(smp->conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); + + return; + +error: + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); + smp_chan_destroy(conn); +} + +static void random_work(struct work_struct *work) +{ + struct smp_chan *smp = container_of(work, struct smp_chan, random); + struct l2cap_conn *conn = smp->conn; + struct hci_conn *hcon = conn->hcon; + struct crypto_blkcipher *tfm = smp->tfm; + u8 reason, confirm[16], res[16], key[16]; + int ret; + + if (IS_ERR_OR_NULL(tfm)) { + reason = SMP_UNSPECIFIED; + goto error; + } + + BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); + + if (hcon->out) + ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, 0, + conn->src, hcon->dst_type, conn->dst, + res); + else + ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, + hcon->dst_type, conn->dst, 0, conn->src, + res); + if (ret) { + reason = SMP_UNSPECIFIED; + goto error; + } + + swap128(res, confirm); + + if (memcmp(smp->pcnf, confirm, sizeof(smp->pcnf)) != 0) { + BT_ERR("Pairing failed (confirmation values mismatch)"); + reason = SMP_CONFIRM_FAILED; + goto error; + } + + if (hcon->out) { + u8 stk[16], rand[8]; + __le16 ediv; + + memset(rand, 0, sizeof(rand)); + ediv = 0; + + smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, key); + swap128(key, stk); + + memset(stk + smp->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); + + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) { + reason = SMP_UNSPECIFIED; + goto error; + } + + hci_le_start_enc(hcon, ediv, rand, stk); + hcon->enc_key_size = smp->smp_key_size; + } else { + u8 stk[16], r[16], rand[8]; + __le16 ediv; + + memset(rand, 0, sizeof(rand)); + ediv = 0; + + swap128(smp->prnd, r); + smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); + + smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, key); + swap128(key, stk); + + memset(stk + smp->smp_key_size, 0, + SMP_MAX_ENC_KEY_SIZE - smp->smp_key_size); + + hci_add_ltk(hcon->hdev, 0, conn->dst, smp->smp_key_size, + ediv, rand, stk); + } + + return; + +error: + smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason), &reason); + smp_chan_destroy(conn); +} + +static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) +{ + struct smp_chan *smp; + + smp = kzalloc(sizeof(struct smp_chan), GFP_ATOMIC); + if (!smp) + return NULL; + + INIT_WORK(&smp->confirm, confirm_work); + INIT_WORK(&smp->random, random_work); + + smp->conn = conn; + conn->smp_chan = smp; + + hci_conn_hold(conn->hcon); + + return smp; +} + +void smp_chan_destroy(struct l2cap_conn *conn) +{ + kfree(conn->smp_chan); + hci_conn_put(conn->hcon); +} + static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing rsp, *req = (void *) skb->data; + struct smp_chan *smp; u8 key_size; + int ret; BT_DBG("conn %p", conn); - conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], req, sizeof(*req)); + if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) + smp = smp_chan_create(conn); + + smp = conn->smp_chan; + + smp->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&smp->preq[1], req, sizeof(*req)); skb_pull(skb, sizeof(*req)); if (req->oob_flag) @@ -260,32 +411,33 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) return SMP_ENC_KEY_SIZE; /* Just works */ - memset(conn->tk, 0, sizeof(conn->tk)); + memset(smp->tk, 0, sizeof(smp->tk)); + + ret = smp_rand(smp->prnd); + if (ret) + return SMP_UNSPECIFIED; - conn->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&conn->prsp[1], &rsp, sizeof(rsp)); + smp->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&smp->prsp[1], &rsp, sizeof(rsp)); smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp); - mod_timer(&conn->security_timer, jiffies + - msecs_to_jiffies(SMP_TIMEOUT)); - return 0; } static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing *req, *rsp = (void *) skb->data; - struct smp_cmd_pairing_confirm cp; - struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + struct smp_chan *smp = conn->smp_chan; + struct hci_dev *hdev = conn->hcon->hdev; + u8 key_size; int ret; - u8 res[16], key_size; BT_DBG("conn %p", conn); skb_pull(skb, sizeof(*rsp)); - req = (void *) &conn->preq[1]; + req = (void *) &smp->preq[1]; key_size = min(req->max_key_size, rsp->max_key_size); if (check_enc_key_size(conn, key_size)) @@ -295,222 +447,154 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) return SMP_OOB_NOT_AVAIL; /* Just works */ - memset(conn->tk, 0, sizeof(conn->tk)); - - conn->prsp[0] = SMP_CMD_PAIRING_RSP; - memcpy(&conn->prsp[1], rsp, sizeof(*rsp)); - - ret = smp_rand(conn->prnd); - if (ret) - return SMP_UNSPECIFIED; + memset(smp->tk, 0, sizeof(smp->tk)); - ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, 0, - conn->src, conn->hcon->dst_type, conn->dst, res); + ret = smp_rand(smp->prnd); if (ret) return SMP_UNSPECIFIED; - swap128(res, cp.confirm_val); + smp->prsp[0] = SMP_CMD_PAIRING_RSP; + memcpy(&smp->prsp[1], rsp, sizeof(*rsp)); - smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); + queue_work(hdev->workqueue, &smp->confirm); return 0; } static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb) { - struct crypto_blkcipher *tfm = conn->hcon->hdev->tfm; + struct smp_chan *smp = conn->smp_chan; + struct hci_dev *hdev = conn->hcon->hdev; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - memcpy(conn->pcnf, skb->data, sizeof(conn->pcnf)); - skb_pull(skb, sizeof(conn->pcnf)); + memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf)); + skb_pull(skb, sizeof(smp->pcnf)); if (conn->hcon->out) { u8 random[16]; - swap128(conn->prnd, random); + swap128(smp->prnd, random); smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(random), random); } else { - struct smp_cmd_pairing_confirm cp; - int ret; - u8 res[16]; - - ret = smp_rand(conn->prnd); - if (ret) - return SMP_UNSPECIFIED; - - ret = smp_c1(tfm, conn->tk, conn->prnd, conn->preq, conn->prsp, - conn->hcon->dst_type, conn->dst, - 0, conn->src, res); - if (ret) - return SMP_CONFIRM_FAILED; - - swap128(res, cp.confirm_val); - - smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cp), &cp); + queue_work(hdev->workqueue, &smp->confirm); } - mod_timer(&conn->security_timer, jiffies + - msecs_to_jiffies(SMP_TIMEOUT)); - return 0; } static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) { - struct hci_conn *hcon = conn->hcon; - struct crypto_blkcipher *tfm = hcon->hdev->tfm; - int ret; - u8 key[16], res[16], random[16], confirm[16]; + struct smp_chan *smp = conn->smp_chan; + struct hci_dev *hdev = conn->hcon->hdev; - swap128(skb->data, random); - skb_pull(skb, sizeof(random)); - - if (conn->hcon->out) - ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, 0, - conn->src, conn->hcon->dst_type, conn->dst, - res); - else - ret = smp_c1(tfm, conn->tk, random, conn->preq, conn->prsp, - conn->hcon->dst_type, conn->dst, 0, conn->src, - res); - if (ret) - return SMP_UNSPECIFIED; - - BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - - swap128(res, confirm); - - if (memcmp(conn->pcnf, confirm, sizeof(conn->pcnf)) != 0) { - BT_ERR("Pairing failed (confirmation values mismatch)"); - return SMP_CONFIRM_FAILED; - } - - if (conn->hcon->out) { - u8 stk[16], rand[8]; - __le16 ediv; - - memset(rand, 0, sizeof(rand)); - ediv = 0; + BT_DBG("conn %p", conn); - smp_s1(tfm, conn->tk, random, conn->prnd, key); - swap128(key, stk); + swap128(skb->data, smp->rrnd); + skb_pull(skb, sizeof(smp->rrnd)); - memset(stk + conn->smp_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + queue_work(hdev->workqueue, &smp->random); - hci_le_start_enc(hcon, ediv, rand, stk); - hcon->enc_key_size = conn->smp_key_size; - } else { - u8 stk[16], r[16], rand[8]; - __le16 ediv; + return 0; +} - memset(rand, 0, sizeof(rand)); - ediv = 0; +static u8 smp_ltk_encrypt(struct l2cap_conn *conn) +{ + struct link_key *key; + struct key_master_id *master; + struct hci_conn *hcon = conn->hcon; - swap128(conn->prnd, r); - smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(r), r); + key = hci_find_link_key_type(hcon->hdev, conn->dst, + HCI_LK_SMP_LTK); + if (!key) + return 0; - smp_s1(tfm, conn->tk, conn->prnd, random, key); - swap128(key, stk); + if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, + &hcon->pend)) + return 1; - memset(stk + conn->smp_key_size, 0, - SMP_MAX_ENC_KEY_SIZE - conn->smp_key_size); + master = (void *) key->data; + hci_le_start_enc(hcon, master->ediv, master->rand, + key->val); + hcon->enc_key_size = key->pin_len; - hci_add_ltk(conn->hcon->hdev, 0, conn->dst, conn->smp_key_size, - ediv, rand, stk); - } + return 1; - return 0; } - static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_security_req *rp = (void *) skb->data; struct smp_cmd_pairing cp; struct hci_conn *hcon = conn->hcon; + struct smp_chan *smp; BT_DBG("conn %p", conn); - if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) + hcon->pending_sec_level = BT_SECURITY_MEDIUM; + + if (smp_ltk_encrypt(conn)) return 0; + if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) + return 0; + + smp = smp_chan_create(conn); + skb_pull(skb, sizeof(*rp)); memset(&cp, 0, sizeof(cp)); build_pairing_cmd(conn, &cp, NULL, rp->auth_req); - conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], &cp, sizeof(cp)); + smp->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&smp->preq[1], &cp, sizeof(cp)); smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); - mod_timer(&conn->security_timer, jiffies + - msecs_to_jiffies(SMP_TIMEOUT)); - - set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); - return 0; } int smp_conn_security(struct l2cap_conn *conn, __u8 sec_level) { struct hci_conn *hcon = conn->hcon; - __u8 authreq; + struct smp_chan *smp = conn->smp_chan; BT_DBG("conn %p hcon %p level 0x%2.2x", conn, hcon, sec_level); if (!lmp_host_le_capable(hcon->hdev)) return 1; - if (IS_ERR(hcon->hdev->tfm)) - return 1; - - if (test_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend)) - return 0; - if (sec_level == BT_SECURITY_LOW) return 1; if (hcon->sec_level >= sec_level) return 1; - authreq = seclevel_to_authreq(sec_level); - - if (hcon->link_mode & HCI_LM_MASTER) { - struct smp_cmd_pairing cp; - struct link_key *key; + if (hcon->link_mode & HCI_LM_MASTER) + if (smp_ltk_encrypt(conn)) + goto done; - key = hci_find_link_key_type(hcon->hdev, conn->dst, - HCI_LK_SMP_LTK); - if (key) { - struct key_master_id *master = (void *) key->data; + if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->pend)) + return 0; - hci_le_start_enc(hcon, master->ediv, master->rand, - key->val); - hcon->enc_key_size = key->pin_len; + smp = smp_chan_create(conn); - goto done; - } - - build_pairing_cmd(conn, &cp, NULL, authreq); - conn->preq[0] = SMP_CMD_PAIRING_REQ; - memcpy(&conn->preq[1], &cp, sizeof(cp)); + if (hcon->link_mode & HCI_LM_MASTER) { + struct smp_cmd_pairing cp; - mod_timer(&conn->security_timer, jiffies + - msecs_to_jiffies(SMP_TIMEOUT)); + build_pairing_cmd(conn, &cp, NULL, SMP_AUTH_NONE); + smp->preq[0] = SMP_CMD_PAIRING_REQ; + memcpy(&smp->preq[1], &cp, sizeof(cp)); smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); } else { struct smp_cmd_security_req cp; - cp.auth_req = authreq; + cp.auth_req = SMP_AUTH_NONE; smp_send_cmd(conn, SMP_CMD_SECURITY_REQ, sizeof(cp), &cp); } done: hcon->pending_sec_level = sec_level; - set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->pend); return 0; } @@ -518,10 +602,11 @@ done: static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_encrypt_info *rp = (void *) skb->data; + struct smp_chan *smp = conn->smp_chan; skb_pull(skb, sizeof(*rp)); - memcpy(conn->tk, rp->ltk, sizeof(conn->tk)); + memcpy(smp->tk, rp->ltk, sizeof(smp->tk)); return 0; } @@ -529,11 +614,12 @@ static int smp_cmd_encrypt_info(struct l2cap_conn *conn, struct sk_buff *skb) static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_master_ident *rp = (void *) skb->data; + struct smp_chan *smp = conn->smp_chan; skb_pull(skb, sizeof(*rp)); - hci_add_ltk(conn->hcon->hdev, 1, conn->src, conn->smp_key_size, - rp->ediv, rp->rand, conn->tk); + hci_add_ltk(conn->hcon->hdev, 1, conn->src, smp->smp_key_size, + rp->ediv, rp->rand, smp->tk); smp_distribute_keys(conn, 1); @@ -552,12 +638,6 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) goto done; } - if (IS_ERR(conn->hcon->hdev->tfm)) { - err = PTR_ERR(conn->hcon->hdev->tfm); - reason = SMP_PAIRING_NOTSUPP; - goto done; - } - skb_pull(skb, sizeof(code)); switch (code) { @@ -621,20 +701,21 @@ done: int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) { struct smp_cmd_pairing *req, *rsp; + struct smp_chan *smp = conn->smp_chan; __u8 *keydist; BT_DBG("conn %p force %d", conn, force); - if (IS_ERR(conn->hcon->hdev->tfm)) - return PTR_ERR(conn->hcon->hdev->tfm); + if (!test_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend)) + return 0; - rsp = (void *) &conn->prsp[1]; + rsp = (void *) &smp->prsp[1]; /* The responder sends its keys first */ if (!force && conn->hcon->out && (rsp->resp_key_dist & 0x07)) return 0; - req = (void *) &conn->preq[1]; + req = (void *) &smp->preq[1]; if (conn->hcon->out) { keydist = &rsp->init_key_dist; @@ -658,7 +739,7 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) smp_send_cmd(conn, SMP_CMD_ENCRYPT_INFO, sizeof(enc), &enc); - hci_add_ltk(conn->hcon->hdev, 1, conn->dst, conn->smp_key_size, + hci_add_ltk(conn->hcon->hdev, 1, conn->dst, smp->smp_key_size, ediv, ident.rand, enc.ltk); ident.ediv = cpu_to_le16(ediv); @@ -698,5 +779,11 @@ int smp_distribute_keys(struct l2cap_conn *conn, __u8 force) *keydist &= ~SMP_DIST_SIGN; } + if (conn->hcon->out || force) { + clear_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->pend); + del_timer(&conn->security_timer); + smp_chan_destroy(conn); + } + return 0; } diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 567e3e5..9b1a95e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1271,9 +1271,11 @@ static int ieee80211_change_bss(struct wiphy *wiphy, } static int ieee80211_set_txq_params(struct wiphy *wiphy, + struct net_device *dev, struct ieee80211_txq_params *params) { struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_tx_queue_params p; if (!local->ops->conf_tx) @@ -1294,8 +1296,8 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, if (params->queue >= local->hw.queues) return -EINVAL; - local->tx_conf[params->queue] = p; - if (drv_conf_tx(local, params->queue, &p)) { + sdata->tx_conf[params->queue] = p; + if (drv_conf_tx(local, sdata, params->queue, &p)) { wiphy_debug(local->hw.wiphy, "failed to set TX queue parameters for queue %d\n", params->queue); @@ -1869,7 +1871,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, u64 *cookie) + const u8 *buf, size_t len, bool no_cck, + u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; @@ -1896,6 +1899,9 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, flags |= IEEE80211_TX_CTL_TX_OFFCHAN; } + if (no_cck) + flags |= IEEE80211_TX_CTL_NO_CCK_RATE; + if (is_offchan && !offchan) return -EBUSY; diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index c914116..883996b 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -78,57 +78,6 @@ DEBUGFS_READONLY_FILE(wep_iv, "%#08x", DEBUGFS_READONLY_FILE(rate_ctrl_alg, "%s", local->rate_ctrl ? local->rate_ctrl->ops->name : "hw/driver"); -static ssize_t tsf_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - u64 tsf; - - tsf = drv_get_tsf(local); - - return mac80211_format_buffer(user_buf, count, ppos, "0x%016llx\n", - (unsigned long long) tsf); -} - -static ssize_t tsf_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - unsigned long long tsf; - char buf[100]; - size_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - if (strncmp(buf, "reset", 5) == 0) { - if (local->ops->reset_tsf) { - drv_reset_tsf(local); - wiphy_info(local->hw.wiphy, "debugfs reset TSF\n"); - } - } else { - tsf = simple_strtoul(buf, NULL, 0); - if (local->ops->set_tsf) { - drv_set_tsf(local, tsf); - wiphy_info(local->hw.wiphy, - "debugfs set TSF to %#018llx\n", tsf); - - } - } - - return count; -} - -static const struct file_operations tsf_ops = { - .read = tsf_read, - .write = tsf_write, - .open = mac80211_open_file_generic, - .llseek = default_llseek, -}; - static ssize_t reset_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { @@ -447,7 +396,6 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD(frequency); DEBUGFS_ADD(total_ps_buffered); DEBUGFS_ADD(wep_iv); - DEBUGFS_ADD(tsf); DEBUGFS_ADD(queues); DEBUGFS_ADD_MODE(reset, 0200); DEBUGFS_ADD(noack); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index dd04629..9352819 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -21,6 +21,7 @@ #include "rate.h" #include "debugfs.h" #include "debugfs_netdev.h" +#include "driver-ops.h" static ssize_t ieee80211_if_read( struct ieee80211_sub_if_data *sdata, @@ -331,6 +332,46 @@ static ssize_t ieee80211_if_fmt_num_buffered_multicast( } __IEEE80211_IF_FILE(num_buffered_multicast, NULL); +/* IBSS attributes */ +static ssize_t ieee80211_if_fmt_tsf( + const struct ieee80211_sub_if_data *sdata, char *buf, int buflen) +{ + struct ieee80211_local *local = sdata->local; + u64 tsf; + + tsf = drv_get_tsf(local, (struct ieee80211_sub_if_data *)sdata); + + return scnprintf(buf, buflen, "0x%016llx\n", (unsigned long long) tsf); +} + +static ssize_t ieee80211_if_parse_tsf( + struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) +{ + struct ieee80211_local *local = sdata->local; + unsigned long long tsf; + int ret; + + if (strncmp(buf, "reset", 5) == 0) { + if (local->ops->reset_tsf) { + drv_reset_tsf(local, sdata); + wiphy_info(local->hw.wiphy, "debugfs reset TSF\n"); + } + } else { + ret = kstrtoull(buf, 10, &tsf); + if (ret < 0) + return -EINVAL; + if (local->ops->set_tsf) { + drv_set_tsf(local, sdata, tsf); + wiphy_info(local->hw.wiphy, + "debugfs set TSF to %#018llx\n", tsf); + } + } + + return buflen; +} +__IEEE80211_IF_FILE_W(tsf); + + /* WDS attributes */ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); @@ -421,6 +462,11 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD_MODE(tkip_mic_test, 0200); } +static void add_ibss_files(struct ieee80211_sub_if_data *sdata) +{ + DEBUGFS_ADD_MODE(tsf, 0600); +} + static void add_wds_files(struct ieee80211_sub_if_data *sdata) { DEBUGFS_ADD(drop_unencrypted); @@ -515,7 +561,7 @@ static void add_files(struct ieee80211_sub_if_data *sdata) add_sta_files(sdata); break; case NL80211_IFTYPE_ADHOC: - /* XXX */ + add_ibss_files(sdata); break; case NL80211_IFTYPE_AP: add_ap_files(sdata); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 9001ff3..4f845c0 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -413,50 +413,55 @@ static inline void drv_sta_remove(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue, +static inline int drv_conf_tx(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, u16 queue, const struct ieee80211_tx_queue_params *params) { int ret = -EOPNOTSUPP; might_sleep(); - trace_drv_conf_tx(local, queue, params); + trace_drv_conf_tx(local, sdata, queue, params); if (local->ops->conf_tx) ret = local->ops->conf_tx(&local->hw, queue, params); trace_drv_return_int(local, ret); return ret; } -static inline u64 drv_get_tsf(struct ieee80211_local *local) +static inline u64 drv_get_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { u64 ret = -1ULL; might_sleep(); - trace_drv_get_tsf(local); + trace_drv_get_tsf(local, sdata); if (local->ops->get_tsf) - ret = local->ops->get_tsf(&local->hw); + ret = local->ops->get_tsf(&local->hw, &sdata->vif); trace_drv_return_u64(local, ret); return ret; } -static inline void drv_set_tsf(struct ieee80211_local *local, u64 tsf) +static inline void drv_set_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + u64 tsf) { might_sleep(); - trace_drv_set_tsf(local, tsf); + trace_drv_set_tsf(local, sdata, tsf); if (local->ops->set_tsf) - local->ops->set_tsf(&local->hw, tsf); + local->ops->set_tsf(&local->hw, &sdata->vif, tsf); trace_drv_return_void(local); } -static inline void drv_reset_tsf(struct ieee80211_local *local) +static inline void drv_reset_tsf(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) { might_sleep(); - trace_drv_reset_tsf(local); + trace_drv_reset_tsf(local, sdata); if (local->ops->reset_tsf) - local->ops->reset_tsf(&local->hw); + local->ops->reset_tsf(&local->hw, &sdata->vif); trace_drv_return_void(local); } diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index f47b00dc7..a46b279 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -697,64 +697,76 @@ TRACE_EVENT(drv_sta_remove, ); TRACE_EVENT(drv_conf_tx, - TP_PROTO(struct ieee80211_local *local, u16 queue, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + u16 queue, const struct ieee80211_tx_queue_params *params), - TP_ARGS(local, queue, params), + TP_ARGS(local, sdata, queue, params), TP_STRUCT__entry( LOCAL_ENTRY + VIF_ENTRY __field(u16, queue) __field(u16, txop) __field(u16, cw_min) __field(u16, cw_max) __field(u8, aifs) + __field(bool, uapsd) ), TP_fast_assign( LOCAL_ASSIGN; + VIF_ASSIGN; __entry->queue = queue; __entry->txop = params->txop; __entry->cw_max = params->cw_max; __entry->cw_min = params->cw_min; __entry->aifs = params->aifs; + __entry->uapsd = params->uapsd; ), TP_printk( - LOCAL_PR_FMT " queue:%d", - LOCAL_PR_ARG, __entry->queue + LOCAL_PR_FMT VIF_PR_FMT " queue:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->queue ) ); -DEFINE_EVENT(local_only_evt, drv_get_tsf, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +DEFINE_EVENT(local_sdata_evt, drv_get_tsf, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); TRACE_EVENT(drv_set_tsf, - TP_PROTO(struct ieee80211_local *local, u64 tsf), + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + u64 tsf), - TP_ARGS(local, tsf), + TP_ARGS(local, sdata, tsf), TP_STRUCT__entry( LOCAL_ENTRY + VIF_ENTRY __field(u64, tsf) ), TP_fast_assign( LOCAL_ASSIGN; + VIF_ASSIGN; __entry->tsf = tsf; ), TP_printk( - LOCAL_PR_FMT " tsf:%llu", - LOCAL_PR_ARG, (unsigned long long)__entry->tsf + LOCAL_PR_FMT VIF_PR_FMT " tsf:%llu", + LOCAL_PR_ARG, VIF_PR_ARG, (unsigned long long)__entry->tsf ) ); -DEFINE_EVENT(local_only_evt, drv_reset_tsf, - TP_PROTO(struct ieee80211_local *local), - TP_ARGS(local) +DEFINE_EVENT(local_sdata_evt, drv_reset_tsf, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) ); DEFINE_EVENT(local_only_evt, drv_tx_last_beacon, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 4f9235b..41f16dd 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -81,7 +81,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, lockdep_assert_held(&ifibss->mtx); /* Reset own TSF to allow time synchronization work. */ - drv_reset_tsf(local); + drv_reset_tsf(local, sdata); skb = ifibss->skb; RCU_INIT_POINTER(ifibss->presp, NULL); @@ -382,7 +382,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, * second best option: get current TSF * (will return -1 if not supported) */ - rx_timestamp = drv_get_tsf(local); + rx_timestamp = drv_get_tsf(local, sdata); } #ifdef CONFIG_MAC80211_IBSS_DEBUG @@ -417,7 +417,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, * must be callable in atomic context. */ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, - u8 *bssid,u8 *addr, u32 supp_rates, + u8 *bssid, u8 *addr, u32 supp_rates, gfp_t gfp) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 21186e2..5cadcbb 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -609,6 +609,8 @@ struct ieee80211_sub_if_data { __be16 control_port_protocol; bool control_port_no_encrypt; + struct ieee80211_tx_queue_params tx_conf[IEEE80211_MAX_QUEUES]; + struct work_struct work; struct sk_buff_head skb_queue; @@ -751,7 +753,6 @@ struct ieee80211_local { struct workqueue_struct *workqueue; unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES]; - struct ieee80211_tx_queue_params tx_conf[IEEE80211_MAX_QUEUES]; /* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */ spinlock_t queue_stop_reason_lock; @@ -1324,7 +1325,7 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, - u32 ratemask, bool directed); + u32 ratemask, bool directed, bool no_cck); void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, const size_t supp_rates_len, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index a33c58f..f435026 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -460,17 +460,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, synchronize_rcu(); kfree(old_beacon); - /* free all potentially still buffered bcast frames */ - while ((skb = skb_dequeue(&sdata->u.ap.ps_bc_buf))) { - local->total_ps_buffered--; - dev_kfree_skb(skb); - } - /* down all dependent devices, that is VLANs */ list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans, u.vlan.list) dev_close(vlan->dev); WARN_ON(!list_empty(&sdata->u.ap.vlans)); + + /* free all potentially still buffered bcast frames */ + local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps_bc_buf); + skb_queue_purge(&sdata->u.ap.ps_bc_buf); } if (going_down) diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 4396906..1213a23 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -43,6 +43,10 @@ enum plink_event { CLS_IGNR }; +static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, + enum ieee80211_self_protected_actioncode action, + u8 *da, __le16 llid, __le16 plid, __le16 reason); + static inline void mesh_plink_inc_estab_count(struct ieee80211_sub_if_data *sdata) { @@ -133,6 +137,10 @@ void mesh_plink_deactivate(struct sta_info *sta) spin_lock_bh(&sta->lock); deactivated = __mesh_plink_deactivate(sta); + sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED); + mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, + sta->sta.addr, sta->llid, sta->plid, + sta->reason); spin_unlock_bh(&sta->lock); if (deactivated) diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2f92ae2..cd37a4e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -160,7 +160,8 @@ static int ecw2cw(int ecw) */ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, struct ieee80211_ht_info *hti, - const u8 *bssid, u16 ap_ht_cap_flags) + const u8 *bssid, u16 ap_ht_cap_flags, + bool beacon_htcap_ie) { struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; @@ -232,6 +233,21 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, WARN_ON(!ieee80211_set_channel_type(local, sdata, channel_type)); } + if (beacon_htcap_ie && (prev_chantype != channel_type)) { + /* + * Whenever the AP announces the HT mode change that can be + * 40MHz intolerant or etc., it would be safer to stop tx + * queues before doing hw config to avoid buffer overflow. + */ + ieee80211_stop_queues_by_reason(&sdata->local->hw, + IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); + + /* flush out all packets */ + synchronize_net(); + + drv_flush(local, false); + } + /* channel_type change automatically detected */ ieee80211_hw_config(local, 0); @@ -243,6 +259,10 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata, IEEE80211_RC_HT_CHANGED, channel_type); rcu_read_unlock(); + + if (beacon_htcap_ie) + ieee80211_wake_queues_by_reason(&sdata->local->hw, + IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); } ht_opmode = le16_to_cpu(hti->operation_mode); @@ -916,8 +936,8 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, params.aifs, params.cw_min, params.cw_max, params.txop, params.uapsd); #endif - local->tx_conf[queue] = params; - if (drv_conf_tx(local, queue, ¶ms)) + sdata->tx_conf[queue] = params; + if (drv_conf_tx(local, sdata, queue, ¶ms)) wiphy_debug(local->hw.wiphy, "failed to set TX queue parameters for queue %d\n", queue); @@ -1219,7 +1239,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) } else { ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0, - (u32) -1, true); + (u32) -1, true, false); } ifmgd->probe_send_count++; @@ -1588,7 +1608,8 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, (sdata->local->hw.queues >= 4) && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, - cbss->bssid, ap_ht_cap_flags); + cbss->bssid, ap_ht_cap_flags, + false); /* set AID and assoc capability, * ieee80211_set_associated() will tell the driver */ @@ -1921,24 +1942,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); - /* - * Whenever the AP announces the HT mode change that can be - * 40MHz intolerant or etc., it would be safer to stop tx - * queues before doing hw config to avoid buffer overflow. - */ - ieee80211_stop_queues_by_reason(&sdata->local->hw, - IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); - - /* flush out all packets */ - synchronize_net(); - - drv_flush(local, false); - changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem, - bssid, ap_ht_cap_flags); - - ieee80211_wake_queues_by_reason(&sdata->local->hw, - IEEE80211_QUEUE_STOP_REASON_CHTYPE_CHANGE); + bssid, ap_ht_cap_flags, true); } /* Note: country IE parsing is done for us by cfg80211 */ diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 3d5a2cb..f61244c 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -233,6 +233,27 @@ static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, /* could not find a basic rate; use original selection */ } +static inline s8 +rate_lowest_non_cck_index(struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta) +{ + int i; + + for (i = 0; i < sband->n_bitrates; i++) { + struct ieee80211_rate *srate = &sband->bitrates[i]; + if ((srate->bitrate == 10) || (srate->bitrate == 20) || + (srate->bitrate == 55) || (srate->bitrate == 110)) + continue; + + if (rate_supported(sta, sband->band, i)) + return i; + } + + /* No matching rate found */ + return 0; +} + + bool rate_control_send_low(struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) @@ -242,7 +263,13 @@ bool rate_control_send_low(struct ieee80211_sta *sta, int mcast_rate; if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) { - info->control.rates[0].idx = rate_lowest_index(txrc->sband, sta); + if ((sband->band != IEEE80211_BAND_2GHZ) || + !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) + info->control.rates[0].idx = + rate_lowest_index(txrc->sband, sta); + else + info->control.rates[0].idx = + rate_lowest_non_cck_index(txrc->sband, sta); info->control.rates[0].count = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? 1 : txrc->hw->max_rate_tries; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 6f09eca..830e60f 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -660,7 +660,8 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, local->scan_req->ssids[i].ssid, local->scan_req->ssids[i].ssid_len, local->scan_req->ie, local->scan_req->ie_len, - local->scan_req->rates[band], false); + local->scan_req->rates[band], false, + local->scan_req->no_cck); /* * After sending probe requests, wait for probe responses diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 4b1466d..2c9dc36 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -632,8 +632,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) qparam.uapsd = false; - local->tx_conf[queue] = qparam; - drv_conf_tx(local, queue, &qparam); + sdata->tx_conf[queue] = qparam; + drv_conf_tx(local, sdata, queue, &qparam); } /* after reinitialize QoS TX queues setting to default, @@ -899,14 +899,18 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, const u8 *ssid, size_t ssid_len, const u8 *ie, size_t ie_len, - u32 ratemask, bool directed) + u32 ratemask, bool directed, bool no_cck) { struct sk_buff *skb; skb = ieee80211_build_probe_req(sdata, dst, ratemask, ssid, ssid_len, ie, ie_len, directed); - if (skb) + if (skb) { + if (no_cck) + IEEE80211_SKB_CB(skb)->flags |= + IEEE80211_TX_CTL_NO_CCK_RATE; ieee80211_tx_skb(sdata, skb); + } } u32 ieee80211_sta_get_rates(struct ieee80211_local *local, @@ -1040,8 +1044,15 @@ int ieee80211_reconfig(struct ieee80211_local *local) mutex_unlock(&local->sta_mtx); /* reconfigure tx conf */ - for (i = 0; i < hw->queues; i++) - drv_conf_tx(local, i, &local->tx_conf[i]); + list_for_each_entry(sdata, &local->interfaces, list) { + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN || + sdata->vif.type == NL80211_IFTYPE_MONITOR || + !ieee80211_sdata_running(sdata)) + continue; + + for (i = 0; i < hw->queues; i++) + drv_conf_tx(local, sdata, i, &sdata->tx_conf[i]); + } /* reconfigure hardware */ ieee80211_hw_config(local, ~0); diff --git a/net/mac80211/work.c b/net/mac80211/work.c index bac3439..af374fa 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -458,7 +458,7 @@ ieee80211_direct_probe(struct ieee80211_work *wk) */ ieee80211_send_probe_req(sdata, NULL, wk->probe_auth.ssid, wk->probe_auth.ssid_len, NULL, 0, - (u32) -1, true); + (u32) -1, true, false); wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; run_again(local, wk->timeout); diff --git a/net/nfc/Kconfig b/net/nfc/Kconfig index 33e095b1..58cddad 100644 --- a/net/nfc/Kconfig +++ b/net/nfc/Kconfig @@ -13,4 +13,6 @@ menuconfig NFC To compile this support as a module, choose M here: the module will be called nfc. +source "net/nfc/nci/Kconfig" + source "drivers/nfc/Kconfig" diff --git a/net/nfc/Makefile b/net/nfc/Makefile index 16250c3..fbb550f 100644 --- a/net/nfc/Makefile +++ b/net/nfc/Makefile @@ -3,5 +3,6 @@ # obj-$(CONFIG_NFC) += nfc.o +obj-$(CONFIG_NFC_NCI) += nci/ nfc-objs := core.o netlink.o af_nfc.o rawsock.o diff --git a/net/nfc/core.c b/net/nfc/core.c index 284e2f6..47e02c1 100644 --- a/net/nfc/core.c +++ b/net/nfc/core.c @@ -53,6 +53,80 @@ int nfc_printk(const char *level, const char *format, ...) EXPORT_SYMBOL(nfc_printk); /** + * nfc_dev_up - turn on the NFC device + * + * @dev: The nfc device to be turned on + * + * The device remains up until the nfc_dev_down function is called. + */ +int nfc_dev_up(struct nfc_dev *dev) +{ + int rc = 0; + + nfc_dbg("dev_name=%s", dev_name(&dev->dev)); + + device_lock(&dev->dev); + + if (!device_is_registered(&dev->dev)) { + rc = -ENODEV; + goto error; + } + + if (dev->dev_up) { + rc = -EALREADY; + goto error; + } + + if (dev->ops->dev_up) + rc = dev->ops->dev_up(dev); + + if (!rc) + dev->dev_up = true; + +error: + device_unlock(&dev->dev); + return rc; +} + +/** + * nfc_dev_down - turn off the NFC device + * + * @dev: The nfc device to be turned off + */ +int nfc_dev_down(struct nfc_dev *dev) +{ + int rc = 0; + + nfc_dbg("dev_name=%s", dev_name(&dev->dev)); + + device_lock(&dev->dev); + + if (!device_is_registered(&dev->dev)) { + rc = -ENODEV; + goto error; + } + + if (!dev->dev_up) { + rc = -EALREADY; + goto error; + } + + if (dev->polling || dev->remote_activated) { + rc = -EBUSY; + goto error; + } + + if (dev->ops->dev_down) + dev->ops->dev_down(dev); + + dev->dev_up = false; + +error: + device_unlock(&dev->dev); + return rc; +} + +/** * nfc_start_poll - start polling for nfc targets * * @dev: The nfc device that must start polling @@ -144,6 +218,8 @@ int nfc_activate_target(struct nfc_dev *dev, u32 target_idx, u32 protocol) } rc = dev->ops->activate_target(dev, target_idx, protocol); + if (!rc) + dev->remote_activated = true; error: device_unlock(&dev->dev); @@ -170,6 +246,7 @@ int nfc_deactivate_target(struct nfc_dev *dev, u32 target_idx) } dev->ops->deactivate_target(dev, target_idx); + dev->remote_activated = false; error: device_unlock(&dev->dev); diff --git a/net/nfc/nci/Kconfig b/net/nfc/nci/Kconfig new file mode 100644 index 0000000..decdc49 --- /dev/null +++ b/net/nfc/nci/Kconfig @@ -0,0 +1,10 @@ +config NFC_NCI + depends on NFC && EXPERIMENTAL + tristate "NCI protocol support (EXPERIMENTAL)" + default n + help + NCI (NFC Controller Interface) is a communication protocol between + an NFC Controller (NFCC) and a Device Host (DH). + + Say Y here to compile NCI support into the kernel or say M to + compile it as module (nci). diff --git a/net/nfc/nci/Makefile b/net/nfc/nci/Makefile new file mode 100644 index 0000000..cdb3a2e --- /dev/null +++ b/net/nfc/nci/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux NFC NCI layer. +# + +obj-$(CONFIG_NFC_NCI) += nci.o + +nci-objs := core.o data.o lib.o ntf.o rsp.o
\ No newline at end of file diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c new file mode 100644 index 0000000..4047e29 --- /dev/null +++ b/net/nfc/nci/core.c @@ -0,0 +1,797 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias <ilane@ti.com> + * + * Acknowledgements: + * This file is based on hci_core.c, which was written + * by Maxim Krasnyansky. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/types.h> +#include <linux/workqueue.h> +#include <linux/completion.h> +#include <linux/sched.h> +#include <linux/bitops.h> +#include <linux/skbuff.h> + +#include "../nfc.h" +#include <net/nfc/nci.h> +#include <net/nfc/nci_core.h> +#include <linux/nfc.h> + +static void nci_cmd_work(struct work_struct *work); +static void nci_rx_work(struct work_struct *work); +static void nci_tx_work(struct work_struct *work); + +/* ---- NCI requests ---- */ + +void nci_req_complete(struct nci_dev *ndev, int result) +{ + if (ndev->req_status == NCI_REQ_PEND) { + ndev->req_result = result; + ndev->req_status = NCI_REQ_DONE; + complete(&ndev->req_completion); + } +} + +static void nci_req_cancel(struct nci_dev *ndev, int err) +{ + if (ndev->req_status == NCI_REQ_PEND) { + ndev->req_result = err; + ndev->req_status = NCI_REQ_CANCELED; + complete(&ndev->req_completion); + } +} + +/* Execute request and wait for completion. */ +static int __nci_request(struct nci_dev *ndev, + void (*req)(struct nci_dev *ndev, unsigned long opt), + unsigned long opt, + __u32 timeout) +{ + int rc = 0; + unsigned long completion_rc; + + ndev->req_status = NCI_REQ_PEND; + + init_completion(&ndev->req_completion); + req(ndev, opt); + completion_rc = wait_for_completion_interruptible_timeout( + &ndev->req_completion, + timeout); + + nfc_dbg("wait_for_completion return %ld", completion_rc); + + if (completion_rc > 0) { + switch (ndev->req_status) { + case NCI_REQ_DONE: + rc = nci_to_errno(ndev->req_result); + break; + + case NCI_REQ_CANCELED: + rc = -ndev->req_result; + break; + + default: + rc = -ETIMEDOUT; + break; + } + } else { + nfc_err("wait_for_completion_interruptible_timeout failed %ld", + completion_rc); + + rc = ((completion_rc == 0) ? (-ETIMEDOUT) : (completion_rc)); + } + + ndev->req_status = ndev->req_result = 0; + + return rc; +} + +static inline int nci_request(struct nci_dev *ndev, + void (*req)(struct nci_dev *ndev, unsigned long opt), + unsigned long opt, __u32 timeout) +{ + int rc; + + if (!test_bit(NCI_UP, &ndev->flags)) + return -ENETDOWN; + + /* Serialize all requests */ + mutex_lock(&ndev->req_lock); + rc = __nci_request(ndev, req, opt, timeout); + mutex_unlock(&ndev->req_lock); + + return rc; +} + +static void nci_reset_req(struct nci_dev *ndev, unsigned long opt) +{ + nci_send_cmd(ndev, NCI_OP_CORE_RESET_CMD, 0, NULL); +} + +static void nci_init_req(struct nci_dev *ndev, unsigned long opt) +{ + nci_send_cmd(ndev, NCI_OP_CORE_INIT_CMD, 0, NULL); +} + +static void nci_init_complete_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_core_conn_create_cmd conn_cmd; + struct nci_rf_disc_map_cmd cmd; + struct disc_map_config *cfg = cmd.mapping_configs; + __u8 *num = &cmd.num_mapping_configs; + int i; + + /* create static rf connection */ + conn_cmd.target_handle = 0; + conn_cmd.num_target_specific_params = 0; + nci_send_cmd(ndev, NCI_OP_CORE_CONN_CREATE_CMD, 2, &conn_cmd); + + /* set rf mapping configurations */ + *num = 0; + + /* by default mapping is set to NCI_RF_INTERFACE_FRAME */ + for (i = 0; i < ndev->num_supported_rf_interfaces; i++) { + if (ndev->supported_rf_interfaces[i] == + NCI_RF_INTERFACE_ISO_DEP) { + cfg[*num].rf_protocol = NCI_RF_PROTOCOL_ISO_DEP; + cfg[*num].mode = NCI_DISC_MAP_MODE_BOTH; + cfg[*num].rf_interface_type = NCI_RF_INTERFACE_ISO_DEP; + (*num)++; + } else if (ndev->supported_rf_interfaces[i] == + NCI_RF_INTERFACE_NFC_DEP) { + cfg[*num].rf_protocol = NCI_RF_PROTOCOL_NFC_DEP; + cfg[*num].mode = NCI_DISC_MAP_MODE_BOTH; + cfg[*num].rf_interface_type = NCI_RF_INTERFACE_NFC_DEP; + (*num)++; + } + + if (*num == NCI_MAX_NUM_MAPPING_CONFIGS) + break; + } + + nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_MAP_CMD, + (1 + ((*num)*sizeof(struct disc_map_config))), + &cmd); +} + +static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_rf_disc_cmd cmd; + __u32 protocols = opt; + + cmd.num_disc_configs = 0; + + if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && + (protocols & NFC_PROTO_JEWEL_MASK + || protocols & NFC_PROTO_MIFARE_MASK + || protocols & NFC_PROTO_ISO14443_MASK + || protocols & NFC_PROTO_NFC_DEP_MASK)) { + cmd.disc_configs[cmd.num_disc_configs].type = + NCI_DISCOVERY_TYPE_POLL_A_PASSIVE; + cmd.disc_configs[cmd.num_disc_configs].frequency = 1; + cmd.num_disc_configs++; + } + + if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && + (protocols & NFC_PROTO_ISO14443_MASK)) { + cmd.disc_configs[cmd.num_disc_configs].type = + NCI_DISCOVERY_TYPE_POLL_B_PASSIVE; + cmd.disc_configs[cmd.num_disc_configs].frequency = 1; + cmd.num_disc_configs++; + } + + if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) && + (protocols & NFC_PROTO_FELICA_MASK + || protocols & NFC_PROTO_NFC_DEP_MASK)) { + cmd.disc_configs[cmd.num_disc_configs].type = + NCI_DISCOVERY_TYPE_POLL_F_PASSIVE; + cmd.disc_configs[cmd.num_disc_configs].frequency = 1; + cmd.num_disc_configs++; + } + + nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD, + (1 + (cmd.num_disc_configs*sizeof(struct disc_config))), + &cmd); +} + +static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt) +{ + struct nci_rf_deactivate_cmd cmd; + + cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE; + + nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD, + sizeof(struct nci_rf_deactivate_cmd), + &cmd); +} + +static int nci_open_device(struct nci_dev *ndev) +{ + int rc = 0; + + mutex_lock(&ndev->req_lock); + + if (test_bit(NCI_UP, &ndev->flags)) { + rc = -EALREADY; + goto done; + } + + if (ndev->ops->open(ndev)) { + rc = -EIO; + goto done; + } + + atomic_set(&ndev->cmd_cnt, 1); + + set_bit(NCI_INIT, &ndev->flags); + + rc = __nci_request(ndev, nci_reset_req, 0, + msecs_to_jiffies(NCI_RESET_TIMEOUT)); + + if (!rc) { + rc = __nci_request(ndev, nci_init_req, 0, + msecs_to_jiffies(NCI_INIT_TIMEOUT)); + } + + if (!rc) { + rc = __nci_request(ndev, nci_init_complete_req, 0, + msecs_to_jiffies(NCI_INIT_TIMEOUT)); + } + + clear_bit(NCI_INIT, &ndev->flags); + + if (!rc) { + set_bit(NCI_UP, &ndev->flags); + } else { + /* Init failed, cleanup */ + skb_queue_purge(&ndev->cmd_q); + skb_queue_purge(&ndev->rx_q); + skb_queue_purge(&ndev->tx_q); + + ndev->ops->close(ndev); + ndev->flags = 0; + } + +done: + mutex_unlock(&ndev->req_lock); + return rc; +} + +static int nci_close_device(struct nci_dev *ndev) +{ + nci_req_cancel(ndev, ENODEV); + mutex_lock(&ndev->req_lock); + + if (!test_and_clear_bit(NCI_UP, &ndev->flags)) { + del_timer_sync(&ndev->cmd_timer); + mutex_unlock(&ndev->req_lock); + return 0; + } + + /* Drop RX and TX queues */ + skb_queue_purge(&ndev->rx_q); + skb_queue_purge(&ndev->tx_q); + + /* Flush RX and TX wq */ + flush_workqueue(ndev->rx_wq); + flush_workqueue(ndev->tx_wq); + + /* Reset device */ + skb_queue_purge(&ndev->cmd_q); + atomic_set(&ndev->cmd_cnt, 1); + + set_bit(NCI_INIT, &ndev->flags); + __nci_request(ndev, nci_reset_req, 0, + msecs_to_jiffies(NCI_RESET_TIMEOUT)); + clear_bit(NCI_INIT, &ndev->flags); + + /* Flush cmd wq */ + flush_workqueue(ndev->cmd_wq); + + /* After this point our queues are empty + * and no works are scheduled. */ + ndev->ops->close(ndev); + + /* Clear flags */ + ndev->flags = 0; + + mutex_unlock(&ndev->req_lock); + + return 0; +} + +/* NCI command timer function */ +static void nci_cmd_timer(unsigned long arg) +{ + struct nci_dev *ndev = (void *) arg; + + nfc_dbg("entry"); + + atomic_set(&ndev->cmd_cnt, 1); + queue_work(ndev->cmd_wq, &ndev->cmd_work); +} + +static int nci_dev_up(struct nfc_dev *nfc_dev) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry"); + + return nci_open_device(ndev); +} + +static int nci_dev_down(struct nfc_dev *nfc_dev) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry"); + + return nci_close_device(ndev); +} + +static int nci_start_poll(struct nfc_dev *nfc_dev, __u32 protocols) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + int rc; + + nfc_dbg("entry"); + + if (test_bit(NCI_DISCOVERY, &ndev->flags)) { + nfc_err("unable to start poll, since poll is already active"); + return -EBUSY; + } + + if (ndev->target_active_prot) { + nfc_err("there is an active target"); + return -EBUSY; + } + + if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { + nfc_dbg("target is active, implicitly deactivate..."); + + rc = nci_request(ndev, nci_rf_deactivate_req, 0, + msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); + if (rc) + return -EBUSY; + } + + rc = nci_request(ndev, nci_rf_discover_req, protocols, + msecs_to_jiffies(NCI_RF_DISC_TIMEOUT)); + + if (!rc) + ndev->poll_prots = protocols; + + return rc; +} + +static void nci_stop_poll(struct nfc_dev *nfc_dev) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry"); + + if (!test_bit(NCI_DISCOVERY, &ndev->flags)) { + nfc_err("unable to stop poll, since poll is not active"); + return; + } + + nci_request(ndev, nci_rf_deactivate_req, 0, + msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); +} + +static int nci_activate_target(struct nfc_dev *nfc_dev, __u32 target_idx, + __u32 protocol) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry, target_idx %d, protocol 0x%x", target_idx, protocol); + + if (!test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { + nfc_err("there is no available target to activate"); + return -EINVAL; + } + + if (ndev->target_active_prot) { + nfc_err("there is already an active target"); + return -EBUSY; + } + + if (!(ndev->target_available_prots & (1 << protocol))) { + nfc_err("target does not support the requested protocol 0x%x", + protocol); + return -EINVAL; + } + + ndev->target_active_prot = protocol; + ndev->target_available_prots = 0; + + return 0; +} + +static void nci_deactivate_target(struct nfc_dev *nfc_dev, __u32 target_idx) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + + nfc_dbg("entry, target_idx %d", target_idx); + + if (!ndev->target_active_prot) { + nfc_err("unable to deactivate target, no active target"); + return; + } + + ndev->target_active_prot = 0; + + if (test_bit(NCI_POLL_ACTIVE, &ndev->flags)) { + nci_request(ndev, nci_rf_deactivate_req, 0, + msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT)); + } +} + +static int nci_data_exchange(struct nfc_dev *nfc_dev, __u32 target_idx, + struct sk_buff *skb, + data_exchange_cb_t cb, + void *cb_context) +{ + struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); + int rc; + + nfc_dbg("entry, target_idx %d, len %d", target_idx, skb->len); + + if (!ndev->target_active_prot) { + nfc_err("unable to exchange data, no active target"); + return -EINVAL; + } + + if (test_and_set_bit(NCI_DATA_EXCHANGE, &ndev->flags)) + return -EBUSY; + + /* store cb and context to be used on receiving data */ + ndev->data_exchange_cb = cb; + ndev->data_exchange_cb_context = cb_context; + + rc = nci_send_data(ndev, ndev->conn_id, skb); + if (rc) + clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); + + return rc; +} + +static struct nfc_ops nci_nfc_ops = { + .dev_up = nci_dev_up, + .dev_down = nci_dev_down, + .start_poll = nci_start_poll, + .stop_poll = nci_stop_poll, + .activate_target = nci_activate_target, + .deactivate_target = nci_deactivate_target, + .data_exchange = nci_data_exchange, +}; + +/* ---- Interface to NCI drivers ---- */ + +/** + * nci_allocate_device - allocate a new nci device + * + * @ops: device operations + * @supported_protocols: NFC protocols supported by the device + */ +struct nci_dev *nci_allocate_device(struct nci_ops *ops, + __u32 supported_protocols, + int tx_headroom, + int tx_tailroom) +{ + struct nci_dev *ndev; + + nfc_dbg("entry, supported_protocols 0x%x", supported_protocols); + + if (!ops->open || !ops->close || !ops->send) + return NULL; + + if (!supported_protocols) + return NULL; + + ndev = kzalloc(sizeof(struct nci_dev), GFP_KERNEL); + if (!ndev) + return NULL; + + ndev->ops = ops; + ndev->tx_headroom = tx_headroom; + ndev->tx_tailroom = tx_tailroom; + + ndev->nfc_dev = nfc_allocate_device(&nci_nfc_ops, + supported_protocols, + tx_headroom + NCI_DATA_HDR_SIZE, + tx_tailroom); + if (!ndev->nfc_dev) + goto free_exit; + + nfc_set_drvdata(ndev->nfc_dev, ndev); + + return ndev; + +free_exit: + kfree(ndev); + return NULL; +} +EXPORT_SYMBOL(nci_allocate_device); + +/** + * nci_free_device - deallocate nci device + * + * @ndev: The nci device to deallocate + */ +void nci_free_device(struct nci_dev *ndev) +{ + nfc_dbg("entry"); + + nfc_free_device(ndev->nfc_dev); + kfree(ndev); +} +EXPORT_SYMBOL(nci_free_device); + +/** + * nci_register_device - register a nci device in the nfc subsystem + * + * @dev: The nci device to register + */ +int nci_register_device(struct nci_dev *ndev) +{ + int rc; + struct device *dev = &ndev->nfc_dev->dev; + char name[32]; + + nfc_dbg("entry"); + + rc = nfc_register_device(ndev->nfc_dev); + if (rc) + goto exit; + + ndev->flags = 0; + + INIT_WORK(&ndev->cmd_work, nci_cmd_work); + snprintf(name, sizeof(name), "%s_nci_cmd_wq", dev_name(dev)); + ndev->cmd_wq = create_singlethread_workqueue(name); + if (!ndev->cmd_wq) { + rc = -ENOMEM; + goto unreg_exit; + } + + INIT_WORK(&ndev->rx_work, nci_rx_work); + snprintf(name, sizeof(name), "%s_nci_rx_wq", dev_name(dev)); + ndev->rx_wq = create_singlethread_workqueue(name); + if (!ndev->rx_wq) { + rc = -ENOMEM; + goto destroy_cmd_wq_exit; + } + + INIT_WORK(&ndev->tx_work, nci_tx_work); + snprintf(name, sizeof(name), "%s_nci_tx_wq", dev_name(dev)); + ndev->tx_wq = create_singlethread_workqueue(name); + if (!ndev->tx_wq) { + rc = -ENOMEM; + goto destroy_rx_wq_exit; + } + + skb_queue_head_init(&ndev->cmd_q); + skb_queue_head_init(&ndev->rx_q); + skb_queue_head_init(&ndev->tx_q); + + setup_timer(&ndev->cmd_timer, nci_cmd_timer, + (unsigned long) ndev); + + mutex_init(&ndev->req_lock); + + goto exit; + +destroy_rx_wq_exit: + destroy_workqueue(ndev->rx_wq); + +destroy_cmd_wq_exit: + destroy_workqueue(ndev->cmd_wq); + +unreg_exit: + nfc_unregister_device(ndev->nfc_dev); + +exit: + return rc; +} +EXPORT_SYMBOL(nci_register_device); + +/** + * nci_unregister_device - unregister a nci device in the nfc subsystem + * + * @dev: The nci device to unregister + */ +void nci_unregister_device(struct nci_dev *ndev) +{ + nfc_dbg("entry"); + + nci_close_device(ndev); + + destroy_workqueue(ndev->cmd_wq); + destroy_workqueue(ndev->rx_wq); + destroy_workqueue(ndev->tx_wq); + + nfc_unregister_device(ndev->nfc_dev); +} +EXPORT_SYMBOL(nci_unregister_device); + +/** + * nci_recv_frame - receive frame from NCI drivers + * + * @skb: The sk_buff to receive + */ +int nci_recv_frame(struct sk_buff *skb) +{ + struct nci_dev *ndev = (struct nci_dev *) skb->dev; + + nfc_dbg("entry, len %d", skb->len); + + if (!ndev || (!test_bit(NCI_UP, &ndev->flags) + && !test_bit(NCI_INIT, &ndev->flags))) { + kfree_skb(skb); + return -ENXIO; + } + + /* Queue frame for rx worker thread */ + skb_queue_tail(&ndev->rx_q, skb); + queue_work(ndev->rx_wq, &ndev->rx_work); + + return 0; +} +EXPORT_SYMBOL(nci_recv_frame); + +static int nci_send_frame(struct sk_buff *skb) +{ + struct nci_dev *ndev = (struct nci_dev *) skb->dev; + + nfc_dbg("entry, len %d", skb->len); + + if (!ndev) { + kfree_skb(skb); + return -ENODEV; + } + + /* Get rid of skb owner, prior to sending to the driver. */ + skb_orphan(skb); + + return ndev->ops->send(skb); +} + +/* Send NCI command */ +int nci_send_cmd(struct nci_dev *ndev, __u16 opcode, __u8 plen, void *payload) +{ + struct nci_ctrl_hdr *hdr; + struct sk_buff *skb; + + nfc_dbg("entry, opcode 0x%x, plen %d", opcode, plen); + + skb = nci_skb_alloc(ndev, (NCI_CTRL_HDR_SIZE + plen), GFP_KERNEL); + if (!skb) { + nfc_err("no memory for command"); + return -ENOMEM; + } + + hdr = (struct nci_ctrl_hdr *) skb_put(skb, NCI_CTRL_HDR_SIZE); + hdr->gid = nci_opcode_gid(opcode); + hdr->oid = nci_opcode_oid(opcode); + hdr->plen = plen; + + nci_mt_set((__u8 *)hdr, NCI_MT_CMD_PKT); + nci_pbf_set((__u8 *)hdr, NCI_PBF_LAST); + + if (plen) + memcpy(skb_put(skb, plen), payload, plen); + + skb->dev = (void *) ndev; + + skb_queue_tail(&ndev->cmd_q, skb); + queue_work(ndev->cmd_wq, &ndev->cmd_work); + + return 0; +} + +/* ---- NCI TX Data worker thread ---- */ + +static void nci_tx_work(struct work_struct *work) +{ + struct nci_dev *ndev = container_of(work, struct nci_dev, tx_work); + struct sk_buff *skb; + + nfc_dbg("entry, credits_cnt %d", atomic_read(&ndev->credits_cnt)); + + /* Send queued tx data */ + while (atomic_read(&ndev->credits_cnt)) { + skb = skb_dequeue(&ndev->tx_q); + if (!skb) + return; + + atomic_dec(&ndev->credits_cnt); + + nfc_dbg("NCI TX: MT=data, PBF=%d, conn_id=%d, plen=%d", + nci_pbf(skb->data), + nci_conn_id(skb->data), + nci_plen(skb->data)); + + nci_send_frame(skb); + } +} + +/* ----- NCI RX worker thread (data & control) ----- */ + +static void nci_rx_work(struct work_struct *work) +{ + struct nci_dev *ndev = container_of(work, struct nci_dev, rx_work); + struct sk_buff *skb; + + while ((skb = skb_dequeue(&ndev->rx_q))) { + /* Process frame */ + switch (nci_mt(skb->data)) { + case NCI_MT_RSP_PKT: + nci_rsp_packet(ndev, skb); + break; + + case NCI_MT_NTF_PKT: + nci_ntf_packet(ndev, skb); + break; + + case NCI_MT_DATA_PKT: + nci_rx_data_packet(ndev, skb); + break; + + default: + nfc_err("unknown MT 0x%x", nci_mt(skb->data)); + kfree_skb(skb); + break; + } + } +} + +/* ----- NCI TX CMD worker thread ----- */ + +static void nci_cmd_work(struct work_struct *work) +{ + struct nci_dev *ndev = container_of(work, struct nci_dev, cmd_work); + struct sk_buff *skb; + + nfc_dbg("entry, cmd_cnt %d", atomic_read(&ndev->cmd_cnt)); + + /* Send queued command */ + if (atomic_read(&ndev->cmd_cnt)) { + skb = skb_dequeue(&ndev->cmd_q); + if (!skb) + return; + + atomic_dec(&ndev->cmd_cnt); + + nfc_dbg("NCI TX: MT=cmd, PBF=%d, GID=0x%x, OID=0x%x, plen=%d", + nci_pbf(skb->data), + nci_opcode_gid(nci_opcode(skb->data)), + nci_opcode_oid(nci_opcode(skb->data)), + nci_plen(skb->data)); + + nci_send_frame(skb); + + mod_timer(&ndev->cmd_timer, + jiffies + msecs_to_jiffies(NCI_CMD_TIMEOUT)); + } +} diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c new file mode 100644 index 0000000..e5ed90fc --- /dev/null +++ b/net/nfc/nci/data.c @@ -0,0 +1,247 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias <ilane@ti.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/wait.h> +#include <linux/bitops.h> +#include <linux/skbuff.h> + +#include "../nfc.h" +#include <net/nfc/nci.h> +#include <net/nfc/nci_core.h> +#include <linux/nfc.h> + +/* Complete data exchange transaction and forward skb to nfc core */ +void nci_data_exchange_complete(struct nci_dev *ndev, + struct sk_buff *skb, + int err) +{ + data_exchange_cb_t cb = ndev->data_exchange_cb; + void *cb_context = ndev->data_exchange_cb_context; + + nfc_dbg("entry, len %d, err %d", ((skb) ? (skb->len) : (0)), err); + + if (cb) { + ndev->data_exchange_cb = NULL; + ndev->data_exchange_cb_context = 0; + + /* forward skb to nfc core */ + cb(cb_context, skb, err); + } else if (skb) { + nfc_err("no rx callback, dropping rx data..."); + + /* no waiting callback, free skb */ + kfree_skb(skb); + } + + clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); +} + +/* ----------------- NCI TX Data ----------------- */ + +static inline void nci_push_data_hdr(struct nci_dev *ndev, + __u8 conn_id, + struct sk_buff *skb, + __u8 pbf) +{ + struct nci_data_hdr *hdr; + int plen = skb->len; + + hdr = (struct nci_data_hdr *) skb_push(skb, NCI_DATA_HDR_SIZE); + hdr->conn_id = conn_id; + hdr->rfu = 0; + hdr->plen = plen; + + nci_mt_set((__u8 *)hdr, NCI_MT_DATA_PKT); + nci_pbf_set((__u8 *)hdr, pbf); + + skb->dev = (void *) ndev; +} + +static int nci_queue_tx_data_frags(struct nci_dev *ndev, + __u8 conn_id, + struct sk_buff *skb) { + int total_len = skb->len; + unsigned char *data = skb->data; + unsigned long flags; + struct sk_buff_head frags_q; + struct sk_buff *skb_frag; + int frag_len; + int rc = 0; + + nfc_dbg("entry, conn_id 0x%x, total_len %d", conn_id, total_len); + + __skb_queue_head_init(&frags_q); + + while (total_len) { + frag_len = min_t(int, total_len, ndev->max_pkt_payload_size); + + skb_frag = nci_skb_alloc(ndev, + (NCI_DATA_HDR_SIZE + frag_len), + GFP_KERNEL); + if (skb_frag == NULL) { + rc = -ENOMEM; + goto free_exit; + } + skb_reserve(skb_frag, NCI_DATA_HDR_SIZE); + + /* first, copy the data */ + memcpy(skb_put(skb_frag, frag_len), data, frag_len); + + /* second, set the header */ + nci_push_data_hdr(ndev, conn_id, skb_frag, + ((total_len == frag_len) ? (NCI_PBF_LAST) : (NCI_PBF_CONT))); + + __skb_queue_tail(&frags_q, skb_frag); + + data += frag_len; + total_len -= frag_len; + + nfc_dbg("frag_len %d, remaining total_len %d", + frag_len, total_len); + } + + /* queue all fragments atomically */ + spin_lock_irqsave(&ndev->tx_q.lock, flags); + + while ((skb_frag = __skb_dequeue(&frags_q)) != NULL) + __skb_queue_tail(&ndev->tx_q, skb_frag); + + spin_unlock_irqrestore(&ndev->tx_q.lock, flags); + + /* free the original skb */ + kfree_skb(skb); + + goto exit; + +free_exit: + while ((skb_frag = __skb_dequeue(&frags_q)) != NULL) + kfree_skb(skb_frag); + +exit: + return rc; +} + +/* Send NCI data */ +int nci_send_data(struct nci_dev *ndev, __u8 conn_id, struct sk_buff *skb) +{ + int rc = 0; + + nfc_dbg("entry, conn_id 0x%x, plen %d", conn_id, skb->len); + + /* check if the packet need to be fragmented */ + if (skb->len <= ndev->max_pkt_payload_size) { + /* no need to fragment packet */ + nci_push_data_hdr(ndev, conn_id, skb, NCI_PBF_LAST); + + skb_queue_tail(&ndev->tx_q, skb); + } else { + /* fragment packet and queue the fragments */ + rc = nci_queue_tx_data_frags(ndev, conn_id, skb); + if (rc) { + nfc_err("failed to fragment tx data packet"); + goto free_exit; + } + } + + queue_work(ndev->tx_wq, &ndev->tx_work); + + goto exit; + +free_exit: + kfree_skb(skb); + +exit: + return rc; +} + +/* ----------------- NCI RX Data ----------------- */ + +static void nci_add_rx_data_frag(struct nci_dev *ndev, + struct sk_buff *skb, + __u8 pbf) +{ + int reassembly_len; + int err = 0; + + if (ndev->rx_data_reassembly) { + reassembly_len = ndev->rx_data_reassembly->len; + + /* first, make enough room for the already accumulated data */ + if (skb_cow_head(skb, reassembly_len)) { + nfc_err("error adding room for accumulated rx data"); + + kfree_skb(skb); + skb = 0; + + kfree_skb(ndev->rx_data_reassembly); + ndev->rx_data_reassembly = 0; + + err = -ENOMEM; + goto exit; + } + + /* second, combine the two fragments */ + memcpy(skb_push(skb, reassembly_len), + ndev->rx_data_reassembly->data, + reassembly_len); + + /* third, free old reassembly */ + kfree_skb(ndev->rx_data_reassembly); + ndev->rx_data_reassembly = 0; + } + + if (pbf == NCI_PBF_CONT) { + /* need to wait for next fragment, store skb and exit */ + ndev->rx_data_reassembly = skb; + return; + } + +exit: + nci_data_exchange_complete(ndev, skb, err); +} + +/* Rx Data packet */ +void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + __u8 pbf = nci_pbf(skb->data); + + nfc_dbg("entry, len %d", skb->len); + + nfc_dbg("NCI RX: MT=data, PBF=%d, conn_id=%d, plen=%d", + nci_pbf(skb->data), + nci_conn_id(skb->data), + nci_plen(skb->data)); + + /* strip the nci data header */ + skb_pull(skb, NCI_DATA_HDR_SIZE); + + if (ndev->target_active_prot == NFC_PROTO_MIFARE) { + /* frame I/F => remove the status byte */ + nfc_dbg("NFC_PROTO_MIFARE => remove the status byte"); + skb_trim(skb, (skb->len - 1)); + } + + nci_add_rx_data_frag(ndev, skb, pbf); +} diff --git a/net/nfc/nci/lib.c b/net/nfc/nci/lib.c new file mode 100644 index 0000000..b19dc2f --- /dev/null +++ b/net/nfc/nci/lib.c @@ -0,0 +1,94 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias <ilane@ti.com> + * + * Acknowledgements: + * This file is based on lib.c, which was written + * by Maxim Krasnyansky. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> + +#include <net/nfc/nci.h> + +/* NCI status codes to Unix errno mapping */ +int nci_to_errno(__u8 code) +{ + switch (code) { + case NCI_STATUS_OK: + return 0; + + case NCI_STATUS_REJECTED: + return -EBUSY; + + case NCI_STATUS_MESSAGE_CORRUPTED: + return -EBADMSG; + + case NCI_STATUS_BUFFER_FULL: + return -ENOBUFS; + + case NCI_STATUS_NOT_INITIALIZED: + return -EHOSTDOWN; + + case NCI_STATUS_SYNTAX_ERROR: + case NCI_STATUS_SEMANTIC_ERROR: + case NCI_STATUS_INVALID_PARAM: + case NCI_STATUS_RF_PROTOCOL_ERROR: + case NCI_STATUS_NFCEE_PROTOCOL_ERROR: + return -EPROTO; + + case NCI_STATUS_UNKNOWN_GID: + case NCI_STATUS_UNKNOWN_OID: + return -EBADRQC; + + case NCI_STATUS_MESSAGE_SIZE_EXCEEDED: + return -EMSGSIZE; + + case NCI_STATUS_DISCOVERY_ALREADY_STARTED: + return -EALREADY; + + case NCI_STATUS_DISCOVERY_TARGET_ACTIVATION_FAILED: + case NCI_STATUS_NFCEE_INTERFACE_ACTIVATION_FAILED: + return -ECONNREFUSED; + + case NCI_STATUS_RF_TRANSMISSION_ERROR: + case NCI_STATUS_NFCEE_TRANSMISSION_ERROR: + return -ECOMM; + + case NCI_STATUS_RF_TIMEOUT_ERROR: + case NCI_STATUS_NFCEE_TIMEOUT_ERROR: + return -ETIMEDOUT; + + case NCI_STATUS_RF_LINK_LOSS_ERROR: + return -ENOLINK; + + case NCI_STATUS_MAX_ACTIVE_NFCEE_INTERFACES_REACHED: + return -EDQUOT; + + case NCI_STATUS_FAILED: + default: + return -ENOSYS; + } +} +EXPORT_SYMBOL(nci_to_errno); diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c new file mode 100644 index 0000000..96633f5 --- /dev/null +++ b/net/nfc/nci/ntf.c @@ -0,0 +1,258 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias <ilane@ti.com> + * + * Acknowledgements: + * This file is based on hci_event.c, which was written + * by Maxim Krasnyansky. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/bitops.h> +#include <linux/skbuff.h> + +#include "../nfc.h" +#include <net/nfc/nci.h> +#include <net/nfc/nci_core.h> +#include <linux/nfc.h> + +/* Handle NCI Notification packets */ + +static void nci_core_conn_credits_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_core_conn_credit_ntf *ntf = (void *) skb->data; + int i; + + nfc_dbg("entry, num_entries %d", ntf->num_entries); + + if (ntf->num_entries > NCI_MAX_NUM_CONN) + ntf->num_entries = NCI_MAX_NUM_CONN; + + /* update the credits */ + for (i = 0; i < ntf->num_entries; i++) { + nfc_dbg("entry[%d]: conn_id %d, credits %d", i, + ntf->conn_entries[i].conn_id, + ntf->conn_entries[i].credits); + + if (ntf->conn_entries[i].conn_id == ndev->conn_id) { + /* found static rf connection */ + atomic_add(ntf->conn_entries[i].credits, + &ndev->credits_cnt); + } + } + + /* trigger the next tx */ + if (!skb_queue_empty(&ndev->tx_q)) + queue_work(ndev->tx_wq, &ndev->tx_work); +} + +static void nci_rf_field_info_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_rf_field_info_ntf *ntf = (void *) skb->data; + + nfc_dbg("entry, rf_field_status %d", ntf->rf_field_status); +} + +static int nci_rf_activate_nfca_passive_poll(struct nci_dev *ndev, + struct nci_rf_activate_ntf *ntf, __u8 *data) +{ + struct rf_tech_specific_params_nfca_poll *nfca_poll; + struct activation_params_nfca_poll_iso_dep *nfca_poll_iso_dep; + + nfca_poll = &ntf->rf_tech_specific_params.nfca_poll; + nfca_poll_iso_dep = &ntf->activation_params.nfca_poll_iso_dep; + + nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data)); + data += 2; + + nfca_poll->nfcid1_len = *data++; + + nfc_dbg("sens_res 0x%x, nfcid1_len %d", + nfca_poll->sens_res, + nfca_poll->nfcid1_len); + + memcpy(nfca_poll->nfcid1, data, nfca_poll->nfcid1_len); + data += nfca_poll->nfcid1_len; + + nfca_poll->sel_res_len = *data++; + + if (nfca_poll->sel_res_len != 0) + nfca_poll->sel_res = *data++; + + ntf->rf_interface_type = *data++; + ntf->activation_params_len = *data++; + + nfc_dbg("sel_res_len %d, sel_res 0x%x, rf_interface_type %d, activation_params_len %d", + nfca_poll->sel_res_len, + nfca_poll->sel_res, + ntf->rf_interface_type, + ntf->activation_params_len); + + switch (ntf->rf_interface_type) { + case NCI_RF_INTERFACE_ISO_DEP: + nfca_poll_iso_dep->rats_res_len = *data++; + if (nfca_poll_iso_dep->rats_res_len > 0) { + memcpy(nfca_poll_iso_dep->rats_res, + data, + nfca_poll_iso_dep->rats_res_len); + } + break; + + case NCI_RF_INTERFACE_FRAME: + /* no activation params */ + break; + + default: + nfc_err("unsupported rf_interface_type 0x%x", + ntf->rf_interface_type); + return -EPROTO; + } + + return 0; +} + +static void nci_target_found(struct nci_dev *ndev, + struct nci_rf_activate_ntf *ntf) +{ + struct nfc_target nfc_tgt; + + if (ntf->rf_protocol == NCI_RF_PROTOCOL_T2T) /* T2T MifareUL */ + nfc_tgt.supported_protocols = NFC_PROTO_MIFARE_MASK; + else if (ntf->rf_protocol == NCI_RF_PROTOCOL_ISO_DEP) /* 4A */ + nfc_tgt.supported_protocols = NFC_PROTO_ISO14443_MASK; + + nfc_tgt.sens_res = ntf->rf_tech_specific_params.nfca_poll.sens_res; + nfc_tgt.sel_res = ntf->rf_tech_specific_params.nfca_poll.sel_res; + + if (!(nfc_tgt.supported_protocols & ndev->poll_prots)) { + nfc_dbg("the target found does not have the desired protocol"); + return; + } + + nfc_dbg("new target found, supported_protocols 0x%x", + nfc_tgt.supported_protocols); + + ndev->target_available_prots = nfc_tgt.supported_protocols; + + nfc_targets_found(ndev->nfc_dev, &nfc_tgt, 1); +} + +static void nci_rf_activate_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_rf_activate_ntf ntf; + __u8 *data = skb->data; + int rc = -1; + + clear_bit(NCI_DISCOVERY, &ndev->flags); + set_bit(NCI_POLL_ACTIVE, &ndev->flags); + + ntf.target_handle = *data++; + ntf.rf_protocol = *data++; + ntf.rf_tech_and_mode = *data++; + ntf.rf_tech_specific_params_len = *data++; + + nfc_dbg("target_handle %d, rf_protocol 0x%x, rf_tech_and_mode 0x%x, rf_tech_specific_params_len %d", + ntf.target_handle, + ntf.rf_protocol, + ntf.rf_tech_and_mode, + ntf.rf_tech_specific_params_len); + + switch (ntf.rf_tech_and_mode) { + case NCI_NFC_A_PASSIVE_POLL_MODE: + rc = nci_rf_activate_nfca_passive_poll(ndev, &ntf, + data); + break; + + default: + nfc_err("unsupported rf_tech_and_mode 0x%x", + ntf.rf_tech_and_mode); + return; + } + + if (!rc) + nci_target_found(ndev, &ntf); +} + +static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 type = skb->data[0]; + + nfc_dbg("entry, type 0x%x", type); + + clear_bit(NCI_POLL_ACTIVE, &ndev->flags); + ndev->target_active_prot = 0; + + /* drop tx data queue */ + skb_queue_purge(&ndev->tx_q); + + /* drop partial rx data packet */ + if (ndev->rx_data_reassembly) { + kfree_skb(ndev->rx_data_reassembly); + ndev->rx_data_reassembly = 0; + } + + /* complete the data exchange transaction, if exists */ + if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags)) + nci_data_exchange_complete(ndev, NULL, -EIO); +} + +void nci_ntf_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + __u16 ntf_opcode = nci_opcode(skb->data); + + nfc_dbg("NCI RX: MT=ntf, PBF=%d, GID=0x%x, OID=0x%x, plen=%d", + nci_pbf(skb->data), + nci_opcode_gid(ntf_opcode), + nci_opcode_oid(ntf_opcode), + nci_plen(skb->data)); + + /* strip the nci control header */ + skb_pull(skb, NCI_CTRL_HDR_SIZE); + + switch (ntf_opcode) { + case NCI_OP_CORE_CONN_CREDITS_NTF: + nci_core_conn_credits_ntf_packet(ndev, skb); + break; + + case NCI_OP_RF_FIELD_INFO_NTF: + nci_rf_field_info_ntf_packet(ndev, skb); + break; + + case NCI_OP_RF_ACTIVATE_NTF: + nci_rf_activate_ntf_packet(ndev, skb); + break; + + case NCI_OP_RF_DEACTIVATE_NTF: + nci_rf_deactivate_ntf_packet(ndev, skb); + break; + + default: + nfc_err("unknown ntf opcode 0x%x", ntf_opcode); + break; + } + + kfree_skb(skb); +} diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c new file mode 100644 index 0000000..0403d4c --- /dev/null +++ b/net/nfc/nci/rsp.c @@ -0,0 +1,226 @@ +/* + * The NFC Controller Interface is the communication protocol between an + * NFC Controller (NFCC) and a Device Host (DH). + * + * Copyright (C) 2011 Texas Instruments, Inc. + * + * Written by Ilan Elias <ilane@ti.com> + * + * Acknowledgements: + * This file is based on hci_event.c, which was written + * by Maxim Krasnyansky. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/bitops.h> +#include <linux/skbuff.h> + +#include "../nfc.h" +#include <net/nfc/nci.h> +#include <net/nfc/nci_core.h> + +/* Handle NCI Response packets */ + +static void nci_core_reset_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nci_core_reset_rsp *rsp = (void *) skb->data; + + nfc_dbg("entry, status 0x%x", rsp->status); + + if (rsp->status == NCI_STATUS_OK) + ndev->nci_ver = rsp->nci_ver; + + nfc_dbg("nci_ver 0x%x", ndev->nci_ver); + + nci_req_complete(ndev, rsp->status); +} + +static void nci_core_init_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + struct nci_core_init_rsp_1 *rsp_1 = (void *) skb->data; + struct nci_core_init_rsp_2 *rsp_2; + + nfc_dbg("entry, status 0x%x", rsp_1->status); + + if (rsp_1->status != NCI_STATUS_OK) + return; + + ndev->nfcc_features = __le32_to_cpu(rsp_1->nfcc_features); + ndev->num_supported_rf_interfaces = rsp_1->num_supported_rf_interfaces; + + if (ndev->num_supported_rf_interfaces > + NCI_MAX_SUPPORTED_RF_INTERFACES) { + ndev->num_supported_rf_interfaces = + NCI_MAX_SUPPORTED_RF_INTERFACES; + } + + memcpy(ndev->supported_rf_interfaces, + rsp_1->supported_rf_interfaces, + ndev->num_supported_rf_interfaces); + + rsp_2 = (void *) (skb->data + 6 + ndev->num_supported_rf_interfaces); + + ndev->max_logical_connections = + rsp_2->max_logical_connections; + ndev->max_routing_table_size = + __le16_to_cpu(rsp_2->max_routing_table_size); + ndev->max_control_packet_payload_length = + rsp_2->max_control_packet_payload_length; + ndev->rf_sending_buffer_size = + __le16_to_cpu(rsp_2->rf_sending_buffer_size); + ndev->rf_receiving_buffer_size = + __le16_to_cpu(rsp_2->rf_receiving_buffer_size); + ndev->manufacturer_id = + __le16_to_cpu(rsp_2->manufacturer_id); + + nfc_dbg("nfcc_features 0x%x", + ndev->nfcc_features); + nfc_dbg("num_supported_rf_interfaces %d", + ndev->num_supported_rf_interfaces); + nfc_dbg("supported_rf_interfaces[0] 0x%x", + ndev->supported_rf_interfaces[0]); + nfc_dbg("supported_rf_interfaces[1] 0x%x", + ndev->supported_rf_interfaces[1]); + nfc_dbg("supported_rf_interfaces[2] 0x%x", + ndev->supported_rf_interfaces[2]); + nfc_dbg("supported_rf_interfaces[3] 0x%x", + ndev->supported_rf_interfaces[3]); + nfc_dbg("max_logical_connections %d", + ndev->max_logical_connections); + nfc_dbg("max_routing_table_size %d", + ndev->max_routing_table_size); + nfc_dbg("max_control_packet_payload_length %d", + ndev->max_control_packet_payload_length); + nfc_dbg("rf_sending_buffer_size %d", + ndev->rf_sending_buffer_size); + nfc_dbg("rf_receiving_buffer_size %d", + ndev->rf_receiving_buffer_size); + nfc_dbg("manufacturer_id 0x%x", + ndev->manufacturer_id); + + nci_req_complete(ndev, rsp_1->status); +} + +static void nci_core_conn_create_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + struct nci_core_conn_create_rsp *rsp = (void *) skb->data; + + nfc_dbg("entry, status 0x%x", rsp->status); + + if (rsp->status != NCI_STATUS_OK) + return; + + ndev->max_pkt_payload_size = rsp->max_pkt_payload_size; + ndev->initial_num_credits = rsp->initial_num_credits; + ndev->conn_id = rsp->conn_id; + + atomic_set(&ndev->credits_cnt, ndev->initial_num_credits); + + nfc_dbg("max_pkt_payload_size %d", ndev->max_pkt_payload_size); + nfc_dbg("initial_num_credits %d", ndev->initial_num_credits); + nfc_dbg("conn_id %d", ndev->conn_id); +} + +static void nci_rf_disc_map_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + nfc_dbg("entry, status 0x%x", status); + + nci_req_complete(ndev, status); +} + +static void nci_rf_disc_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + nfc_dbg("entry, status 0x%x", status); + + if (status == NCI_STATUS_OK) + set_bit(NCI_DISCOVERY, &ndev->flags); + + nci_req_complete(ndev, status); +} + +static void nci_rf_deactivate_rsp_packet(struct nci_dev *ndev, + struct sk_buff *skb) +{ + __u8 status = skb->data[0]; + + nfc_dbg("entry, status 0x%x", status); + + clear_bit(NCI_DISCOVERY, &ndev->flags); + + nci_req_complete(ndev, status); +} + +void nci_rsp_packet(struct nci_dev *ndev, struct sk_buff *skb) +{ + __u16 rsp_opcode = nci_opcode(skb->data); + + /* we got a rsp, stop the cmd timer */ + del_timer(&ndev->cmd_timer); + + nfc_dbg("NCI RX: MT=rsp, PBF=%d, GID=0x%x, OID=0x%x, plen=%d", + nci_pbf(skb->data), + nci_opcode_gid(rsp_opcode), + nci_opcode_oid(rsp_opcode), + nci_plen(skb->data)); + + /* strip the nci control header */ + skb_pull(skb, NCI_CTRL_HDR_SIZE); + + switch (rsp_opcode) { + case NCI_OP_CORE_RESET_RSP: + nci_core_reset_rsp_packet(ndev, skb); + break; + + case NCI_OP_CORE_INIT_RSP: + nci_core_init_rsp_packet(ndev, skb); + break; + + case NCI_OP_CORE_CONN_CREATE_RSP: + nci_core_conn_create_rsp_packet(ndev, skb); + break; + + case NCI_OP_RF_DISCOVER_MAP_RSP: + nci_rf_disc_map_rsp_packet(ndev, skb); + break; + + case NCI_OP_RF_DISCOVER_RSP: + nci_rf_disc_rsp_packet(ndev, skb); + break; + + case NCI_OP_RF_DEACTIVATE_RSP: + nci_rf_deactivate_rsp_packet(ndev, skb); + break; + + default: + nfc_err("unknown rsp opcode 0x%x", rsp_opcode); + break; + } + + kfree_skb(skb); + + /* trigger the next cmd */ + atomic_set(&ndev->cmd_cnt, 1); + if (!skb_queue_empty(&ndev->cmd_q)) + queue_work(ndev->cmd_wq, &ndev->cmd_work); +} diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c index ccdff79..03f8818 100644 --- a/net/nfc/netlink.c +++ b/net/nfc/netlink.c @@ -367,6 +367,52 @@ out_putdev: return rc; } +static int nfc_genl_dev_up(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + int rc; + u32 idx; + + nfc_dbg("entry"); + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) + return -EINVAL; + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) + return -ENODEV; + + rc = nfc_dev_up(dev); + + nfc_put_device(dev); + return rc; +} + +static int nfc_genl_dev_down(struct sk_buff *skb, struct genl_info *info) +{ + struct nfc_dev *dev; + int rc; + u32 idx; + + nfc_dbg("entry"); + + if (!info->attrs[NFC_ATTR_DEVICE_INDEX]) + return -EINVAL; + + idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]); + + dev = nfc_get_device(idx); + if (!dev) + return -ENODEV; + + rc = nfc_dev_down(dev); + + nfc_put_device(dev); + return rc; +} + static int nfc_genl_start_poll(struct sk_buff *skb, struct genl_info *info) { struct nfc_dev *dev; @@ -441,6 +487,16 @@ static struct genl_ops nfc_genl_ops[] = { .policy = nfc_genl_policy, }, { + .cmd = NFC_CMD_DEV_UP, + .doit = nfc_genl_dev_up, + .policy = nfc_genl_policy, + }, + { + .cmd = NFC_CMD_DEV_DOWN, + .doit = nfc_genl_dev_down, + .policy = nfc_genl_policy, + }, + { .cmd = NFC_CMD_START_POLL, .doit = nfc_genl_start_poll, .policy = nfc_genl_policy, diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h index aaf9832..b6753f4 100644 --- a/net/nfc/nfc.h +++ b/net/nfc/nfc.h @@ -24,7 +24,7 @@ #ifndef __LOCAL_NFC_H #define __LOCAL_NFC_H -#include <net/nfc.h> +#include <net/nfc/nfc.h> #include <net/sock.h> __attribute__((format (printf, 2, 3))) @@ -101,6 +101,10 @@ static inline void nfc_device_iter_exit(struct class_dev_iter *iter) class_dev_iter_exit(iter); } +int nfc_dev_up(struct nfc_dev *dev); + +int nfc_dev_down(struct nfc_dev *dev); + int nfc_start_poll(struct nfc_dev *dev, u32 protocols); int nfc_stop_poll(struct nfc_dev *dev); diff --git a/net/rfkill/core.c b/net/rfkill/core.c index be90640..5be1957 100644 --- a/net/rfkill/core.c +++ b/net/rfkill/core.c @@ -235,7 +235,7 @@ static bool __rfkill_set_hw_state(struct rfkill *rfkill, else rfkill->state &= ~RFKILL_BLOCK_HW; *change = prev != blocked; - any = rfkill->state & RFKILL_BLOCK_ANY; + any = !!(rfkill->state & RFKILL_BLOCK_ANY); spin_unlock_irqrestore(&rfkill->lock, flags); rfkill_led_trigger_event(rfkill); diff --git a/net/wireless/core.h b/net/wireless/core.h index 796a4bd..b9ec306 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -375,7 +375,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, u64 *cookie); + const u8 *buf, size_t len, bool no_cck, + u64 *cookie); /* SME */ int __cfg80211_connect(struct cfg80211_registered_device *rdev, @@ -406,6 +407,7 @@ void cfg80211_sme_failed_assoc(struct wireless_dev *wdev); bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev); /* internal helpers */ +bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher); int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, struct key_params *params, int key_idx, bool pairwise, const u8 *mac_addr); diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 832f657..21fc970 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -900,7 +900,8 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, u64 *cookie) + const u8 *buf, size_t len, bool no_cck, + u64 *cookie) { struct wireless_dev *wdev = dev->ieee80211_ptr; const struct ieee80211_mgmt *mgmt; @@ -991,7 +992,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, /* Transmit the Action frame as requested by user space */ return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan, channel_type, channel_type_valid, - wait, buf, len, cookie); + wait, buf, len, no_cck, cookie); } bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, @@ -1095,3 +1096,14 @@ void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, nl80211_gtk_rekey_notify(rdev, dev, bssid, replay_ctr, gfp); } EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); + +void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, + const u8 *bssid, bool preauth, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_pmksa_candidate_notify(rdev, dev, index, bssid, preauth, gfp); +} +EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1108954..b850757 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -191,6 +191,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { .len = IEEE80211_MAX_DATA_LEN }, [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, + [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, }; /* policy for the key attributes */ @@ -1235,6 +1236,11 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) goto bad_res; } + if (!netdev) { + result = -EINVAL; + goto bad_res; + } + nla_for_each_nested(nl_txq_params, info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], rem_txq_params) { @@ -1247,6 +1253,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) goto bad_res; result = rdev->ops->set_txq_params(&rdev->wiphy, + netdev, &txq_params); if (result) goto bad_res; @@ -2613,7 +2620,7 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) /* parse WME attributes if sta is WME capable */ if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && - (params.sta_flags_set & NL80211_STA_FLAG_WME) && + (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) && info->attrs[NL80211_ATTR_STA_WME]) { struct nlattr *tb[NL80211_STA_WME_MAX + 1]; struct nlattr *nla; @@ -3620,6 +3627,9 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) } } + request->no_cck = + nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); + request->dev = dev; request->wiphy = &rdev->wiphy; @@ -4126,22 +4136,6 @@ static bool nl80211_valid_wpa_versions(u32 wpa_versions) NL80211_WPA_VERSION_2)); } -static bool nl80211_valid_akm_suite(u32 akm) -{ - return akm == WLAN_AKM_SUITE_8021X || - akm == WLAN_AKM_SUITE_PSK; -} - -static bool nl80211_valid_cipher_suite(u32 cipher) -{ - return cipher == WLAN_CIPHER_SUITE_WEP40 || - cipher == WLAN_CIPHER_SUITE_WEP104 || - cipher == WLAN_CIPHER_SUITE_TKIP || - cipher == WLAN_CIPHER_SUITE_CCMP || - cipher == WLAN_CIPHER_SUITE_AES_CMAC; -} - - static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -4274,7 +4268,8 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, memcpy(settings->ciphers_pairwise, data, len); for (i = 0; i < settings->n_ciphers_pairwise; i++) - if (!nl80211_valid_cipher_suite( + if (!cfg80211_supported_cipher_suite( + &rdev->wiphy, settings->ciphers_pairwise[i])) return -EINVAL; } @@ -4282,7 +4277,8 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { settings->cipher_group = nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); - if (!nl80211_valid_cipher_suite(settings->cipher_group)) + if (!cfg80211_supported_cipher_suite(&rdev->wiphy, + settings->cipher_group)) return -EINVAL; } @@ -4295,7 +4291,7 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, if (info->attrs[NL80211_ATTR_AKM_SUITES]) { void *data; - int len, i; + int len; data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]); len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]); @@ -4304,11 +4300,10 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, if (len % sizeof(u32)) return -EINVAL; - memcpy(settings->akm_suites, data, len); + if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES) + return -EINVAL; - for (i = 0; i < settings->n_ciphers_pairwise; i++) - if (!nl80211_valid_akm_suite(settings->akm_suites[i])) - return -EINVAL; + memcpy(settings->akm_suites, data, len); } return 0; @@ -4527,8 +4522,12 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) wiphy = &rdev->wiphy; - if (info->attrs[NL80211_ATTR_MAC]) + if (info->attrs[NL80211_ATTR_MAC]) { ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); + + if (!is_valid_ether_addr(ibss.bssid)) + return -EINVAL; + } ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); @@ -5185,6 +5184,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) struct sk_buff *msg; unsigned int wait = 0; bool offchan; + bool no_cck; if (!info->attrs[NL80211_ATTR_FRAME] || !info->attrs[NL80211_ATTR_WIPHY_FREQ]) @@ -5221,6 +5221,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; + no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); + freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); chan = rdev_freq_to_chan(rdev, freq, channel_type); if (chan == NULL) @@ -5241,7 +5243,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) channel_type_valid, wait, nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]), - &cookie); + no_cck, &cookie); if (err) goto free_msg; @@ -7266,6 +7268,52 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int index, + const u8 *bssid, bool preauth, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *attr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + + attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE); + if (!attr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index); + NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid); + if (preauth) + NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH); + + nla_nest_end(msg, attr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + void nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *peer, diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 5d69c56..f24a1fb 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -113,4 +113,8 @@ void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *bssid, const u8 *replay_ctr, gfp_t gfp); +void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, int index, + const u8 *bssid, bool preauth, gfp_t gfp); + #endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/scan.c b/net/wireless/scan.c index b0f0039..0fb1424 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -228,6 +228,33 @@ const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len) } EXPORT_SYMBOL(cfg80211_find_ie); +const u8 *cfg80211_find_vendor_ie(unsigned int oui, u8 oui_type, + const u8 *ies, int len) +{ + struct ieee80211_vendor_ie *ie; + const u8 *pos = ies, *end = ies + len; + int ie_oui; + + while (pos < end) { + pos = cfg80211_find_ie(WLAN_EID_VENDOR_SPECIFIC, pos, + end - pos); + if (!pos) + return NULL; + + if (end - pos < sizeof(*ie)) + return NULL; + + ie = (struct ieee80211_vendor_ie *)pos; + ie_oui = ie->oui[0] << 16 | ie->oui[1] << 8 | ie->oui[2]; + if (ie_oui == oui && ie->oui_type == oui_type) + return pos; + + pos += 2 + ie->len; + } + return NULL; +} +EXPORT_SYMBOL(cfg80211_find_vendor_ie); + static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2) { const u8 *ie1 = cfg80211_find_ie(num, ies1, len1); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index dec0fa2..6e86d5a 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -110,17 +110,22 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) else { int i = 0, j; enum ieee80211_band band; + struct ieee80211_supported_band *bands; + struct ieee80211_channel *channel; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!wdev->wiphy->bands[band]) + bands = wdev->wiphy->bands[band]; + if (!bands) continue; - for (j = 0; j < wdev->wiphy->bands[band]->n_channels; - i++, j++) - request->channels[i] = - &wdev->wiphy->bands[band]->channels[j]; - request->rates[band] = - (1 << wdev->wiphy->bands[band]->n_bitrates) - 1; + for (j = 0; j < bands->n_channels; j++) { + channel = &bands->channels[j]; + if (channel->flags & IEEE80211_CHAN_DISABLED) + continue; + request->channels[i++] = channel; + } + request->rates[band] = (1 << bands->n_bitrates) - 1; } + n_channels = i; } request->n_channels = n_channels; request->ssids = (void *)&request->channels[n_channels]; diff --git a/net/wireless/util.c b/net/wireless/util.c index 39dbf4a..6304ed6 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -151,12 +151,19 @@ void ieee80211_set_bitrate_flags(struct wiphy *wiphy) set_mandatory_flags_band(wiphy->bands[band], band); } +bool cfg80211_supported_cipher_suite(struct wiphy *wiphy, u32 cipher) +{ + int i; + for (i = 0; i < wiphy->n_cipher_suites; i++) + if (cipher == wiphy->cipher_suites[i]) + return true; + return false; +} + int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, struct key_params *params, int key_idx, bool pairwise, const u8 *mac_addr) { - int i; - if (key_idx > 5) return -EINVAL; @@ -226,10 +233,7 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, } } - for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) - if (params->cipher == rdev->wiphy.cipher_suites[i]) - break; - if (i == rdev->wiphy.n_cipher_suites) + if (!cfg80211_supported_cipher_suite(&rdev->wiphy, params->cipher)) return -EINVAL; return 0; |