diff options
author | John W. Linville <linville@tuxdriver.com> | 2014-01-01 15:39:56 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2014-01-01 15:39:56 -0500 |
commit | ad86c55bac643a745f480d26689d153ec0f38b04 (patch) | |
tree | 8ae3e20d2f3619021393166fe1f50a8ca00c1c33 | |
parent | 21eb218989523b7bee28900aaec9f9296b70fa27 (diff) | |
parent | 39731b78b4afebb7501c05b68e2443a1b250b41c (diff) | |
download | blackbird-op-linux-ad86c55bac643a745f480d26689d153ec0f38b04.tar.gz blackbird-op-linux-ad86c55bac643a745f480d26689d153ec0f38b04.zip |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
123 files changed, 5206 insertions, 3982 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 9bae7901162d..0d58ff5d2a62 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1430,7 +1430,7 @@ F: Documentation/aoe/ F: drivers/block/aoe/ ATHEROS ATH GENERIC UTILITIES -M: "Luis R. Rodriguez" <mcgrof@qca.qualcomm.com> +M: "Luis R. Rodriguez" <mcgrof@do-not-panic.com> L: linux-wireless@vger.kernel.org S: Supported F: drivers/net/wireless/ath/* @@ -1438,7 +1438,7 @@ F: drivers/net/wireless/ath/* ATHEROS ATH5K WIRELESS DRIVER M: Jiri Slaby <jirislaby@gmail.com> M: Nick Kossifidis <mickflemm@gmail.com> -M: "Luis R. Rodriguez" <mcgrof@qca.qualcomm.com> +M: "Luis R. Rodriguez" <mcgrof@do-not-panic.com> L: linux-wireless@vger.kernel.org L: ath5k-devel@lists.ath5k.org W: http://wireless.kernel.org/en/users/Drivers/ath5k diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index e15430a82e90..5a9f6bdc88f1 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -176,6 +176,7 @@ static int bcma_register_cores(struct bcma_bus *bus) bcma_err(bus, "Could not register dev for core 0x%03X\n", core->id.id); + put_device(&core->dev); continue; } core->dev_registered = true; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 546d5da0b894..4f16d79c9eb1 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2754,9 +2754,9 @@ static int ath6kl_set_bitrate_mask64(struct wmi *wmi, u8 if_idx, mask->control[band].legacy << 4; /* copy mcs rate mask */ - mcsrate = mask->control[band].mcs[1]; + mcsrate = mask->control[band].ht_mcs[1]; mcsrate <<= 8; - mcsrate |= mask->control[band].mcs[0]; + mcsrate |= mask->control[band].ht_mcs[0]; ratemask[band] |= mcsrate << 12; ratemask[band] |= mcsrate << 28; } @@ -2806,7 +2806,7 @@ static int ath6kl_set_bitrate_mask32(struct wmi *wmi, u8 if_idx, mask->control[band].legacy << 4; /* copy mcs rate mask */ - mcsrate = mask->control[band].mcs[0]; + mcsrate = mask->control[band].ht_mcs[0]; ratemask[band] |= mcsrate << 12; ratemask[band] |= mcsrate << 20; } diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 337c459eda28..e9904e5ccd81 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -11,12 +11,14 @@ ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o ath9k-$(CONFIG_ATH9K_LEGACY_RATE_CONTROL) += rc.o ath9k-$(CONFIG_ATH9K_PCI) += pci.o ath9k-$(CONFIG_ATH9K_AHB) += ahb.o -ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o ath9k-$(CONFIG_ATH9K_TX99) += tx99.o ath9k-$(CONFIG_ATH9K_WOW) += wow.o +ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o \ + spectral.o + obj-$(CONFIG_ATH9K) += ath9k.o ath9k_hw-y:= \ diff --git a/drivers/net/wireless/ath/ath9k/antenna.c b/drivers/net/wireless/ath/ath9k/antenna.c index bd048cc69a33..a3668433dc02 100644 --- a/drivers/net/wireless/ath/ath9k/antenna.c +++ b/drivers/net/wireless/ath/ath9k/antenna.c @@ -724,14 +724,14 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) struct ath_ant_comb *antcomb = &sc->ant_comb; int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; int curr_main_set; - int main_rssi = rs->rs_rssi_ctl0; - int alt_rssi = rs->rs_rssi_ctl1; + int main_rssi = rs->rs_rssi_ctl[0]; + int alt_rssi = rs->rs_rssi_ctl[1]; int rx_ant_conf, main_ant_conf; bool short_scan = false, ret; - rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) & + rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) & ATH_ANT_RX_MASK; - main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) & + main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) & ATH_ANT_RX_MASK; if (alt_rssi >= antcomb->low_rssi_thresh) { diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c index 5c95fd9e9c9e..149aba3c7298 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c @@ -32,12 +32,8 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah) return 0; } - if (ah->config.pcie_clock_req) - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9280PciePhy_clkreq_off_L1_9280); - else - INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9280PciePhy_clkreq_always_on_L1_9280); + INIT_INI_ARRAY(&ah->iniPcieSerdes, + ar9280PciePhy_clkreq_always_on_L1_9280); if (AR_SREV_9287_11_OR_LATER(ah)) { INIT_INI_ARRAY(&ah->iniModes, ar9287Modes_9287_1_1); diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c index 8d78253c26ce..857ede3a999c 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c @@ -29,7 +29,8 @@ static void ar9002_hw_set_desc_link(void *ds, u32 ds_link) ((struct ath_desc*) ds)->ds_link = ds_link; } -static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) +static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked, + u32 *sync_cause_p) { u32 isr = 0; u32 mask2 = 0; @@ -136,7 +137,8 @@ static bool ar9002_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) } if (sync_cause) { - ath9k_debug_sync_cause(common, sync_cause); + if (sync_cause_p) + *sync_cause_p = sync_cause; fatal_int = (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_phy.c b/drivers/net/wireless/ath/ath9k/ar9002_phy.c index f087117b2e6b..9a2afa2c690b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c @@ -201,7 +201,6 @@ static void ar9002_hw_spur_mitigate(struct ath_hw *ah, ath9k_hw_get_channel_centers(ah, chan, ¢ers); freq = centers.synth_center; - ah->config.spurmode = SPUR_ENABLE_EEPROM; for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { cur_bb_spur = ah->eep_ops->get_spur_channel(ah, i, is2GHz); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index ec317d6937d6..c8d22eccfef8 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -131,6 +131,7 @@ static const struct ar9300_eeprom ar9300_default = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -138,7 +139,7 @@ static const struct ar9300_eeprom ar9300_default = { }, .base_ext1 = { .ant_div_control = 0, - .future = {0, 0, 0}, + .future = {0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { @@ -333,6 +334,7 @@ static const struct ar9300_eeprom ar9300_default = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -707,6 +709,7 @@ static const struct ar9300_eeprom ar9300_x113 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -714,7 +717,7 @@ static const struct ar9300_eeprom ar9300_x113 = { }, .base_ext1 = { .ant_div_control = 0, - .future = {0, 0, 0}, + .future = {0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { @@ -909,6 +912,7 @@ static const struct ar9300_eeprom ar9300_x113 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -1284,6 +1288,7 @@ static const struct ar9300_eeprom ar9300_h112 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -1291,7 +1296,7 @@ static const struct ar9300_eeprom ar9300_h112 = { }, .base_ext1 = { .ant_div_control = 0, - .future = {0, 0, 0}, + .future = {0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { @@ -1486,6 +1491,7 @@ static const struct ar9300_eeprom ar9300_h112 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -1861,6 +1867,7 @@ static const struct ar9300_eeprom ar9300_x112 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80c080), .papdRateMaskHt40 = LE32(0x0080c080), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -1868,7 +1875,7 @@ static const struct ar9300_eeprom ar9300_x112 = { }, .base_ext1 = { .ant_div_control = 0, - .future = {0, 0, 0}, + .future = {0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { @@ -2063,6 +2070,7 @@ static const struct ar9300_eeprom ar9300_x112 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -2437,6 +2445,7 @@ static const struct ar9300_eeprom ar9300_h116 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0c80C080), .papdRateMaskHt40 = LE32(0x0080C080), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -2444,7 +2453,7 @@ static const struct ar9300_eeprom ar9300_h116 = { }, .base_ext1 = { .ant_div_control = 0, - .future = {0, 0, 0}, + .future = {0, 0}, .tempslopextension = {0, 0, 0, 0, 0, 0, 0, 0} }, .calFreqPier2G = { @@ -2639,6 +2648,7 @@ static const struct ar9300_eeprom ar9300_h116 = { .thresh62 = 28, .papdRateMaskHt20 = LE32(0x0cf0e0e0), .papdRateMaskHt40 = LE32(0x6cf0e0e0), + .switchcomspdt = 0, .xlna_bias_strength = 0, .futureModal = { 0, 0, 0, 0, 0, 0, 0, @@ -4111,6 +4121,37 @@ static void ar9003_hw_thermo_cal_apply(struct ath_hw *ah) } } +static void ar9003_hw_apply_minccapwr_thresh(struct ath_hw *ah, + bool is2ghz) +{ + struct ar9300_eeprom *eep = &ah->eeprom.ar9300_eep; + const u_int32_t cca_ctrl[AR9300_MAX_CHAINS] = { + AR_PHY_CCA_CTRL_0, + AR_PHY_CCA_CTRL_1, + AR_PHY_CCA_CTRL_2, + }; + int chain; + u32 val; + + if (is2ghz) { + if (!(eep->base_ext1.misc_enable & BIT(2))) + return; + } else { + if (!(eep->base_ext1.misc_enable & BIT(3))) + return; + } + + for (chain = 0; chain < AR9300_MAX_CHAINS; chain++) { + if (!(ah->caps.tx_chainmask & BIT(chain))) + continue; + + val = ar9003_modal_header(ah, is2ghz)->noiseFloorThreshCh[chain]; + REG_RMW_FIELD(ah, cca_ctrl[chain], + AR_PHY_EXT_CCA0_THRESH62_1, val); + } + +} + static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -4125,6 +4166,7 @@ static void ath9k_hw_ar9300_set_board_values(struct ath_hw *ah, if (!AR_SREV_9330(ah) && !AR_SREV_9340(ah)) ar9003_hw_internal_regulator_apply(ah); ar9003_hw_apply_tuning_caps(ah); + ar9003_hw_apply_minccapwr_thresh(ah, chan); ar9003_hw_txend_to_xpa_off_apply(ah, is2ghz); ar9003_hw_thermometer_apply(ah); ar9003_hw_thermo_cal_apply(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h index 0e5daa58a4fc..694ca2e680e5 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.h @@ -270,10 +270,20 @@ struct cal_ctl_data_5g { u8 ctlEdges[AR9300_NUM_BAND_EDGES_5G]; } __packed; +#define MAX_BASE_EXTENSION_FUTURE 2 + struct ar9300_BaseExtension_1 { u8 ant_div_control; - u8 future[3]; - u8 tempslopextension[8]; + u8 future[MAX_BASE_EXTENSION_FUTURE]; + /* + * misc_enable: + * + * BIT 0 - TX Gain Cap enable. + * BIT 1 - Uncompressed Checksum enable. + * BIT 2/3 - MinCCApwr enable 2g/5g. + */ + u8 misc_enable; + int8_t tempslopextension[8]; int8_t quick_drop_low; int8_t quick_drop_high; } __packed; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index f6c5c1b50471..729ffbf07343 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -175,7 +175,8 @@ static void ar9003_hw_set_desc_link(void *ds, u32 ds_link) ads->ctl10 |= ar9003_calc_ptr_chksum(ads); } -static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) +static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked, + u32 *sync_cause_p) { u32 isr = 0; u32 mask2 = 0; @@ -310,7 +311,8 @@ static bool ar9003_hw_get_isr(struct ath_hw *ah, enum ath9k_int *masked) ar9003_mci_get_isr(ah, masked); if (sync_cause) { - ath9k_debug_sync_cause(common, sync_cause); + if (sync_cause_p) + *sync_cause_p = sync_cause; fatal_int = (sync_cause & (AR_INTR_SYNC_HOST1_FATAL | AR_INTR_SYNC_HOST1_PERR)) @@ -476,12 +478,12 @@ int ath9k_hw_process_rxdesc_edma(struct ath_hw *ah, struct ath_rx_status *rxs, /* XXX: Keycache */ rxs->rs_rssi = MS(rxsp->status5, AR_RxRSSICombined); - rxs->rs_rssi_ctl0 = MS(rxsp->status1, AR_RxRSSIAnt00); - rxs->rs_rssi_ctl1 = MS(rxsp->status1, AR_RxRSSIAnt01); - rxs->rs_rssi_ctl2 = MS(rxsp->status1, AR_RxRSSIAnt02); - rxs->rs_rssi_ext0 = MS(rxsp->status5, AR_RxRSSIAnt10); - rxs->rs_rssi_ext1 = MS(rxsp->status5, AR_RxRSSIAnt11); - rxs->rs_rssi_ext2 = MS(rxsp->status5, AR_RxRSSIAnt12); + rxs->rs_rssi_ctl[0] = MS(rxsp->status1, AR_RxRSSIAnt00); + rxs->rs_rssi_ctl[1] = MS(rxsp->status1, AR_RxRSSIAnt01); + rxs->rs_rssi_ctl[2] = MS(rxsp->status1, AR_RxRSSIAnt02); + rxs->rs_rssi_ext[0] = MS(rxsp->status5, AR_RxRSSIAnt10); + rxs->rs_rssi_ext[1] = MS(rxsp->status5, AR_RxRSSIAnt11); + rxs->rs_rssi_ext[2] = MS(rxsp->status5, AR_RxRSSIAnt12); if (rxsp->status11 & AR_RxKeyIdxValid) rxs->rs_keyix = MS(rxsp->status11, AR_KeyIdx); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index bbbfc4dc59eb..1b441715ba39 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -270,7 +270,7 @@ #define AR_PHY_AGC (AR_AGC_BASE + 0x14) #define AR_PHY_EXT_ATTEN_CTL_0 (AR_AGC_BASE + 0x18) #define AR_PHY_CCA_0 (AR_AGC_BASE + 0x1c) -#define AR_PHY_EXT_CCA0 (AR_AGC_BASE + 0x20) +#define AR_PHY_CCA_CTRL_0 (AR_AGC_BASE + 0x20) #define AR_PHY_RESTART (AR_AGC_BASE + 0x24) /* @@ -398,6 +398,8 @@ #define AR9280_PHY_CCA_THRESH62_S 12 #define AR_PHY_EXT_CCA0_THRESH62 0x000000FF #define AR_PHY_EXT_CCA0_THRESH62_S 0 +#define AR_PHY_EXT_CCA0_THRESH62_1 0x000001FF +#define AR_PHY_EXT_CCA0_THRESH62_1_S 0 #define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK 0x0000003F #define AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK_S 0 #define AR_PHY_CCK_DETECT_ANT_SWITCH_TIME 0x00001FC0 diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 5e5d5cb2458c..f2202e78fa7b 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -27,40 +27,15 @@ #include "common.h" #include "mci.h" #include "dfs.h" - -/* - * Header for the ath9k.ko driver core *only* -- hw code nor any other driver - * should rely on this file or its contents. - */ +#include "spectral.h" struct ath_node; +struct ath_rate_table; -/* Macro to expand scalars to 64-bit objects */ - -#define ito64(x) (sizeof(x) == 1) ? \ - (((unsigned long long int)(x)) & (0xff)) : \ - (sizeof(x) == 2) ? \ - (((unsigned long long int)(x)) & 0xffff) : \ - ((sizeof(x) == 4) ? \ - (((unsigned long long int)(x)) & 0xffffffff) : \ - (unsigned long long int)(x)) - -/* increment with wrap-around */ -#define INCR(_l, _sz) do { \ - (_l)++; \ - (_l) &= ((_sz) - 1); \ - } while (0) - -/* decrement with wrap-around */ -#define DECR(_l, _sz) do { \ - (_l)--; \ - (_l) &= ((_sz) - 1); \ - } while (0) - -#define TSF_TO_TU(_h,_l) \ - ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) - -#define ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<<i)) +extern struct ieee80211_ops ath9k_ops; +extern int ath9k_modparam_nohwcrypt; +extern int led_blink; +extern bool is_ath9k_unloaded; struct ath_config { u16 txpowlimit; @@ -70,6 +45,17 @@ struct ath_config { /* Descriptor Management */ /*************************/ +#define ATH_TXSTATUS_RING_SIZE 512 + +/* Macro to expand scalars to 64-bit objects */ +#define ito64(x) (sizeof(x) == 1) ? \ + (((unsigned long long int)(x)) & (0xff)) : \ + (sizeof(x) == 2) ? \ + (((unsigned long long int)(x)) & 0xffff) : \ + ((sizeof(x) == 4) ? \ + (((unsigned long long int)(x)) & 0xffffffff) : \ + (unsigned long long int)(x)) + #define ATH_TXBUF_RESET(_bf) do { \ (_bf)->bf_lastbf = NULL; \ (_bf)->bf_next = NULL; \ @@ -77,23 +63,6 @@ struct ath_config { sizeof(struct ath_buf_state)); \ } while (0) -/** - * enum buffer_type - Buffer type flags - * - * @BUF_AMPDU: This buffer is an ampdu, as part of an aggregate (during TX) - * @BUF_AGGR: Indicates whether the buffer can be aggregated - * (used in aggregation scheduling) - */ -enum buffer_type { - BUF_AMPDU = BIT(0), - BUF_AGGR = BIT(1), -}; - -#define bf_isampdu(bf) (bf->bf_state.bf_type & BUF_AMPDU) -#define bf_isaggr(bf) (bf->bf_state.bf_type & BUF_AGGR) - -#define ATH_TXSTATUS_RING_SIZE 512 - #define DS2PHYS(_dd, _ds) \ ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) #define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) @@ -113,11 +82,20 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, /* RX / TX */ /***********/ +#define ATH_TXQ_SETUP(sc, i) ((sc)->tx.txqsetup & (1<<i)) + +/* increment with wrap-around */ +#define INCR(_l, _sz) do { \ + (_l)++; \ + (_l) &= ((_sz) - 1); \ + } while (0) + #define ATH_RXBUF 512 #define ATH_TXBUF 512 #define ATH_TXBUF_RESERVE 5 #define ATH_MAX_QDEPTH (ATH_TXBUF / 4 - ATH_TXBUF_RESERVE) #define ATH_TXMAXTRY 13 +#define ATH_MAX_SW_RETRIES 30 #define TID_TO_WME_AC(_tid) \ ((((_tid) == 0) || ((_tid) == 3)) ? IEEE80211_AC_BE : \ @@ -133,6 +111,9 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_AGGR_MIN_QDEPTH 2 /* minimum h/w qdepth for non-aggregated traffic */ #define ATH_NON_AGGR_MIN_QDEPTH 8 +#define ATH_TX_COMPLETE_POLL_INT 1000 +#define ATH_TXFIFO_DEPTH 8 +#define ATH_TX_ERROR 0x01 #define IEEE80211_SEQ_SEQ_SHIFT 4 #define IEEE80211_SEQ_MAX 4096 @@ -167,9 +148,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, #define IS_CCK_RATE(rate) ((rate >= 0x18) && (rate <= 0x1e)) -#define ATH_TX_COMPLETE_POLL_INT 1000 - -#define ATH_TXFIFO_DEPTH 8 struct ath_txq { int mac80211_qnum; /* mac80211 queue number, -1 means not mac80211 Q */ u32 axq_qnum; /* ath9k hardware queue number */ @@ -214,6 +192,21 @@ struct ath_rxbuf { dma_addr_t bf_buf_addr; }; +/** + * enum buffer_type - Buffer type flags + * + * @BUF_AMPDU: This buffer is an ampdu, as part of an aggregate (during TX) + * @BUF_AGGR: Indicates whether the buffer can be aggregated + * (used in aggregation scheduling) + */ +enum buffer_type { + BUF_AMPDU = BIT(0), + BUF_AGGR = BIT(1), +}; + +#define bf_isampdu(bf) (bf->bf_state.bf_type & BUF_AMPDU) +#define bf_isaggr(bf) (bf->bf_state.bf_type & BUF_AGGR) + struct ath_buf_state { u8 bf_type; u8 bfs_paprd; @@ -278,7 +271,6 @@ struct ath_tx_control { struct ieee80211_sta *sta; }; -#define ATH_TX_ERROR 0x01 /** * @txq_map: Index is mac80211 queue number. This is @@ -372,6 +364,22 @@ struct ath_vif { struct ath_buf *av_bcbuf; }; +struct ath9k_vif_iter_data { + u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */ + u8 mask[ETH_ALEN]; /* bssid mask */ + bool has_hw_macaddr; + + int naps; /* number of AP vifs */ + int nmeshes; /* number of mesh vifs */ + int nstations; /* number of station vifs */ + int nwds; /* number of WDS vifs */ + int nadhocs; /* number of adhoc vifs */ +}; + +void ath9k_calculate_iter_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ath9k_vif_iter_data *iter_data); + /*******************/ /* Beacon Handling */ /*******************/ @@ -387,6 +395,9 @@ struct ath_vif { #define ATH_DEFAULT_BMISS_LIMIT 10 #define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) +#define TSF_TO_TU(_h,_l) \ + ((((u32)(_h)) << 22) | (((u32)(_l)) >> 10)) + struct ath_beacon_config { int beacon_interval; u16 listen_interval; @@ -420,12 +431,10 @@ struct ath_beacon { }; void ath9k_beacon_tasklet(unsigned long data); -bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, u32 changed); void ath9k_beacon_assign_slot(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif); -void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif); void ath9k_set_beacon(struct ath_softc *sc); bool ath9k_csa_is_finished(struct ath_softc *sc); @@ -440,10 +449,9 @@ bool ath9k_csa_is_finished(struct ath_softc *sc); #define ATH_LONG_CALINTERVAL_INT 1000 /* 1000 ms */ #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ -#define ATH_ANI_MAX_SKIP_COUNT 10 - -#define ATH_PAPRD_TIMEOUT 100 /* msecs */ -#define ATH_PLL_WORK_INTERVAL 100 +#define ATH_ANI_MAX_SKIP_COUNT 10 +#define ATH_PAPRD_TIMEOUT 100 /* msecs */ +#define ATH_PLL_WORK_INTERVAL 100 void ath_tx_complete_poll_work(struct work_struct *work); void ath_reset_work(struct work_struct *work); @@ -477,20 +485,19 @@ enum bt_op_flags { }; struct ath_btcoex { - bool hw_timer_enabled; spinlock_t btcoex_lock; struct timer_list period_timer; /* Timer for BT period */ + struct timer_list no_stomp_timer; u32 bt_priority_cnt; unsigned long bt_priority_time; unsigned long op_flags; int bt_stomp_type; /* Types of BT stomping */ - u32 btcoex_no_stomp; /* in usec */ + u32 btcoex_no_stomp; /* in msec */ u32 btcoex_period; /* in msec */ - u32 btscan_no_stomp; /* in usec */ + u32 btscan_no_stomp; /* in msec */ u32 duty_cycle; u32 bt_wait_time; int rssi_count; - struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ struct ath_mci_profile mci; u8 stomp_audio; }; @@ -538,12 +545,6 @@ static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size) } #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ -struct ath9k_wow_pattern { - u8 pattern_bytes[MAX_PATTERN_SIZE]; - u8 mask_bytes[MAX_PATTERN_SIZE]; - u32 pattern_len; -}; - /********************/ /* LED Control */ /********************/ @@ -575,6 +576,12 @@ static inline void ath_fill_led_pin(struct ath_softc *sc) /* Wake on Wireless LAN */ /************************/ +struct ath9k_wow_pattern { + u8 pattern_bytes[MAX_PATTERN_SIZE]; + u8 mask_bytes[MAX_PATTERN_SIZE]; + u32 pattern_len; +}; + #ifdef CONFIG_ATH9K_WOW void ath9k_init_wow(struct ieee80211_hw *hw); int ath9k_suspend(struct ieee80211_hw *hw, @@ -678,13 +685,8 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs); * Used when PCI device not fully initialized by bootrom/BIOS */ #define DEFAULT_CACHELINE 32 -#define ATH_REGCLASSIDS_MAX 10 #define ATH_CABQ_READY_TIME 80 /* % of beacon interval */ -#define ATH_MAX_SW_RETRIES 30 -#define ATH_CHAN_MAX 255 - #define ATH_TXPOWER_MAX 100 /* .5 dBm units */ -#define ATH_RATE_DUMMY_MARKER 0 enum sc_op_flags { SC_OP_INVALID, @@ -703,37 +705,6 @@ enum sc_op_flags { #define PS_BEACON_SYNC BIT(4) #define PS_WAIT_FOR_ANI BIT(5) -struct ath_rate_table; - -struct ath9k_vif_iter_data { - u8 hw_macaddr[ETH_ALEN]; /* address of the first vif */ - u8 mask[ETH_ALEN]; /* bssid mask */ - bool has_hw_macaddr; - - int naps; /* number of AP vifs */ - int nmeshes; /* number of mesh vifs */ - int nstations; /* number of station vifs */ - int nwds; /* number of WDS vifs */ - int nadhocs; /* number of adhoc vifs */ -}; - -/* enum spectral_mode: - * - * @SPECTRAL_DISABLED: spectral mode is disabled - * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with - * something else. - * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples - * is performed manually. - * @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels - * during a channel scan. - */ -enum spectral_mode { - SPECTRAL_DISABLED = 0, - SPECTRAL_BACKGROUND, - SPECTRAL_MANUAL, - SPECTRAL_CHANSCAN, -}; - struct ath_softc { struct ieee80211_hw *hw; struct device *dev; @@ -823,162 +794,6 @@ struct ath_softc { #endif }; -#define SPECTRAL_SCAN_BITMASK 0x10 -/* Radar info packet format, used for DFS and spectral formats. */ -struct ath_radar_info { - u8 pulse_length_pri; - u8 pulse_length_ext; - u8 pulse_bw_info; -} __packed; - -/* The HT20 spectral data has 4 bytes of additional information at it's end. - * - * [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]} - * [7:0]: all bins max_magnitude[9:2] - * [7:0]: all bins {max_index[5:0], max_magnitude[11:10]} - * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned) - */ -struct ath_ht20_mag_info { - u8 all_bins[3]; - u8 max_exp; -} __packed; - -#define SPECTRAL_HT20_NUM_BINS 56 - -/* WARNING: don't actually use this struct! MAC may vary the amount of - * data by -1/+2. This struct is for reference only. - */ -struct ath_ht20_fft_packet { - u8 data[SPECTRAL_HT20_NUM_BINS]; - struct ath_ht20_mag_info mag_info; - struct ath_radar_info radar_info; -} __packed; - -#define SPECTRAL_HT20_TOTAL_DATA_LEN (sizeof(struct ath_ht20_fft_packet)) - -/* Dynamic 20/40 mode: - * - * [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]} - * [7:0]: lower bins max_magnitude[9:2] - * [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]} - * [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]} - * [7:0]: upper bins max_magnitude[9:2] - * [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]} - * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned) - */ -struct ath_ht20_40_mag_info { - u8 lower_bins[3]; - u8 upper_bins[3]; - u8 max_exp; -} __packed; - -#define SPECTRAL_HT20_40_NUM_BINS 128 - -/* WARNING: don't actually use this struct! MAC may vary the amount of - * data. This struct is for reference only. - */ -struct ath_ht20_40_fft_packet { - u8 data[SPECTRAL_HT20_40_NUM_BINS]; - struct ath_ht20_40_mag_info mag_info; - struct ath_radar_info radar_info; -} __packed; - - -#define SPECTRAL_HT20_40_TOTAL_DATA_LEN (sizeof(struct ath_ht20_40_fft_packet)) - -/* grabs the max magnitude from the all/upper/lower bins */ -static inline u16 spectral_max_magnitude(u8 *bins) -{ - return (bins[0] & 0xc0) >> 6 | - (bins[1] & 0xff) << 2 | - (bins[2] & 0x03) << 10; -} - -/* return the max magnitude from the all/upper/lower bins */ -static inline u8 spectral_max_index(u8 *bins) -{ - s8 m = (bins[2] & 0xfc) >> 2; - - /* TODO: this still doesn't always report the right values ... */ - if (m > 32) - m |= 0xe0; - else - m &= ~0xe0; - - return m + 29; -} - -/* return the bitmap weight from the all/upper/lower bins */ -static inline u8 spectral_bitmap_weight(u8 *bins) -{ - return bins[0] & 0x3f; -} - -/* FFT sample format given to userspace via debugfs. - * - * Please keep the type/length at the front position and change - * other fields after adding another sample type - * - * TODO: this might need rework when switching to nl80211-based - * interface. - */ -enum ath_fft_sample_type { - ATH_FFT_SAMPLE_HT20 = 1, - ATH_FFT_SAMPLE_HT20_40, -}; - -struct fft_sample_tlv { - u8 type; /* see ath_fft_sample */ - __be16 length; - /* type dependent data follows */ -} __packed; - -struct fft_sample_ht20 { - struct fft_sample_tlv tlv; - - u8 max_exp; - - __be16 freq; - s8 rssi; - s8 noise; - - __be16 max_magnitude; - u8 max_index; - u8 bitmap_weight; - - __be64 tsf; - - u8 data[SPECTRAL_HT20_NUM_BINS]; -} __packed; - -struct fft_sample_ht20_40 { - struct fft_sample_tlv tlv; - - u8 channel_type; - __be16 freq; - - s8 lower_rssi; - s8 upper_rssi; - - __be64 tsf; - - s8 lower_noise; - s8 upper_noise; - - __be16 lower_max_magnitude; - __be16 upper_max_magnitude; - - u8 lower_max_index; - u8 upper_max_index; - - u8 lower_bitmap_weight; - u8 upper_bitmap_weight; - - u8 max_exp; - - u8 data[SPECTRAL_HT20_40_NUM_BINS]; -} __packed; - /********/ /* TX99 */ /********/ @@ -999,19 +814,13 @@ static inline int ath9k_tx99_send(struct ath_softc *sc, } #endif /* CONFIG_ATH9K_TX99 */ -void ath9k_tasklet(unsigned long data); -int ath_cabq_update(struct ath_softc *); - static inline void ath_read_cachesize(struct ath_common *common, int *csz) { common->bus_ops->read_cachesize(common, csz); } -extern struct ieee80211_ops ath9k_ops; -extern int ath9k_modparam_nohwcrypt; -extern int led_blink; -extern bool is_ath9k_unloaded; - +void ath9k_tasklet(unsigned long data); +int ath_cabq_update(struct ath_softc *); u8 ath9k_parse_mpdudensity(u8 mpdudensity); irqreturn_t ath_isr(int irq, void *dev); int ath_reset(struct ath_softc *sc); @@ -1020,13 +829,12 @@ void ath_restart_work(struct ath_softc *sc); int ath9k_init_device(u16 devid, struct ath_softc *sc, const struct ath_bus_ops *bus_ops); void ath9k_deinit_device(struct ath_softc *sc); -void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); void ath9k_reload_chainmask_settings(struct ath_softc *sc); - -void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw); -int ath9k_spectral_scan_config(struct ieee80211_hw *hw, - enum spectral_mode spectral_mode); - +u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate); +void ath_start_rfkill_poll(struct ath_softc *sc); +void ath9k_rfkill_poll_state(struct ieee80211_hw *hw); +void ath9k_ps_wakeup(struct ath_softc *sc); +void ath9k_ps_restore(struct ath_softc *sc); #ifdef CONFIG_ATH9K_PCI int ath_pci_init(void); @@ -1044,15 +852,4 @@ static inline int ath_ahb_init(void) { return 0; }; static inline void ath_ahb_exit(void) {}; #endif -void ath9k_ps_wakeup(struct ath_softc *sc); -void ath9k_ps_restore(struct ath_softc *sc); - -u8 ath_txchainmask_reduction(struct ath_softc *sc, u8 chainmask, u32 rate); - -void ath_start_rfkill_poll(struct ath_softc *sc); -void ath9k_rfkill_poll_state(struct ieee80211_hw *hw); -void ath9k_calculate_iter_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ath9k_vif_iter_data *iter_data); - #endif /* ATH9K_H */ diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 17be35392bb4..112aff720e13 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -274,18 +274,19 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc) return slot; } -void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) +static void ath9k_set_tsfadjust(struct ath_softc *sc, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf; struct ath_vif *avp = (void *)vif->drv_priv; - u64 tsfadjust; + u32 tsfadjust; if (avp->av_bslot == 0) return; - tsfadjust = cur_conf->beacon_interval * avp->av_bslot / ATH_BCBUF; - avp->tsf_adjust = cpu_to_le64(TU_TO_USEC(tsfadjust)); + tsfadjust = cur_conf->beacon_interval * avp->av_bslot; + tsfadjust = TU_TO_USEC(tsfadjust) / ATH_BCBUF; + avp->tsf_adjust = cpu_to_le64(tsfadjust); ath_dbg(common, CONFIG, "tsfadjust is: %llu for bslot: %d\n", (unsigned long long)tsfadjust, avp->av_bslot); @@ -431,6 +432,33 @@ static void ath9k_beacon_init(struct ath_softc *sc, u32 nexttbtt, ath9k_hw_enable_interrupts(ah); } +/* Calculate the modulo of a 64 bit TSF snapshot with a TU divisor */ +static u32 ath9k_mod_tsf64_tu(u64 tsf, u32 div_tu) +{ + u32 tsf_mod, tsf_hi, tsf_lo, mod_hi, mod_lo; + + tsf_mod = tsf & (BIT(10) - 1); + tsf_hi = tsf >> 32; + tsf_lo = ((u32) tsf) >> 10; + + mod_hi = tsf_hi % div_tu; + mod_lo = ((mod_hi << 22) + tsf_lo) % div_tu; + + return (mod_lo << 10) | tsf_mod; +} + +static u32 ath9k_get_next_tbtt(struct ath_softc *sc, u64 tsf, + unsigned int interval) +{ + struct ath_hw *ah = sc->sc_ah; + unsigned int offset; + + tsf += TU_TO_USEC(FUDGE + ah->config.sw_beacon_response_time); + offset = ath9k_mod_tsf64_tu(tsf, interval); + + return (u32) tsf + TU_TO_USEC(interval) - offset; +} + /* * For multi-bss ap support beacons are either staggered evenly over N slots or * burst together. For the former arrange for the SWBA to be delivered for each @@ -446,7 +474,8 @@ static void ath9k_beacon_config_ap(struct ath_softc *sc, /* NB: the beacon interval is kept internally in TU's */ intval = TU_TO_USEC(conf->beacon_interval); intval /= ATH_BCBUF; - nexttbtt = intval; + nexttbtt = ath9k_get_next_tbtt(sc, ath9k_hw_gettsf64(ah), + conf->beacon_interval); if (conf->enable_beacon) ah->imask |= ATH9K_INT_SWBA; @@ -458,7 +487,7 @@ static void ath9k_beacon_config_ap(struct ath_softc *sc, (conf->enable_beacon) ? "Enable" : "Disable", nexttbtt, intval, conf->beacon_interval); - ath9k_beacon_init(sc, nexttbtt, intval, true); + ath9k_beacon_init(sc, nexttbtt, intval, false); } /* @@ -475,11 +504,9 @@ static void ath9k_beacon_config_sta(struct ath_softc *sc, struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath9k_beacon_state bs; - int dtimperiod, dtimcount, sleepduration; - int cfpperiod, cfpcount; - u32 nexttbtt = 0, intval, tsftu; + int dtim_intval, sleepduration; + u32 nexttbtt = 0, intval; u64 tsf; - int num_beacons, offset, dtim_dec_count, cfp_dec_count; /* No need to configure beacon if we are not associated */ if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { @@ -492,53 +519,25 @@ static void ath9k_beacon_config_sta(struct ath_softc *sc, intval = conf->beacon_interval; /* - * Setup dtim and cfp parameters according to + * Setup dtim parameters according to * last beacon we received (which may be none). */ - dtimperiod = conf->dtim_period; - dtimcount = conf->dtim_count; - if (dtimcount >= dtimperiod) /* NB: sanity check */ - dtimcount = 0; - cfpperiod = 1; /* NB: no PCF support yet */ - cfpcount = 0; - + dtim_intval = intval * conf->dtim_period; sleepduration = conf->listen_interval * intval; /* * Pull nexttbtt forward to reflect the current - * TSF and calculate dtim+cfp state for the result. + * TSF and calculate dtim state for the result. */ tsf = ath9k_hw_gettsf64(ah); - tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; - - num_beacons = tsftu / intval + 1; - offset = tsftu % intval; - nexttbtt = tsftu - offset; - if (offset) - nexttbtt += intval; - - /* DTIM Beacon every dtimperiod Beacon */ - dtim_dec_count = num_beacons % dtimperiod; - /* CFP every cfpperiod DTIM Beacon */ - cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; - if (dtim_dec_count) - cfp_dec_count++; - - dtimcount -= dtim_dec_count; - if (dtimcount < 0) - dtimcount += dtimperiod; - - cfpcount -= cfp_dec_count; - if (cfpcount < 0) - cfpcount += cfpperiod; - - bs.bs_intval = intval; + nexttbtt = ath9k_get_next_tbtt(sc, tsf, intval); + + bs.bs_intval = TU_TO_USEC(intval); + bs.bs_dtimperiod = conf->dtim_period * bs.bs_intval; bs.bs_nexttbtt = nexttbtt; - bs.bs_dtimperiod = dtimperiod*intval; - bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; - bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; - bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; - bs.bs_cfpmaxduration = 0; + bs.bs_nextdtim = nexttbtt; + if (conf->dtim_period > 1) + bs.bs_nextdtim = ath9k_get_next_tbtt(sc, tsf, dtim_intval); /* * Calculate the number of consecutive beacons to miss* before taking @@ -566,18 +565,16 @@ static void ath9k_beacon_config_sta(struct ath_softc *sc, * XXX fixed at 100ms */ - bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); + bs.bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100), + sleepduration)); if (bs.bs_sleepduration > bs.bs_dtimperiod) bs.bs_sleepduration = bs.bs_dtimperiod; /* TSF out of range threshold fixed at 1 second */ bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; - ath_dbg(common, BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); - ath_dbg(common, BEACON, - "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", - bs.bs_bmissthreshold, bs.bs_sleepduration, - bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); + ath_dbg(common, BEACON, "bmiss: %u sleep: %u\n", + bs.bs_bmissthreshold, bs.bs_sleepduration); /* Set the computed STA beacon timers */ @@ -600,25 +597,11 @@ static void ath9k_beacon_config_adhoc(struct ath_softc *sc, intval = TU_TO_USEC(conf->beacon_interval); - if (conf->ibss_creator) { + if (conf->ibss_creator) nexttbtt = intval; - } else { - u32 tbtt, offset, tsftu; - u64 tsf; - - /* - * Pull nexttbtt forward to reflect the current - * sync'd TSF. - */ - tsf = ath9k_hw_gettsf64(ah); - tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE; - offset = tsftu % conf->beacon_interval; - tbtt = tsftu - offset; - if (offset) - tbtt += conf->beacon_interval; - - nexttbtt = TU_TO_USEC(tbtt); - } + else + nexttbtt = ath9k_get_next_tbtt(sc, ath9k_hw_gettsf64(ah), + conf->beacon_interval); if (conf->enable_beacon) ah->imask |= ATH9K_INT_SWBA; @@ -640,7 +623,8 @@ static void ath9k_beacon_config_adhoc(struct ath_softc *sc, set_bit(SC_OP_BEACONS, &sc->sc_flags); } -bool ath9k_allow_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif) +static bool ath9k_allow_beacon_config(struct ath_softc *sc, + struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath_vif *avp = (void *)vif->drv_priv; @@ -711,12 +695,17 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif, unsigned long flags; bool skip_beacon = false; + if (vif->type == NL80211_IFTYPE_AP) + ath9k_set_tsfadjust(sc, vif); + + if (!ath9k_allow_beacon_config(sc, vif)) + return; + if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) { ath9k_cache_beacon_config(sc, bss_conf); ath9k_set_beacon(sc); set_bit(SC_OP_BEACONS, &sc->sc_flags); return; - } /* diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index 9963b0bf9f72..3dfc2c7f1f07 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -66,7 +66,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) .bt_first_slot_time = 5, .bt_hold_rx_clear = true, }; - u32 i, idx; bool rxclear_polarity = ath_bt_config.bt_rxclear_polarity; if (AR_SREV_9300_20_OR_LATER(ah)) @@ -88,11 +87,6 @@ void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) SM(ath_bt_config.bt_hold_rx_clear, AR_BT_HOLD_RX_CLEAR) | SM(ATH_BTCOEX_BMISS_THRESH, AR_BT_BCN_MISS_THRESH) | AR_BT_DISABLE_BT_ANT; - - for (i = 0; i < 32; i++) { - idx = (debruijn32 << i) >> 27; - ah->hw_gen_timers.gen_timer_index[idx] = i; - } } EXPORT_SYMBOL(ath9k_hw_init_btcoex_hw); diff --git a/drivers/net/wireless/ath/ath9k/common.c b/drivers/net/wireless/ath/ath9k/common.c index a7e5a05b2eff..768c733cad31 100644 --- a/drivers/net/wireless/ath/ath9k/common.c +++ b/drivers/net/wireless/ath/ath9k/common.c @@ -98,10 +98,8 @@ struct ath9k_channel *ath9k_cmn_get_channel(struct ieee80211_hw *hw, { struct ieee80211_channel *curchan = chandef->chan; struct ath9k_channel *channel; - u8 chan_idx; - chan_idx = curchan->hw_value; - channel = &ah->channels[chan_idx]; + channel = &ah->channels[curchan->hw_value]; ath9k_cmn_update_ichannel(channel, chandef); return channel; diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 2f7dccfdb727..b041052a10ee 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -17,7 +17,6 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/export.h> -#include <linux/relay.h> #include <asm/unaligned.h> #include "ath9k.h" @@ -27,6 +26,47 @@ #define REG_READ_D(_ah, _reg) \ ath9k_hw_common(_ah)->ops->read((_ah), (_reg)) +void ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause) +{ + if (sync_cause) + sc->debug.stats.istats.sync_cause_all++; + if (sync_cause & AR_INTR_SYNC_RTC_IRQ) + sc->debug.stats.istats.sync_rtc_irq++; + if (sync_cause & AR_INTR_SYNC_MAC_IRQ) + sc->debug.stats.istats.sync_mac_irq++; + if (sync_cause & AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS) + sc->debug.stats.istats.eeprom_illegal_access++; + if (sync_cause & AR_INTR_SYNC_APB_TIMEOUT) + sc->debug.stats.istats.apb_timeout++; + if (sync_cause & AR_INTR_SYNC_PCI_MODE_CONFLICT) + sc->debug.stats.istats.pci_mode_conflict++; + if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) + sc->debug.stats.istats.host1_fatal++; + if (sync_cause & AR_INTR_SYNC_HOST1_PERR) + sc->debug.stats.istats.host1_perr++; + if (sync_cause & AR_INTR_SYNC_TRCV_FIFO_PERR) + sc->debug.stats.istats.trcv_fifo_perr++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_EP) + sc->debug.stats.istats.radm_cpl_ep++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_DLLP_ABORT) + sc->debug.stats.istats.radm_cpl_dllp_abort++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_TLP_ABORT) + sc->debug.stats.istats.radm_cpl_tlp_abort++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_ECRC_ERR) + sc->debug.stats.istats.radm_cpl_ecrc_err++; + if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) + sc->debug.stats.istats.radm_cpl_timeout++; + if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) + sc->debug.stats.istats.local_timeout++; + if (sync_cause & AR_INTR_SYNC_PM_ACCESS) + sc->debug.stats.istats.pm_access++; + if (sync_cause & AR_INTR_SYNC_MAC_AWAKE) + sc->debug.stats.istats.mac_awake++; + if (sync_cause & AR_INTR_SYNC_MAC_ASLEEP) + sc->debug.stats.istats.mac_asleep++; + if (sync_cause & AR_INTR_SYNC_MAC_SLEEP_ACCESS) + sc->debug.stats.istats.mac_sleep_access++; +} static ssize_t ath9k_debugfs_read_buf(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -1016,293 +1056,6 @@ static const struct file_operations fops_recv = { .llseek = default_llseek, }; -static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char *mode = ""; - unsigned int len; - - switch (sc->spectral_mode) { - case SPECTRAL_DISABLED: - mode = "disable"; - break; - case SPECTRAL_BACKGROUND: - mode = "background"; - break; - case SPECTRAL_CHANSCAN: - mode = "chanscan"; - break; - case SPECTRAL_MANUAL: - mode = "manual"; - break; - } - len = strlen(mode); - return simple_read_from_buffer(user_buf, count, ppos, mode, len); -} - -static ssize_t write_file_spec_scan_ctl(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - char buf[32]; - ssize_t len; - - if (config_enabled(CONFIG_ATH9K_TX99)) - return -EOPNOTSUPP; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - - if (strncmp("trigger", buf, 7) == 0) { - ath9k_spectral_scan_trigger(sc->hw); - } else if (strncmp("background", buf, 9) == 0) { - ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND); - ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n"); - } else if (strncmp("chanscan", buf, 8) == 0) { - ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN); - ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n"); - } else if (strncmp("manual", buf, 6) == 0) { - ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL); - ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n"); - } else if (strncmp("disable", buf, 7) == 0) { - ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED); - ath_dbg(common, CONFIG, "spectral scan: disabled\n"); - } else { - return -EINVAL; - } - - return count; -} - -static const struct file_operations fops_spec_scan_ctl = { - .read = read_file_spec_scan_ctl, - .write = write_file_spec_scan_ctl, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_spectral_short_repeat(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[32]; - unsigned int len; - - len = sprintf(buf, "%d\n", sc->spec_config.short_repeat); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t write_file_spectral_short_repeat(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - unsigned long val; - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtoul(buf, 0, &val)) - return -EINVAL; - - if (val < 0 || val > 1) - return -EINVAL; - - sc->spec_config.short_repeat = val; - return count; -} - -static const struct file_operations fops_spectral_short_repeat = { - .read = read_file_spectral_short_repeat, - .write = write_file_spectral_short_repeat, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_spectral_count(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[32]; - unsigned int len; - - len = sprintf(buf, "%d\n", sc->spec_config.count); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t write_file_spectral_count(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - unsigned long val; - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtoul(buf, 0, &val)) - return -EINVAL; - - if (val < 0 || val > 255) - return -EINVAL; - - sc->spec_config.count = val; - return count; -} - -static const struct file_operations fops_spectral_count = { - .read = read_file_spectral_count, - .write = write_file_spectral_count, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_spectral_period(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[32]; - unsigned int len; - - len = sprintf(buf, "%d\n", sc->spec_config.period); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t write_file_spectral_period(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - unsigned long val; - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtoul(buf, 0, &val)) - return -EINVAL; - - if (val < 0 || val > 255) - return -EINVAL; - - sc->spec_config.period = val; - return count; -} - -static const struct file_operations fops_spectral_period = { - .read = read_file_spectral_period, - .write = write_file_spectral_period, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static ssize_t read_file_spectral_fft_period(struct file *file, - char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - char buf[32]; - unsigned int len; - - len = sprintf(buf, "%d\n", sc->spec_config.fft_period); - return simple_read_from_buffer(user_buf, count, ppos, buf, len); -} - -static ssize_t write_file_spectral_fft_period(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ath_softc *sc = file->private_data; - unsigned long val; - char buf[32]; - ssize_t len; - - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - - buf[len] = '\0'; - if (kstrtoul(buf, 0, &val)) - return -EINVAL; - - if (val < 0 || val > 15) - return -EINVAL; - - sc->spec_config.fft_period = val; - return count; -} - -static const struct file_operations fops_spectral_fft_period = { - .read = read_file_spectral_fft_period, - .write = write_file_spectral_fft_period, - .open = simple_open, - .owner = THIS_MODULE, - .llseek = default_llseek, -}; - -static struct dentry *create_buf_file_handler(const char *filename, - struct dentry *parent, - umode_t mode, - struct rchan_buf *buf, - int *is_global) -{ - struct dentry *buf_file; - - buf_file = debugfs_create_file(filename, mode, parent, buf, - &relay_file_operations); - *is_global = 1; - return buf_file; -} - -static int remove_buf_file_handler(struct dentry *dentry) -{ - debugfs_remove(dentry); - - return 0; -} - -void ath_debug_send_fft_sample(struct ath_softc *sc, - struct fft_sample_tlv *fft_sample_tlv) -{ - int length; - if (!sc->rfs_chan_spec_scan) - return; - - length = __be16_to_cpu(fft_sample_tlv->length) + - sizeof(*fft_sample_tlv); - relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length); -} - -static struct rchan_callbacks rfs_spec_scan_cb = { - .create_buf_file = create_buf_file_handler, - .remove_buf_file = remove_buf_file_handler, -}; - - static ssize_t read_file_regidx(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1772,10 +1525,7 @@ void ath9k_get_et_stats(struct ieee80211_hw *hw, void ath9k_deinit_debug(struct ath_softc *sc) { - if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) { - relay_close(sc->rfs_chan_spec_scan); - sc->rfs_chan_spec_scan = NULL; - } + ath9k_spectral_deinit_debug(sc); } int ath9k_init_debug(struct ath_hw *ah) @@ -1795,6 +1545,7 @@ int ath9k_init_debug(struct ath_hw *ah) ath9k_dfs_init_debug(sc); ath9k_tx99_init_debug(sc); + ath9k_spectral_init_debug(sc); debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_dma); @@ -1841,23 +1592,6 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_base_eeprom); debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_modal_eeprom); - sc->rfs_chan_spec_scan = relay_open("spectral_scan", - sc->debug.debugfs_phy, - 1024, 256, &rfs_spec_scan_cb, - NULL); - debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, - &fops_spec_scan_ctl); - debugfs_create_file("spectral_short_repeat", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, - &fops_spectral_short_repeat); - debugfs_create_file("spectral_count", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, &fops_spectral_count); - debugfs_create_file("spectral_period", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, &fops_spectral_period); - debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR, - sc->debug.debugfs_phy, sc, - &fops_spectral_fft_period); debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index d6e3fa4299a4..ec02d38ea8ea 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -292,11 +292,11 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct dentry *dir); -void ath_debug_send_fft_sample(struct ath_softc *sc, - struct fft_sample_tlv *fft_sample); void ath9k_debug_stat_ant(struct ath_softc *sc, struct ath_hw_antcomb_conf *div_ant_conf, int main_rssi_avg, int alt_rssi_avg); +void ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause); + #else #define RX_STAT_INC(c) /* NOP */ @@ -331,6 +331,11 @@ static inline void ath9k_debug_stat_ant(struct ath_softc *sc, } +static inline void +ath9k_debug_sync_cause(struct ath_softc *sc, u32 sync_cause) +{ +} + #endif /* CONFIG_ATH9K_DEBUGFS */ #endif /* DEBUG_H */ diff --git a/drivers/net/wireless/ath/ath9k/dfs.c b/drivers/net/wireless/ath/ath9k/dfs.c index 7187d3671512..857bb28b3894 100644 --- a/drivers/net/wireless/ath/ath9k/dfs.c +++ b/drivers/net/wireless/ath/ath9k/dfs.c @@ -158,8 +158,8 @@ void ath9k_dfs_process_phyerr(struct ath_softc *sc, void *data, return; } - ard.rssi = rs->rs_rssi_ctl0; - ard.ext_rssi = rs->rs_rssi_ext0; + ard.rssi = rs->rs_rssi_ctl[0]; + ard.ext_rssi = rs->rs_rssi_ext[0]; /* * hardware stores this as 8 bit signed value. diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index b4091716e9b3..07b806c56c56 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -1085,31 +1085,7 @@ static void ath9k_hw_4k_set_board_values(struct ath_hw *ah, static u16 ath9k_hw_4k_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { -#define EEP_MAP4K_SPURCHAN \ - (ah->eeprom.map4k.modalHeader.spurChans[i].spurChan) - struct ath_common *common = ath9k_hw_common(ah); - - u16 spur_val = AR_NO_SPUR; - - ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", - i, is2GHz, ah->config.spurchans[i][is2GHz]); - - switch (ah->config.spurmode) { - case SPUR_DISABLE: - break; - case SPUR_ENABLE_IOCTL: - spur_val = ah->config.spurchans[i][is2GHz]; - ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", - spur_val); - break; - case SPUR_ENABLE_EEPROM: - spur_val = EEP_MAP4K_SPURCHAN; - break; - } - - return spur_val; - -#undef EEP_MAP4K_SPURCHAN + return ah->eeprom.map4k.modalHeader.spurChans[i].spurChan; } const struct eeprom_ops eep_4k_ops = { diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index e1d0c217c104..5ba1385c9838 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -1004,31 +1004,7 @@ static void ath9k_hw_ar9287_set_board_values(struct ath_hw *ah, static u16 ath9k_hw_ar9287_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { -#define EEP_MAP9287_SPURCHAN \ - (ah->eeprom.map9287.modalHeader.spurChans[i].spurChan) - - struct ath_common *common = ath9k_hw_common(ah); - u16 spur_val = AR_NO_SPUR; - - ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", - i, is2GHz, ah->config.spurchans[i][is2GHz]); - - switch (ah->config.spurmode) { - case SPUR_DISABLE: - break; - case SPUR_ENABLE_IOCTL: - spur_val = ah->config.spurchans[i][is2GHz]; - ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", - spur_val); - break; - case SPUR_ENABLE_EEPROM: - spur_val = EEP_MAP9287_SPURCHAN; - break; - } - - return spur_val; - -#undef EEP_MAP9287_SPURCHAN + return ah->eeprom.map9287.modalHeader.spurChans[i].spurChan; } const struct eeprom_ops eep_ar9287_ops = { diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 39107e31e79a..3218ca994746 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -1348,31 +1348,7 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, static u16 ath9k_hw_def_get_spur_channel(struct ath_hw *ah, u16 i, bool is2GHz) { -#define EEP_DEF_SPURCHAN \ - (ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan) - struct ath_common *common = ath9k_hw_common(ah); - - u16 spur_val = AR_NO_SPUR; - - ath_dbg(common, ANI, "Getting spur idx:%d is2Ghz:%d val:%x\n", - i, is2GHz, ah->config.spurchans[i][is2GHz]); - - switch (ah->config.spurmode) { - case SPUR_DISABLE: - break; - case SPUR_ENABLE_IOCTL: - spur_val = ah->config.spurchans[i][is2GHz]; - ath_dbg(common, ANI, "Getting spur val from new loc. %d\n", - spur_val); - break; - case SPUR_ENABLE_EEPROM: - spur_val = EEP_DEF_SPURCHAN; - break; - } - - return spur_val; - -#undef EEP_DEF_SPURCHAN + return ah->eeprom.def.modalHeader[is2GHz].spurChans[i].spurChan; } const struct eeprom_ops eep_def_ops = { diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index c34f21241da9..b1956bf6e01e 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -157,36 +157,6 @@ static void ath_detect_bt_priority(struct ath_softc *sc) } } -static void ath9k_gen_timer_start(struct ath_hw *ah, - struct ath_gen_timer *timer, - u32 trig_timeout, - u32 timer_period) -{ - ath9k_hw_gen_timer_start(ah, timer, trig_timeout, timer_period); - - if ((ah->imask & ATH9K_INT_GENTIMER) == 0) { - ath9k_hw_disable_interrupts(ah); - ah->imask |= ATH9K_INT_GENTIMER; - ath9k_hw_set_interrupts(ah); - ath9k_hw_enable_interrupts(ah); - } -} - -static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) -{ - struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; - - ath9k_hw_gen_timer_stop(ah, timer); - - /* if no timer is enabled, turn off interrupt mask */ - if (timer_table->timer_mask.val == 0) { - ath9k_hw_disable_interrupts(ah); - ah->imask &= ~ATH9K_INT_GENTIMER; - ath9k_hw_set_interrupts(ah); - ath9k_hw_enable_interrupts(ah); - } -} - static void ath_mci_ftp_adjust(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; @@ -257,19 +227,9 @@ static void ath_btcoex_period_timer(unsigned long data) spin_unlock_bh(&btcoex->btcoex_lock); - /* - * btcoex_period is in msec while (btocex/btscan_)no_stomp are in usec, - * ensure that we properly convert btcoex_period to usec - * for any comparision with (btcoex/btscan_)no_stomp. - */ - if (btcoex->btcoex_period * 1000 != btcoex->btcoex_no_stomp) { - if (btcoex->hw_timer_enabled) - ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); - - ath9k_gen_timer_start(ah, btcoex->no_stomp_timer, timer_period, - timer_period * 10); - btcoex->hw_timer_enabled = true; - } + if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) + mod_timer(&btcoex->no_stomp_timer, + jiffies + msecs_to_jiffies(timer_period)); ath9k_ps_restore(sc); @@ -282,7 +242,7 @@ skip_hw_wakeup: * Generic tsf based hw timer which configures weight * registers to time slice between wlan and bt traffic */ -static void ath_btcoex_no_stomp_timer(void *arg) +static void ath_btcoex_no_stomp_timer(unsigned long arg) { struct ath_softc *sc = (struct ath_softc *)arg; struct ath_hw *ah = sc->sc_ah; @@ -311,24 +271,18 @@ static int ath_init_btcoex_timer(struct ath_softc *sc) struct ath_btcoex *btcoex = &sc->btcoex; btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD; - btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * 1000 * + btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * btcoex->btcoex_period / 100; - btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * 1000 * + btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * btcoex->btcoex_period / 100; setup_timer(&btcoex->period_timer, ath_btcoex_period_timer, (unsigned long) sc); + setup_timer(&btcoex->no_stomp_timer, ath_btcoex_no_stomp_timer, + (unsigned long) sc); spin_lock_init(&btcoex->btcoex_lock); - btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah, - ath_btcoex_no_stomp_timer, - ath_btcoex_no_stomp_timer, - (void *) sc, AR_FIRST_NDP_TIMER); - - if (!btcoex->no_stomp_timer) - return -ENOMEM; - return 0; } @@ -343,10 +297,7 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc) ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n"); /* make sure duty cycle timer is also stopped when resuming */ - if (btcoex->hw_timer_enabled) { - ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); - btcoex->hw_timer_enabled = false; - } + del_timer_sync(&btcoex->no_stomp_timer); btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; @@ -363,24 +314,16 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc) void ath9k_btcoex_timer_pause(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; - struct ath_hw *ah = sc->sc_ah; del_timer_sync(&btcoex->period_timer); - - if (btcoex->hw_timer_enabled) { - ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); - btcoex->hw_timer_enabled = false; - } + del_timer_sync(&btcoex->no_stomp_timer); } void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; - if (btcoex->hw_timer_enabled) { - ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); - btcoex->hw_timer_enabled = false; - } + del_timer_sync(&btcoex->no_stomp_timer); } u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) @@ -400,12 +343,6 @@ u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status) { - struct ath_hw *ah = sc->sc_ah; - - if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) - if (status & ATH9K_INT_GENTIMER) - ath_gen_timer_isr(sc->sc_ah); - if (status & ATH9K_INT_MCI) ath_mci_intr(sc); } @@ -447,10 +384,6 @@ void ath9k_deinit_btcoex(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; - if ((sc->btcoex.no_stomp_timer) && - ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_3WIRE) - ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer); - if (ath9k_hw_mci_is_enabled(ah)) ath_mci_cleanup(sc); } diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 055d7c25e090..58da3468d1f0 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -600,10 +600,15 @@ void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw); struct base_eep_header *ath9k_htc_get_eeprom_base(struct ath9k_htc_priv *priv); #ifdef CONFIG_MAC80211_LEDS +void ath9k_configure_leds(struct ath9k_htc_priv *priv); void ath9k_init_leds(struct ath9k_htc_priv *priv); void ath9k_deinit_leds(struct ath9k_htc_priv *priv); void ath9k_led_work(struct work_struct *work); #else +static inline void ath9k_configure_leds(struct ath9k_htc_priv *priv) +{ +} + static inline void ath9k_init_leds(struct ath9k_htc_priv *priv) { } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index e0c03bd64182..8b5757734596 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -70,11 +70,11 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, struct ath9k_beacon_state bs; enum ath9k_int imask = 0; int dtimperiod, dtimcount, sleepduration; - int cfpperiod, cfpcount, bmiss_timeout; + int bmiss_timeout; u32 nexttbtt = 0, intval, tsftu; __be32 htc_imask = 0; u64 tsf; - int num_beacons, offset, dtim_dec_count, cfp_dec_count; + int num_beacons, offset, dtim_dec_count; int ret __attribute__ ((unused)); u8 cmd_rsp; @@ -84,7 +84,7 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, bmiss_timeout = (ATH_DEFAULT_BMISS_LIMIT * bss_conf->beacon_interval); /* - * Setup dtim and cfp parameters according to + * Setup dtim parameters according to * last beacon we received (which may be none). */ dtimperiod = bss_conf->dtim_period; @@ -93,8 +93,6 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, dtimcount = 1; if (dtimcount >= dtimperiod) /* NB: sanity check */ dtimcount = 0; - cfpperiod = 1; /* NB: no PCF support yet */ - cfpcount = 0; sleepduration = intval; if (sleepduration <= 0) @@ -102,7 +100,7 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, /* * Pull nexttbtt forward to reflect the current - * TSF and calculate dtim+cfp state for the result. + * TSF and calculate dtim state for the result. */ tsf = ath9k_hw_gettsf64(priv->ah); tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; @@ -115,26 +113,14 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, /* DTIM Beacon every dtimperiod Beacon */ dtim_dec_count = num_beacons % dtimperiod; - /* CFP every cfpperiod DTIM Beacon */ - cfp_dec_count = (num_beacons / dtimperiod) % cfpperiod; - if (dtim_dec_count) - cfp_dec_count++; - dtimcount -= dtim_dec_count; if (dtimcount < 0) dtimcount += dtimperiod; - cfpcount -= cfp_dec_count; - if (cfpcount < 0) - cfpcount += cfpperiod; - - bs.bs_intval = intval; - bs.bs_nexttbtt = nexttbtt; - bs.bs_dtimperiod = dtimperiod*intval; - bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount*intval; - bs.bs_cfpperiod = cfpperiod*bs.bs_dtimperiod; - bs.bs_cfpnext = bs.bs_nextdtim + cfpcount*bs.bs_dtimperiod; - bs.bs_cfpmaxduration = 0; + bs.bs_intval = TU_TO_USEC(intval); + bs.bs_nexttbtt = TU_TO_USEC(nexttbtt); + bs.bs_dtimperiod = dtimperiod * bs.bs_intval; + bs.bs_nextdtim = bs.bs_nexttbtt + dtimcount * bs.bs_intval; /* * Calculate the number of consecutive beacons to miss* before taking @@ -161,7 +147,8 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, * XXX fixed at 100ms */ - bs.bs_sleepduration = roundup(IEEE80211_MS_TO_TU(100), sleepduration); + bs.bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100), + sleepduration)); if (bs.bs_sleepduration > bs.bs_dtimperiod) bs.bs_sleepduration = bs.bs_dtimperiod; @@ -170,10 +157,8 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, ath_dbg(common, CONFIG, "intval: %u tsf: %llu tsftu: %u\n", intval, tsf, tsftu); - ath_dbg(common, CONFIG, - "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", - bs.bs_bmissthreshold, bs.bs_sleepduration, - bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); + ath_dbg(common, CONFIG, "bmiss: %u sleep: %u\n", + bs.bs_bmissthreshold, bs.bs_sleepduration); /* Set the computed STA beacon timers */ diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c index 105582d6b714..50f74a2a4cf8 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c @@ -255,6 +255,17 @@ void ath9k_deinit_leds(struct ath9k_htc_priv *priv) cancel_work_sync(&priv->led_work); } + +void ath9k_configure_leds(struct ath9k_htc_priv *priv) +{ + /* Configure gpio 1 for output */ + ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + /* LED off, active low */ + ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); + +} + void ath9k_init_leds(struct ath9k_htc_priv *priv) { int ret; @@ -268,11 +279,7 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv) else priv->ah->led_pin = ATH_LED_PIN_DEF; - /* Configure gpio 1 for output */ - ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin, - AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - /* LED off, active low */ - ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); + ath9k_configure_leds(priv); snprintf(priv->led_name, sizeof(priv->led_name), "ath9k_htc-%s", wiphy_name(priv->hw->wiphy)); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index c3676bf1d6c4..b576c44bb314 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -1000,6 +1000,8 @@ int ath9k_htc_resume(struct htc_target *htc_handle) ret = ath9k_init_htc_services(priv, priv->ah->hw_version.devid, priv->ah->hw_version.usbdev); + ath9k_configure_leds(priv); + return ret; } #endif diff --git a/drivers/net/wireless/ath/ath9k/hw-ops.h b/drivers/net/wireless/ath/ath9k/hw-ops.h index 4f9378ddf07f..cc58a8ee1223 100644 --- a/drivers/net/wireless/ath/ath9k/hw-ops.h +++ b/drivers/net/wireless/ath/ath9k/hw-ops.h @@ -49,9 +49,10 @@ static inline bool ath9k_hw_calibrate(struct ath_hw *ah, return ath9k_hw_ops(ah)->calibrate(ah, chan, rxchainmask, longcal); } -static inline bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked) +static inline bool ath9k_hw_getisr(struct ath_hw *ah, enum ath9k_int *masked, + u32 *sync_cause_p) { - return ath9k_hw_ops(ah)->get_isr(ah, masked); + return ath9k_hw_ops(ah)->get_isr(ah, masked, sync_cause_p); } static inline void ath9k_hw_set_txdesc(struct ath_hw *ah, void *ds, diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 4ee24b1eec38..a4b1ae026216 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -18,6 +18,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/time.h> +#include <linux/bitops.h> #include <asm/unaligned.h> #include "hw.h" @@ -84,48 +85,6 @@ static void ath9k_hw_ani_cache_ini_regs(struct ath_hw *ah) #ifdef CONFIG_ATH9K_DEBUGFS -void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause) -{ - struct ath_softc *sc = common->priv; - if (sync_cause) - sc->debug.stats.istats.sync_cause_all++; - if (sync_cause & AR_INTR_SYNC_RTC_IRQ) - sc->debug.stats.istats.sync_rtc_irq++; - if (sync_cause & AR_INTR_SYNC_MAC_IRQ) - sc->debug.stats.istats.sync_mac_irq++; - if (sync_cause & AR_INTR_SYNC_EEPROM_ILLEGAL_ACCESS) - sc->debug.stats.istats.eeprom_illegal_access++; - if (sync_cause & AR_INTR_SYNC_APB_TIMEOUT) - sc->debug.stats.istats.apb_timeout++; - if (sync_cause & AR_INTR_SYNC_PCI_MODE_CONFLICT) - sc->debug.stats.istats.pci_mode_conflict++; - if (sync_cause & AR_INTR_SYNC_HOST1_FATAL) - sc->debug.stats.istats.host1_fatal++; - if (sync_cause & AR_INTR_SYNC_HOST1_PERR) - sc->debug.stats.istats.host1_perr++; - if (sync_cause & AR_INTR_SYNC_TRCV_FIFO_PERR) - sc->debug.stats.istats.trcv_fifo_perr++; - if (sync_cause & AR_INTR_SYNC_RADM_CPL_EP) - sc->debug.stats.istats.radm_cpl_ep++; - if (sync_cause & AR_INTR_SYNC_RADM_CPL_DLLP_ABORT) - sc->debug.stats.istats.radm_cpl_dllp_abort++; - if (sync_cause & AR_INTR_SYNC_RADM_CPL_TLP_ABORT) - sc->debug.stats.istats.radm_cpl_tlp_abort++; - if (sync_cause & AR_INTR_SYNC_RADM_CPL_ECRC_ERR) - sc->debug.stats.istats.radm_cpl_ecrc_err++; - if (sync_cause & AR_INTR_SYNC_RADM_CPL_TIMEOUT) - sc->debug.stats.istats.radm_cpl_timeout++; - if (sync_cause & AR_INTR_SYNC_LOCAL_TIMEOUT) - sc->debug.stats.istats.local_timeout++; - if (sync_cause & AR_INTR_SYNC_PM_ACCESS) - sc->debug.stats.istats.pm_access++; - if (sync_cause & AR_INTR_SYNC_MAC_AWAKE) - sc->debug.stats.istats.mac_awake++; - if (sync_cause & AR_INTR_SYNC_MAC_ASLEEP) - sc->debug.stats.istats.mac_asleep++; - if (sync_cause & AR_INTR_SYNC_MAC_SLEEP_ACCESS) - sc->debug.stats.istats.mac_sleep_access++; -} #endif @@ -438,21 +397,12 @@ static bool ath9k_hw_chip_test(struct ath_hw *ah) static void ath9k_hw_init_config(struct ath_hw *ah) { - int i; - ah->config.dma_beacon_response_time = 1; ah->config.sw_beacon_response_time = 6; - ah->config.additional_swba_backoff = 0; ah->config.ack_6mb = 0x0; ah->config.cwm_ignore_extcca = 0; - ah->config.pcie_clock_req = 0; ah->config.analog_shiftreg = 1; - for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) { - ah->config.spurchans[i][0] = AR_NO_SPUR; - ah->config.spurchans[i][1] = AR_NO_SPUR; - } - ah->config.rx_intr_mitigation = true; /* @@ -485,7 +435,6 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) ah->hw_version.magic = AR5416_MAGIC; ah->hw_version.subvendorid = 0; - ah->atim_window = 0; ah->sta_id1_defaults = AR_STA_ID1_CRPT_MIC_ENABLE | AR_STA_ID1_MCAST_KSRCH; @@ -1281,6 +1230,42 @@ void ath9k_hw_get_delta_slope_vals(struct ath_hw *ah, u32 coef_scaled, *coef_exponent = coef_exp - 16; } +/* AR9330 WAR: + * call external reset function to reset WMAC if: + * - doing a cold reset + * - we have pending frames in the TX queues. + */ +static bool ath9k_hw_ar9330_reset_war(struct ath_hw *ah, int type) +{ + int i, npend = 0; + + for (i = 0; i < AR_NUM_QCU; i++) { + npend = ath9k_hw_numtxpending(ah, i); + if (npend) + break; + } + + if (ah->external_reset && + (npend || type == ATH9K_RESET_COLD)) { + int reset_err = 0; + + ath_dbg(ath9k_hw_common(ah), RESET, + "reset MAC via external reset\n"); + + reset_err = ah->external_reset(); + if (reset_err) { + ath_err(ath9k_hw_common(ah), + "External reset failed, err=%d\n", + reset_err); + return false; + } + + REG_WRITE(ah, AR_RTC_RESET, 1); + } + + return true; +} + static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) { u32 rst_flags; @@ -1331,38 +1316,8 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) } if (AR_SREV_9330(ah)) { - int npend = 0; - int i; - - /* AR9330 WAR: - * call external reset function to reset WMAC if: - * - doing a cold reset - * - we have pending frames in the TX queues - */ - - for (i = 0; i < AR_NUM_QCU; i++) { - npend = ath9k_hw_numtxpending(ah, i); - if (npend) - break; - } - - if (ah->external_reset && - (npend || type == ATH9K_RESET_COLD)) { - int reset_err = 0; - - ath_dbg(ath9k_hw_common(ah), RESET, - "reset MAC via external reset\n"); - - reset_err = ah->external_reset(); - if (reset_err) { - ath_err(ath9k_hw_common(ah), - "External reset failed, err=%d\n", - reset_err); - return false; - } - - REG_WRITE(ah, AR_RTC_RESET, 1); - } + if (!ath9k_hw_ar9330_reset_war(ah, type)) + return false; } if (ath9k_hw_mci_is_enabled(ah)) @@ -1372,7 +1327,12 @@ static bool ath9k_hw_set_reset(struct ath_hw *ah, int type) REGWRITE_BUFFER_FLUSH(ah); - udelay(50); + if (AR_SREV_9300_20_OR_LATER(ah)) + udelay(50); + else if (AR_SREV_9100(ah)) + udelay(10000); + else + udelay(100); REG_WRITE(ah, AR_RTC_RC, 0); if (!ath9k_hw_wait(ah, AR_RTC_RC, AR_RTC_RC_M, 0, AH_WAIT_TIMEOUT)) { @@ -1408,8 +1368,7 @@ static bool ath9k_hw_set_reset_power_on(struct ath_hw *ah) REGWRITE_BUFFER_FLUSH(ah); - if (!AR_SREV_9300_20_OR_LATER(ah)) - udelay(2); + udelay(2); if (!AR_SREV_9100(ah) && !AR_SREV_9300_20_OR_LATER(ah)) REG_WRITE(ah, AR_RC, 0); @@ -1485,7 +1444,6 @@ static bool ath9k_hw_chip_reset(struct ath_hw *ah, if (AR_SREV_9330(ah)) ar9003_hw_internal_regulator_apply(ah); ath9k_hw_init_pll(ah, chan); - ath9k_hw_set_rfmode(ah, chan); return true; } @@ -1954,6 +1912,8 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (r) return r; + ath9k_hw_set_rfmode(ah, chan); + if (ath9k_hw_mci_is_enabled(ah)) ar9003_mci_reset(ah, false, IS_CHAN_2GHZ(chan), save_fullsleep); @@ -2264,9 +2224,6 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) case NL80211_IFTYPE_ADHOC: REG_SET_BIT(ah, AR_TXCFG, AR_TXCFG_ADHOC_BEACON_ATIM_TX_POLICY); - REG_WRITE(ah, AR_NEXT_NDP_TIMER, next_beacon + - TU_TO_USEC(ah->atim_window ? ah->atim_window : 1)); - flags |= AR_NDP_TIMER_EN; case NL80211_IFTYPE_MESH_POINT: case NL80211_IFTYPE_AP: REG_WRITE(ah, AR_NEXT_TBTT_TIMER, next_beacon); @@ -2287,7 +2244,6 @@ void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period) REG_WRITE(ah, AR_BEACON_PERIOD, beacon_period); REG_WRITE(ah, AR_DMA_BEACON_PERIOD, beacon_period); REG_WRITE(ah, AR_SWBA_PERIOD, beacon_period); - REG_WRITE(ah, AR_NDP_PERIOD, beacon_period); REGWRITE_BUFFER_FLUSH(ah); @@ -2304,12 +2260,9 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, ENABLE_REGWRITE_BUFFER(ah); - REG_WRITE(ah, AR_NEXT_TBTT_TIMER, TU_TO_USEC(bs->bs_nexttbtt)); - - REG_WRITE(ah, AR_BEACON_PERIOD, - TU_TO_USEC(bs->bs_intval)); - REG_WRITE(ah, AR_DMA_BEACON_PERIOD, - TU_TO_USEC(bs->bs_intval)); + REG_WRITE(ah, AR_NEXT_TBTT_TIMER, bs->bs_nexttbtt); + REG_WRITE(ah, AR_BEACON_PERIOD, bs->bs_intval); + REG_WRITE(ah, AR_DMA_BEACON_PERIOD, bs->bs_intval); REGWRITE_BUFFER_FLUSH(ah); @@ -2337,9 +2290,8 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, ENABLE_REGWRITE_BUFFER(ah); - REG_WRITE(ah, AR_NEXT_DTIM, - TU_TO_USEC(bs->bs_nextdtim - SLEEP_SLOP)); - REG_WRITE(ah, AR_NEXT_TIM, TU_TO_USEC(nextTbtt - SLEEP_SLOP)); + REG_WRITE(ah, AR_NEXT_DTIM, bs->bs_nextdtim - SLEEP_SLOP); + REG_WRITE(ah, AR_NEXT_TIM, nextTbtt - SLEEP_SLOP); REG_WRITE(ah, AR_SLEEP1, SM((CAB_TIMEOUT_VAL << 3), AR_SLEEP1_CAB_TIMEOUT) @@ -2353,8 +2305,8 @@ void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, REG_WRITE(ah, AR_SLEEP2, SM(beacontimeout, AR_SLEEP2_BEACON_TIMEOUT)); - REG_WRITE(ah, AR_TIM_PERIOD, TU_TO_USEC(beaconintval)); - REG_WRITE(ah, AR_DTIM_PERIOD, TU_TO_USEC(dtimperiod)); + REG_WRITE(ah, AR_TIM_PERIOD, beaconintval); + REG_WRITE(ah, AR_DTIM_PERIOD, dtimperiod); REGWRITE_BUFFER_FLUSH(ah); @@ -2990,20 +2942,6 @@ static const struct ath_gen_timer_configuration gen_tmr_configuration[] = /* HW generic timer primitives */ -/* compute and clear index of rightmost 1 */ -static u32 rightmost_index(struct ath_gen_timer_table *timer_table, u32 *mask) -{ - u32 b; - - b = *mask; - b &= (0-b); - *mask &= ~b; - b *= debruijn32; - b >>= 27; - - return timer_table->gen_timer_index[b]; -} - u32 ath9k_hw_gettsf32(struct ath_hw *ah) { return REG_READ(ah, AR_TSF_L32); @@ -3019,6 +2957,10 @@ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer *timer; + if ((timer_index < AR_FIRST_NDP_TIMER) || + (timer_index >= ATH_MAX_GEN_TIMER)) + return NULL; + timer = kzalloc(sizeof(struct ath_gen_timer), GFP_KERNEL); if (timer == NULL) return NULL; @@ -3036,23 +2978,13 @@ EXPORT_SYMBOL(ath_gen_timer_alloc); void ath9k_hw_gen_timer_start(struct ath_hw *ah, struct ath_gen_timer *timer, - u32 trig_timeout, + u32 timer_next, u32 timer_period) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; - u32 tsf, timer_next; - - BUG_ON(!timer_period); - - set_bit(timer->index, &timer_table->timer_mask.timer_bits); - - tsf = ath9k_hw_gettsf32(ah); + u32 mask = 0; - timer_next = tsf + trig_timeout; - - ath_dbg(ath9k_hw_common(ah), BTCOEX, - "current tsf %x period %x timer_next %x\n", - tsf, timer_period, timer_next); + timer_table->timer_mask |= BIT(timer->index); /* * Program generic timer registers @@ -3078,10 +3010,19 @@ void ath9k_hw_gen_timer_start(struct ath_hw *ah, (1 << timer->index)); } - /* Enable both trigger and thresh interrupt masks */ - REG_SET_BIT(ah, AR_IMR_S5, - (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | - SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); + if (timer->trigger) + mask |= SM(AR_GENTMR_BIT(timer->index), + AR_IMR_S5_GENTIMER_TRIG); + if (timer->overflow) + mask |= SM(AR_GENTMR_BIT(timer->index), + AR_IMR_S5_GENTIMER_THRESH); + + REG_SET_BIT(ah, AR_IMR_S5, mask); + + if ((ah->imask & ATH9K_INT_GENTIMER) == 0) { + ah->imask |= ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah); + } } EXPORT_SYMBOL(ath9k_hw_gen_timer_start); @@ -3089,11 +3030,6 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; - if ((timer->index < AR_FIRST_NDP_TIMER) || - (timer->index >= ATH_MAX_GEN_TIMER)) { - return; - } - /* Clear generic timer enable bits. */ REG_CLR_BIT(ah, gen_tmr_configuration[timer->index].mode_addr, gen_tmr_configuration[timer->index].mode_mask); @@ -3113,7 +3049,12 @@ void ath9k_hw_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) (SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_THRESH) | SM(AR_GENTMR_BIT(timer->index), AR_IMR_S5_GENTIMER_TRIG))); - clear_bit(timer->index, &timer_table->timer_mask.timer_bits); + timer_table->timer_mask &= ~BIT(timer->index); + + if (timer_table->timer_mask == 0) { + ah->imask &= ~ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah); + } } EXPORT_SYMBOL(ath9k_hw_gen_timer_stop); @@ -3134,32 +3075,32 @@ void ath_gen_timer_isr(struct ath_hw *ah) { struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; struct ath_gen_timer *timer; - struct ath_common *common = ath9k_hw_common(ah); - u32 trigger_mask, thresh_mask, index; + unsigned long trigger_mask, thresh_mask; + unsigned int index; /* get hardware generic timer interrupt status */ trigger_mask = ah->intr_gen_timer_trigger; thresh_mask = ah->intr_gen_timer_thresh; - trigger_mask &= timer_table->timer_mask.val; - thresh_mask &= timer_table->timer_mask.val; + trigger_mask &= timer_table->timer_mask; + thresh_mask &= timer_table->timer_mask; trigger_mask &= ~thresh_mask; - while (thresh_mask) { - index = rightmost_index(timer_table, &thresh_mask); + for_each_set_bit(index, &thresh_mask, ARRAY_SIZE(timer_table->timers)) { timer = timer_table->timers[index]; - BUG_ON(!timer); - ath_dbg(common, BTCOEX, "TSF overflow for Gen timer %d\n", - index); + if (!timer) + continue; + if (!timer->overflow) + continue; timer->overflow(timer->arg); } - while (trigger_mask) { - index = rightmost_index(timer_table, &trigger_mask); + for_each_set_bit(index, &trigger_mask, ARRAY_SIZE(timer_table->timers)) { timer = timer_table->timers[index]; - BUG_ON(!timer); - ath_dbg(common, BTCOEX, - "Gen timer[%d] trigger\n", index); + if (!timer) + continue; + if (!timer->trigger) + continue; timer->trigger(timer->arg); } } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index b1ff54631dca..6132ffeb3048 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -168,7 +168,7 @@ #define CAB_TIMEOUT_VAL 10 #define BEACON_TIMEOUT_VAL 10 #define MIN_BEACON_TIMEOUT_VAL 1 -#define SLEEP_SLOP 3 +#define SLEEP_SLOP TU_TO_USEC(3) #define INIT_CONFIG_STATUS 0x00000000 #define INIT_RSSI_THR 0x00000700 @@ -280,10 +280,8 @@ struct ath9k_hw_capabilities { struct ath9k_ops_config { int dma_beacon_response_time; int sw_beacon_response_time; - int additional_swba_backoff; int ack_6mb; u32 cwm_ignore_extcca; - u8 pcie_clock_req; u32 pcie_waen; u8 analog_shiftreg; u32 ofdm_trig_low; @@ -294,18 +292,11 @@ struct ath9k_ops_config { int serialize_regmode; bool rx_intr_mitigation; bool tx_intr_mitigation; -#define SPUR_DISABLE 0 -#define SPUR_ENABLE_IOCTL 1 -#define SPUR_ENABLE_EEPROM 2 -#define AR_SPUR_5413_1 1640 -#define AR_SPUR_5413_2 1200 #define AR_NO_SPUR 0x8000 #define AR_BASE_FREQ_2GHZ 2300 #define AR_BASE_FREQ_5GHZ 4900 #define AR_SPUR_FEEQ_BOUND_HT40 19 #define AR_SPUR_FEEQ_BOUND_HT20 10 - int spurmode; - u16 spurchans[AR_EEPROM_MODAL_SPURS][2]; u8 max_txtrig_level; u16 ani_poll_interval; /* ANI poll interval in ms */ @@ -460,10 +451,6 @@ struct ath9k_beacon_state { u32 bs_intval; #define ATH9K_TSFOOR_THRESHOLD 0x00004240 /* 16k us */ u32 bs_dtimperiod; - u16 bs_cfpperiod; - u16 bs_cfpmaxduration; - u32 bs_cfpnext; - u16 bs_timoffset; u16 bs_bmissthreshold; u32 bs_sleepduration; u32 bs_tsfoor_threshold; @@ -499,12 +486,6 @@ struct ath9k_hw_version { #define AR_GENTMR_BIT(_index) (1 << (_index)) -/* - * Using de Bruijin sequence to look up 1's index in a 32 bit number - * debruijn32 = 0000 0111 0111 1100 1011 0101 0011 0001 - */ -#define debruijn32 0x077CB531U - struct ath_gen_timer_configuration { u32 next_addr; u32 period_addr; @@ -520,12 +501,8 @@ struct ath_gen_timer { }; struct ath_gen_timer_table { - u32 gen_timer_index[32]; struct ath_gen_timer *timers[ATH_MAX_GEN_TIMER]; - union { - unsigned long timer_bits; - u16 val; - } timer_mask; + u16 timer_mask; }; struct ath_hw_antcomb_conf { @@ -690,7 +667,8 @@ struct ath_hw_ops { struct ath9k_channel *chan, u8 rxchainmask, bool longcal); - bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked); + bool (*get_isr)(struct ath_hw *ah, enum ath9k_int *masked, + u32 *sync_cause_p); void (*set_txdesc)(struct ath_hw *ah, void *ds, struct ath_tx_info *i); int (*proc_txdesc)(struct ath_hw *ah, void *ds, @@ -786,7 +764,6 @@ struct ath_hw { u32 txurn_interrupt_mask; atomic_t intr_ref_cnt; bool chip_fullsleep; - u32 atim_window; u32 modes_index; /* Calibration */ @@ -1018,13 +995,6 @@ bool ath9k_hw_check_alive(struct ath_hw *ah); bool ath9k_hw_setpower(struct ath_hw *ah, enum ath9k_power_mode mode); -#ifdef CONFIG_ATH9K_DEBUGFS -void ath9k_debug_sync_cause(struct ath_common *common, u32 sync_cause); -#else -static inline void ath9k_debug_sync_cause(struct ath_common *common, - u32 sync_cause) {} -#endif - /* Generic hw timer primitives */ struct ath_gen_timer *ath_gen_timer_alloc(struct ath_hw *ah, void (*trigger)(void *), diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 609b7e13f3f0..e63465b7eab9 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -470,7 +470,6 @@ static int ath9k_init_queues(struct ath_softc *sc) sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah); sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); - ath_cabq_update(sc); sc->tx.uapsdq = ath_txq_setup(sc, ATH9K_TX_QUEUE_UAPSD, 0); @@ -705,7 +704,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, ah->reg_ops.read = ath9k_ioread32; ah->reg_ops.write = ath9k_iowrite32; ah->reg_ops.rmw = ath9k_reg_rmw; - atomic_set(&ah->intr_ref_cnt, -1); sc->sc_ah = ah; pCap = &ah->caps; @@ -899,7 +897,7 @@ static const struct ieee80211_iface_combination if_comb[] = { } }; -void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) +static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 6a18f9d3e9cc..89d7e206992b 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -481,8 +481,7 @@ bool ath9k_hw_resettxqueue(struct ath_hw *ah, u32 q) | AR_Q_MISC_CBR_INCR_DIS0); value = (qi->tqi_readyTime - (ah->config.sw_beacon_response_time - - ah->config.dma_beacon_response_time) - - ah->config.additional_swba_backoff) * 1024; + ah->config.dma_beacon_response_time)) * 1024; REG_WRITE(ah, AR_QRDYTIMECFG(q), value | AR_Q_RDYTIMECFG_EN); REG_SET_BIT(ah, AR_DMISC(q), @@ -550,25 +549,25 @@ int ath9k_hw_rxprocdesc(struct ath_hw *ah, struct ath_desc *ds, if (ads.ds_rxstatus8 & AR_PostDelimCRCErr) { rs->rs_rssi = ATH9K_RSSI_BAD; - rs->rs_rssi_ctl0 = ATH9K_RSSI_BAD; - rs->rs_rssi_ctl1 = ATH9K_RSSI_BAD; - rs->rs_rssi_ctl2 = ATH9K_RSSI_BAD; - rs->rs_rssi_ext0 = ATH9K_RSSI_BAD; - rs->rs_rssi_ext1 = ATH9K_RSSI_BAD; - rs->rs_rssi_ext2 = ATH9K_RSSI_BAD; + rs->rs_rssi_ctl[0] = ATH9K_RSSI_BAD; + rs->rs_rssi_ctl[1] = ATH9K_RSSI_BAD; + rs->rs_rssi_ctl[2] = ATH9K_RSSI_BAD; + rs->rs_rssi_ext[0] = ATH9K_RSSI_BAD; + rs->rs_rssi_ext[1] = ATH9K_RSSI_BAD; + rs->rs_rssi_ext[2] = ATH9K_RSSI_BAD; } else { rs->rs_rssi = MS(ads.ds_rxstatus4, AR_RxRSSICombined); - rs->rs_rssi_ctl0 = MS(ads.ds_rxstatus0, + rs->rs_rssi_ctl[0] = MS(ads.ds_rxstatus0, AR_RxRSSIAnt00); - rs->rs_rssi_ctl1 = MS(ads.ds_rxstatus0, + rs->rs_rssi_ctl[1] = MS(ads.ds_rxstatus0, AR_RxRSSIAnt01); - rs->rs_rssi_ctl2 = MS(ads.ds_rxstatus0, + rs->rs_rssi_ctl[2] = MS(ads.ds_rxstatus0, AR_RxRSSIAnt02); - rs->rs_rssi_ext0 = MS(ads.ds_rxstatus4, + rs->rs_rssi_ext[0] = MS(ads.ds_rxstatus4, AR_RxRSSIAnt10); - rs->rs_rssi_ext1 = MS(ads.ds_rxstatus4, + rs->rs_rssi_ext[1] = MS(ads.ds_rxstatus4, AR_RxRSSIAnt11); - rs->rs_rssi_ext2 = MS(ads.ds_rxstatus4, + rs->rs_rssi_ext[2] = MS(ads.ds_rxstatus4, AR_RxRSSIAnt12); } if (ads.ds_rxstatus8 & AR_RxKeyIdxValid) diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index e3eed81f2439..10271373a0cd 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -133,12 +133,8 @@ struct ath_rx_status { u8 rs_rate; u8 rs_antenna; u8 rs_more; - int8_t rs_rssi_ctl0; - int8_t rs_rssi_ctl1; - int8_t rs_rssi_ctl2; - int8_t rs_rssi_ext0; - int8_t rs_rssi_ext1; - int8_t rs_rssi_ext2; + int8_t rs_rssi_ctl[3]; + int8_t rs_rssi_ext[3]; u8 rs_isaggr; u8 rs_firstaggr; u8 rs_moreaggr; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index b1dcf89138d3..173a889f9dbb 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -508,6 +508,9 @@ void ath9k_tasklet(unsigned long data) wake_up(&sc->tx_wait); } + if (status & ATH9K_INT_GENTIMER) + ath_gen_timer_isr(sc->sc_ah); + ath9k_btcoex_handle_interrupt(sc, status); /* re-enable hardware interrupt */ @@ -538,6 +541,7 @@ irqreturn_t ath_isr(int irq, void *dev) struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); enum ath9k_int status; + u32 sync_cause; bool sched = false; /* @@ -564,7 +568,8 @@ irqreturn_t ath_isr(int irq, void *dev) * bits we haven't explicitly enabled so we mask the * value to insure we only process bits we requested. */ - ath9k_hw_getisr(ah, &status); /* NB: clears ISR too */ + ath9k_hw_getisr(ah, &status, &sync_cause); /* NB: clears ISR too */ + ath9k_debug_sync_cause(sc, sync_cause); status &= ah->imask; /* discard unasked-for bits */ /* @@ -757,6 +762,8 @@ static int ath9k_start(struct ieee80211_hw *hw) */ ath9k_cmn_init_crypto(sc->sc_ah); + ath9k_hw_reset_tsf(ah); + spin_unlock_bh(&sc->sc_pcu_lock); mutex_unlock(&sc->mutex); @@ -1657,13 +1664,8 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, } if ((changed & BSS_CHANGED_BEACON_ENABLED) || - (changed & BSS_CHANGED_BEACON_INT)) { - if (ah->opmode == NL80211_IFTYPE_AP && - bss_conf->enable_beacon) - ath9k_set_tsfadjust(sc, vif); - if (ath9k_allow_beacon_config(sc, vif)) - ath9k_beacon_config(sc, vif, changed); - } + (changed & BSS_CHANGED_BEACON_INT)) + ath9k_beacon_config(sc, vif, changed); if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 0ac1b5f04256..71799fcade54 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -200,7 +200,7 @@ skip_tuning: if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE) btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE; - btcoex->btcoex_no_stomp = btcoex->btcoex_period * 1000 * + btcoex->btcoex_no_stomp = btcoex->btcoex_period * (100 - btcoex->duty_cycle) / 100; ath9k_hw_btcoex_enable(sc->sc_ah); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 95ddca5495d4..3692b2a501a2 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -15,7 +15,6 @@ */ #include <linux/dma-mapping.h> -#include <linux/relay.h> #include "ath9k.h" #include "ar9003_mac.h" @@ -906,6 +905,7 @@ static void ath9k_process_rssi(struct ath_common *common, struct ath_hw *ah = common->ah; int last_rssi; int rssi = rx_stats->rs_rssi; + int i, j; /* * RSSI is not available for subframes in an A-MPDU. @@ -924,6 +924,20 @@ static void ath9k_process_rssi(struct ath_common *common, return; } + for (i = 0, j = 0; i < ARRAY_SIZE(rx_stats->rs_rssi_ctl); i++) { + s8 rssi; + + if (!(ah->rxchainmask & BIT(i))) + continue; + + rssi = rx_stats->rs_rssi_ctl[i]; + if (rssi != ATH9K_RSSI_BAD) { + rxs->chains |= BIT(j); + rxs->chain_signal[j] = ah->noise + rssi; + } + j++; + } + /* * Update Beacon RSSI, this is used by ANI. */ @@ -960,186 +974,6 @@ static void ath9k_process_tsf(struct ath_rx_status *rs, rxs->mactime += 0x100000000ULL; } -#ifdef CONFIG_ATH9K_DEBUGFS -static s8 fix_rssi_inv_only(u8 rssi_val) -{ - if (rssi_val == 128) - rssi_val = 0; - return (s8) rssi_val; -} -#endif - -/* returns 1 if this was a spectral frame, even if not handled. */ -static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, - struct ath_rx_status *rs, u64 tsf) -{ -#ifdef CONFIG_ATH9K_DEBUGFS - struct ath_hw *ah = sc->sc_ah; - u8 num_bins, *bins, *vdata = (u8 *)hdr; - struct fft_sample_ht20 fft_sample_20; - struct fft_sample_ht20_40 fft_sample_40; - struct fft_sample_tlv *tlv; - struct ath_radar_info *radar_info; - int len = rs->rs_datalen; - int dc_pos; - u16 fft_len, length, freq = ah->curchan->chan->center_freq; - enum nl80211_channel_type chan_type; - - /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer - * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT - * yet, but this is supposed to be possible as well. - */ - if (rs->rs_phyerr != ATH9K_PHYERR_RADAR && - rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT && - rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) - return 0; - - /* check if spectral scan bit is set. This does not have to be checked - * if received through a SPECTRAL phy error, but shouldn't hurt. - */ - radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; - if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) - return 0; - - chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef); - if ((chan_type == NL80211_CHAN_HT40MINUS) || - (chan_type == NL80211_CHAN_HT40PLUS)) { - fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; - num_bins = SPECTRAL_HT20_40_NUM_BINS; - bins = (u8 *)fft_sample_40.data; - } else { - fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; - num_bins = SPECTRAL_HT20_NUM_BINS; - bins = (u8 *)fft_sample_20.data; - } - - /* Variation in the data length is possible and will be fixed later */ - if ((len > fft_len + 2) || (len < fft_len - 1)) - return 1; - - switch (len - fft_len) { - case 0: - /* length correct, nothing to do. */ - memcpy(bins, vdata, num_bins); - break; - case -1: - /* first byte missing, duplicate it. */ - memcpy(&bins[1], vdata, num_bins - 1); - bins[0] = vdata[0]; - break; - case 2: - /* MAC added 2 extra bytes at bin 30 and 32, remove them. */ - memcpy(bins, vdata, 30); - bins[30] = vdata[31]; - memcpy(&bins[31], &vdata[33], num_bins - 31); - break; - case 1: - /* MAC added 2 extra bytes AND first byte is missing. */ - bins[0] = vdata[0]; - memcpy(&bins[1], vdata, 30); - bins[31] = vdata[31]; - memcpy(&bins[32], &vdata[33], num_bins - 32); - break; - default: - return 1; - } - - /* DC value (value in the middle) is the blind spot of the spectral - * sample and invalid, interpolate it. - */ - dc_pos = num_bins / 2; - bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2; - - if ((chan_type == NL80211_CHAN_HT40MINUS) || - (chan_type == NL80211_CHAN_HT40PLUS)) { - s8 lower_rssi, upper_rssi; - s16 ext_nf; - u8 lower_max_index, upper_max_index; - u8 lower_bitmap_w, upper_bitmap_w; - u16 lower_mag, upper_mag; - struct ath9k_hw_cal_data *caldata = ah->caldata; - struct ath_ht20_40_mag_info *mag_info; - - if (caldata) - ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan, - caldata->nfCalHist[3].privNF); - else - ext_nf = ATH_DEFAULT_NOISE_FLOOR; - - length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv); - fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40; - fft_sample_40.tlv.length = __cpu_to_be16(length); - fft_sample_40.freq = __cpu_to_be16(freq); - fft_sample_40.channel_type = chan_type; - - if (chan_type == NL80211_CHAN_HT40PLUS) { - lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); - upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0); - - fft_sample_40.lower_noise = ah->noise; - fft_sample_40.upper_noise = ext_nf; - } else { - lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext0); - upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); - - fft_sample_40.lower_noise = ext_nf; - fft_sample_40.upper_noise = ah->noise; - } - fft_sample_40.lower_rssi = lower_rssi; - fft_sample_40.upper_rssi = upper_rssi; - - mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1; - lower_mag = spectral_max_magnitude(mag_info->lower_bins); - upper_mag = spectral_max_magnitude(mag_info->upper_bins); - fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); - fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); - lower_max_index = spectral_max_index(mag_info->lower_bins); - upper_max_index = spectral_max_index(mag_info->upper_bins); - fft_sample_40.lower_max_index = lower_max_index; - fft_sample_40.upper_max_index = upper_max_index; - lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins); - upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins); - fft_sample_40.lower_bitmap_weight = lower_bitmap_w; - fft_sample_40.upper_bitmap_weight = upper_bitmap_w; - fft_sample_40.max_exp = mag_info->max_exp & 0xf; - - fft_sample_40.tsf = __cpu_to_be64(tsf); - - tlv = (struct fft_sample_tlv *)&fft_sample_40; - } else { - u8 max_index, bitmap_w; - u16 magnitude; - struct ath_ht20_mag_info *mag_info; - - length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv); - fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20; - fft_sample_20.tlv.length = __cpu_to_be16(length); - fft_sample_20.freq = __cpu_to_be16(freq); - - fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl0); - fft_sample_20.noise = ah->noise; - - mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; - magnitude = spectral_max_magnitude(mag_info->all_bins); - fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); - max_index = spectral_max_index(mag_info->all_bins); - fft_sample_20.max_index = max_index; - bitmap_w = spectral_bitmap_weight(mag_info->all_bins); - fft_sample_20.bitmap_weight = bitmap_w; - fft_sample_20.max_exp = mag_info->max_exp & 0xf; - - fft_sample_20.tsf = __cpu_to_be64(tsf); - - tlv = (struct fft_sample_tlv *)&fft_sample_20; - } - - ath_debug_send_fft_sample(sc, tlv); - return 1; -#else - return 0; -#endif -} - static bool ath9k_is_mybeacon(struct ath_softc *sc, struct ieee80211_hdr *hdr) { struct ath_hw *ah = sc->sc_ah; diff --git a/drivers/net/wireless/ath/ath9k/spectral.c b/drivers/net/wireless/ath/ath9k/spectral.c new file mode 100644 index 000000000000..11adb5eb51cc --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/spectral.c @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/relay.h> +#include "ath9k.h" + +static s8 fix_rssi_inv_only(u8 rssi_val) +{ + if (rssi_val == 128) + rssi_val = 0; + return (s8) rssi_val; +} + +static void ath_debug_send_fft_sample(struct ath_softc *sc, + struct fft_sample_tlv *fft_sample_tlv) +{ + int length; + if (!sc->rfs_chan_spec_scan) + return; + + length = __be16_to_cpu(fft_sample_tlv->length) + + sizeof(*fft_sample_tlv); + relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length); +} + +/* returns 1 if this was a spectral frame, even if not handled. */ +int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, + struct ath_rx_status *rs, u64 tsf) +{ + struct ath_hw *ah = sc->sc_ah; + u8 num_bins, *bins, *vdata = (u8 *)hdr; + struct fft_sample_ht20 fft_sample_20; + struct fft_sample_ht20_40 fft_sample_40; + struct fft_sample_tlv *tlv; + struct ath_radar_info *radar_info; + int len = rs->rs_datalen; + int dc_pos; + u16 fft_len, length, freq = ah->curchan->chan->center_freq; + enum nl80211_channel_type chan_type; + + /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer + * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT + * yet, but this is supposed to be possible as well. + */ + if (rs->rs_phyerr != ATH9K_PHYERR_RADAR && + rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT && + rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL) + return 0; + + /* check if spectral scan bit is set. This does not have to be checked + * if received through a SPECTRAL phy error, but shouldn't hurt. + */ + radar_info = ((struct ath_radar_info *)&vdata[len]) - 1; + if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK)) + return 0; + + chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef); + if ((chan_type == NL80211_CHAN_HT40MINUS) || + (chan_type == NL80211_CHAN_HT40PLUS)) { + fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN; + num_bins = SPECTRAL_HT20_40_NUM_BINS; + bins = (u8 *)fft_sample_40.data; + } else { + fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN; + num_bins = SPECTRAL_HT20_NUM_BINS; + bins = (u8 *)fft_sample_20.data; + } + + /* Variation in the data length is possible and will be fixed later */ + if ((len > fft_len + 2) || (len < fft_len - 1)) + return 1; + + switch (len - fft_len) { + case 0: + /* length correct, nothing to do. */ + memcpy(bins, vdata, num_bins); + break; + case -1: + /* first byte missing, duplicate it. */ + memcpy(&bins[1], vdata, num_bins - 1); + bins[0] = vdata[0]; + break; + case 2: + /* MAC added 2 extra bytes at bin 30 and 32, remove them. */ + memcpy(bins, vdata, 30); + bins[30] = vdata[31]; + memcpy(&bins[31], &vdata[33], num_bins - 31); + break; + case 1: + /* MAC added 2 extra bytes AND first byte is missing. */ + bins[0] = vdata[0]; + memcpy(&bins[1], vdata, 30); + bins[31] = vdata[31]; + memcpy(&bins[32], &vdata[33], num_bins - 32); + break; + default: + return 1; + } + + /* DC value (value in the middle) is the blind spot of the spectral + * sample and invalid, interpolate it. + */ + dc_pos = num_bins / 2; + bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2; + + if ((chan_type == NL80211_CHAN_HT40MINUS) || + (chan_type == NL80211_CHAN_HT40PLUS)) { + s8 lower_rssi, upper_rssi; + s16 ext_nf; + u8 lower_max_index, upper_max_index; + u8 lower_bitmap_w, upper_bitmap_w; + u16 lower_mag, upper_mag; + struct ath9k_hw_cal_data *caldata = ah->caldata; + struct ath_ht20_40_mag_info *mag_info; + + if (caldata) + ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan, + caldata->nfCalHist[3].privNF); + else + ext_nf = ATH_DEFAULT_NOISE_FLOOR; + + length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv); + fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40; + fft_sample_40.tlv.length = __cpu_to_be16(length); + fft_sample_40.freq = __cpu_to_be16(freq); + fft_sample_40.channel_type = chan_type; + + if (chan_type == NL80211_CHAN_HT40PLUS) { + lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); + upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]); + + fft_sample_40.lower_noise = ah->noise; + fft_sample_40.upper_noise = ext_nf; + } else { + lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]); + upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); + + fft_sample_40.lower_noise = ext_nf; + fft_sample_40.upper_noise = ah->noise; + } + fft_sample_40.lower_rssi = lower_rssi; + fft_sample_40.upper_rssi = upper_rssi; + + mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1; + lower_mag = spectral_max_magnitude(mag_info->lower_bins); + upper_mag = spectral_max_magnitude(mag_info->upper_bins); + fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag); + fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag); + lower_max_index = spectral_max_index(mag_info->lower_bins); + upper_max_index = spectral_max_index(mag_info->upper_bins); + fft_sample_40.lower_max_index = lower_max_index; + fft_sample_40.upper_max_index = upper_max_index; + lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins); + upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins); + fft_sample_40.lower_bitmap_weight = lower_bitmap_w; + fft_sample_40.upper_bitmap_weight = upper_bitmap_w; + fft_sample_40.max_exp = mag_info->max_exp & 0xf; + + fft_sample_40.tsf = __cpu_to_be64(tsf); + + tlv = (struct fft_sample_tlv *)&fft_sample_40; + } else { + u8 max_index, bitmap_w; + u16 magnitude; + struct ath_ht20_mag_info *mag_info; + + length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv); + fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20; + fft_sample_20.tlv.length = __cpu_to_be16(length); + fft_sample_20.freq = __cpu_to_be16(freq); + + fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]); + fft_sample_20.noise = ah->noise; + + mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1; + magnitude = spectral_max_magnitude(mag_info->all_bins); + fft_sample_20.max_magnitude = __cpu_to_be16(magnitude); + max_index = spectral_max_index(mag_info->all_bins); + fft_sample_20.max_index = max_index; + bitmap_w = spectral_bitmap_weight(mag_info->all_bins); + fft_sample_20.bitmap_weight = bitmap_w; + fft_sample_20.max_exp = mag_info->max_exp & 0xf; + + fft_sample_20.tsf = __cpu_to_be64(tsf); + + tlv = (struct fft_sample_tlv *)&fft_sample_20; + } + + ath_debug_send_fft_sample(sc, tlv); + + return 1; +} + +/*********************/ +/* spectral_scan_ctl */ +/*********************/ + +static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char *mode = ""; + unsigned int len; + + switch (sc->spectral_mode) { + case SPECTRAL_DISABLED: + mode = "disable"; + break; + case SPECTRAL_BACKGROUND: + mode = "background"; + break; + case SPECTRAL_CHANSCAN: + mode = "chanscan"; + break; + case SPECTRAL_MANUAL: + mode = "manual"; + break; + } + len = strlen(mode); + return simple_read_from_buffer(user_buf, count, ppos, mode, len); +} + +static ssize_t write_file_spec_scan_ctl(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + char buf[32]; + ssize_t len; + + if (config_enabled(CONFIG_ATH9K_TX99)) + return -EOPNOTSUPP; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + + if (strncmp("trigger", buf, 7) == 0) { + ath9k_spectral_scan_trigger(sc->hw); + } else if (strncmp("background", buf, 9) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND); + ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n"); + } else if (strncmp("chanscan", buf, 8) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN); + ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n"); + } else if (strncmp("manual", buf, 6) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL); + ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n"); + } else if (strncmp("disable", buf, 7) == 0) { + ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED); + ath_dbg(common, CONFIG, "spectral scan: disabled\n"); + } else { + return -EINVAL; + } + + return count; +} + +static const struct file_operations fops_spec_scan_ctl = { + .read = read_file_spec_scan_ctl, + .write = write_file_spec_scan_ctl, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/*************************/ +/* spectral_short_repeat */ +/*************************/ + +static ssize_t read_file_spectral_short_repeat(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.short_repeat); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_short_repeat(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 1) + return -EINVAL; + + sc->spec_config.short_repeat = val; + return count; +} + +static const struct file_operations fops_spectral_short_repeat = { + .read = read_file_spectral_short_repeat, + .write = write_file_spectral_short_repeat, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/******************/ +/* spectral_count */ +/******************/ + +static ssize_t read_file_spectral_count(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.count); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_count(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 255) + return -EINVAL; + + sc->spec_config.count = val; + return count; +} + +static const struct file_operations fops_spectral_count = { + .read = read_file_spectral_count, + .write = write_file_spectral_count, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/*******************/ +/* spectral_period */ +/*******************/ + +static ssize_t read_file_spectral_period(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.period); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_period(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 255) + return -EINVAL; + + sc->spec_config.period = val; + return count; +} + +static const struct file_operations fops_spectral_period = { + .read = read_file_spectral_period, + .write = write_file_spectral_period, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/***********************/ +/* spectral_fft_period */ +/***********************/ + +static ssize_t read_file_spectral_fft_period(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + char buf[32]; + unsigned int len; + + len = sprintf(buf, "%d\n", sc->spec_config.fft_period); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t write_file_spectral_fft_period(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 15) + return -EINVAL; + + sc->spec_config.fft_period = val; + return count; +} + +static const struct file_operations fops_spectral_fft_period = { + .read = read_file_spectral_fft_period, + .write = write_file_spectral_fft_period, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +/*******************/ +/* Relay interface */ +/*******************/ + +static struct dentry *create_buf_file_handler(const char *filename, + struct dentry *parent, + umode_t mode, + struct rchan_buf *buf, + int *is_global) +{ + struct dentry *buf_file; + + buf_file = debugfs_create_file(filename, mode, parent, buf, + &relay_file_operations); + *is_global = 1; + return buf_file; +} + +static int remove_buf_file_handler(struct dentry *dentry) +{ + debugfs_remove(dentry); + + return 0; +} + +struct rchan_callbacks rfs_spec_scan_cb = { + .create_buf_file = create_buf_file_handler, + .remove_buf_file = remove_buf_file_handler, +}; + +/*********************/ +/* Debug Init/Deinit */ +/*********************/ + +void ath9k_spectral_deinit_debug(struct ath_softc *sc) +{ + if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) { + relay_close(sc->rfs_chan_spec_scan); + sc->rfs_chan_spec_scan = NULL; + } +} + +void ath9k_spectral_init_debug(struct ath_softc *sc) +{ + sc->rfs_chan_spec_scan = relay_open("spectral_scan", + sc->debug.debugfs_phy, + 1024, 256, &rfs_spec_scan_cb, + NULL); + debugfs_create_file("spectral_scan_ctl", + S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spec_scan_ctl); + debugfs_create_file("spectral_short_repeat", + S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spectral_short_repeat); + debugfs_create_file("spectral_count", + S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spectral_count); + debugfs_create_file("spectral_period", + S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spectral_period); + debugfs_create_file("spectral_fft_period", + S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, + &fops_spectral_fft_period); +} diff --git a/drivers/net/wireless/ath/ath9k/spectral.h b/drivers/net/wireless/ath/ath9k/spectral.h new file mode 100644 index 000000000000..ead63412ee1a --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/spectral.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2013 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef SPECTRAL_H +#define SPECTRAL_H + +/* enum spectral_mode: + * + * @SPECTRAL_DISABLED: spectral mode is disabled + * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with + * something else. + * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples + * is performed manually. + * @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels + * during a channel scan. + */ +enum spectral_mode { + SPECTRAL_DISABLED = 0, + SPECTRAL_BACKGROUND, + SPECTRAL_MANUAL, + SPECTRAL_CHANSCAN, +}; + +#define SPECTRAL_SCAN_BITMASK 0x10 +/* Radar info packet format, used for DFS and spectral formats. */ +struct ath_radar_info { + u8 pulse_length_pri; + u8 pulse_length_ext; + u8 pulse_bw_info; +} __packed; + +/* The HT20 spectral data has 4 bytes of additional information at it's end. + * + * [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]} + * [7:0]: all bins max_magnitude[9:2] + * [7:0]: all bins {max_index[5:0], max_magnitude[11:10]} + * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned) + */ +struct ath_ht20_mag_info { + u8 all_bins[3]; + u8 max_exp; +} __packed; + +#define SPECTRAL_HT20_NUM_BINS 56 + +/* WARNING: don't actually use this struct! MAC may vary the amount of + * data by -1/+2. This struct is for reference only. + */ +struct ath_ht20_fft_packet { + u8 data[SPECTRAL_HT20_NUM_BINS]; + struct ath_ht20_mag_info mag_info; + struct ath_radar_info radar_info; +} __packed; + +#define SPECTRAL_HT20_TOTAL_DATA_LEN (sizeof(struct ath_ht20_fft_packet)) + +/* Dynamic 20/40 mode: + * + * [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]} + * [7:0]: lower bins max_magnitude[9:2] + * [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]} + * [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]} + * [7:0]: upper bins max_magnitude[9:2] + * [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]} + * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned) + */ +struct ath_ht20_40_mag_info { + u8 lower_bins[3]; + u8 upper_bins[3]; + u8 max_exp; +} __packed; + +#define SPECTRAL_HT20_40_NUM_BINS 128 + +/* WARNING: don't actually use this struct! MAC may vary the amount of + * data. This struct is for reference only. + */ +struct ath_ht20_40_fft_packet { + u8 data[SPECTRAL_HT20_40_NUM_BINS]; + struct ath_ht20_40_mag_info mag_info; + struct ath_radar_info radar_info; +} __packed; + + +#define SPECTRAL_HT20_40_TOTAL_DATA_LEN (sizeof(struct ath_ht20_40_fft_packet)) + +/* grabs the max magnitude from the all/upper/lower bins */ +static inline u16 spectral_max_magnitude(u8 *bins) +{ + return (bins[0] & 0xc0) >> 6 | + (bins[1] & 0xff) << 2 | + (bins[2] & 0x03) << 10; +} + +/* return the max magnitude from the all/upper/lower bins */ +static inline u8 spectral_max_index(u8 *bins) +{ + s8 m = (bins[2] & 0xfc) >> 2; + + /* TODO: this still doesn't always report the right values ... */ + if (m > 32) + m |= 0xe0; + else + m &= ~0xe0; + + return m + 29; +} + +/* return the bitmap weight from the all/upper/lower bins */ +static inline u8 spectral_bitmap_weight(u8 *bins) +{ + return bins[0] & 0x3f; +} + +/* FFT sample format given to userspace via debugfs. + * + * Please keep the type/length at the front position and change + * other fields after adding another sample type + * + * TODO: this might need rework when switching to nl80211-based + * interface. + */ +enum ath_fft_sample_type { + ATH_FFT_SAMPLE_HT20 = 1, + ATH_FFT_SAMPLE_HT20_40, +}; + +struct fft_sample_tlv { + u8 type; /* see ath_fft_sample */ + __be16 length; + /* type dependent data follows */ +} __packed; + +struct fft_sample_ht20 { + struct fft_sample_tlv tlv; + + u8 max_exp; + + __be16 freq; + s8 rssi; + s8 noise; + + __be16 max_magnitude; + u8 max_index; + u8 bitmap_weight; + + __be64 tsf; + + u8 data[SPECTRAL_HT20_NUM_BINS]; +} __packed; + +struct fft_sample_ht20_40 { + struct fft_sample_tlv tlv; + + u8 channel_type; + __be16 freq; + + s8 lower_rssi; + s8 upper_rssi; + + __be64 tsf; + + s8 lower_noise; + s8 upper_noise; + + __be16 lower_max_magnitude; + __be16 upper_max_magnitude; + + u8 lower_max_index; + u8 upper_max_index; + + u8 lower_bitmap_weight; + u8 upper_bitmap_weight; + + u8 max_exp; + + u8 data[SPECTRAL_HT20_40_NUM_BINS]; +} __packed; + +void ath9k_spectral_init_debug(struct ath_softc *sc); +void ath9k_spectral_deinit_debug(struct ath_softc *sc); + +void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw); +int ath9k_spectral_scan_config(struct ieee80211_hw *hw, + enum spectral_mode spectral_mode); + +#ifdef CONFIG_ATH9K_DEBUGFS +int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr, + struct ath_rx_status *rs, u64 tsf); +#else +static inline int ath_process_fft(struct ath_softc *sc, + struct ieee80211_hdr *hdr, + struct ath_rx_status *rs, u64 tsf) +{ + return 0; +} +#endif /* CONFIG_ATH9K_DEBUGFS */ + +#endif /* SPECTRAL_H */ diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index a907bc94800d..9d735c55a0f3 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -174,14 +174,7 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, static struct ath_atx_tid * ath_get_skb_tid(struct ath_softc *sc, struct ath_node *an, struct sk_buff *skb) { - struct ieee80211_hdr *hdr; - u8 tidno = 0; - - hdr = (struct ieee80211_hdr *) skb->data; - if (ieee80211_is_data_qos(hdr->frame_control)) - tidno = ieee80211_get_qos_ctl(hdr)[0]; - - tidno &= IEEE80211_QOS_CTL_TID_MASK; + u8 tidno = skb->priority & IEEE80211_QOS_CTL_TID_MASK; return ATH_AN_2_TID(an, tidno); } diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 8f37562e0b11..750626b0e22d 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -2060,22 +2060,28 @@ static void wcn36xx_smd_rsp_process(struct wcn36xx *wcn, void *buf, size_t len) case WCN36XX_HAL_OTA_TX_COMPL_IND: case WCN36XX_HAL_MISSED_BEACON_IND: case WCN36XX_HAL_DELETE_STA_CONTEXT_IND: - mutex_lock(&wcn->hal_ind_mutex); msg_ind = kmalloc(sizeof(*msg_ind), GFP_KERNEL); - if (msg_ind) { - msg_ind->msg_len = len; - msg_ind->msg = kmalloc(len, GFP_KERNEL); - memcpy(msg_ind->msg, buf, len); - list_add_tail(&msg_ind->list, &wcn->hal_ind_queue); - queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work); - wcn36xx_dbg(WCN36XX_DBG_HAL, "indication arrived\n"); + if (!msg_ind) + goto nomem; + msg_ind->msg_len = len; + msg_ind->msg = kmalloc(len, GFP_KERNEL); + if (!msg_ind->msg) { + kfree(msg_ind); +nomem: + /* + * FIXME: Do something smarter then just + * printing an error. + */ + wcn36xx_err("Run out of memory while handling SMD_EVENT (%d)\n", + msg_header->msg_type); + break; } + memcpy(msg_ind->msg, buf, len); + mutex_lock(&wcn->hal_ind_mutex); + list_add_tail(&msg_ind->list, &wcn->hal_ind_queue); + queue_work(wcn->hal_ind_wq, &wcn->hal_ind_work); mutex_unlock(&wcn->hal_ind_mutex); - if (msg_ind) - break; - /* FIXME: Do something smarter then just printing an error. */ - wcn36xx_err("Run out of memory while handling SMD_EVENT (%d)\n", - msg_header->msg_type); + wcn36xx_dbg(WCN36XX_DBG_HAL, "indication arrived\n"); break; default: wcn36xx_err("SMD_EVENT (%d) not supported\n", diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 2082402d4b63..5681b9862023 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -36,7 +36,6 @@ brcmfmac-objs += \ brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ dhd_sdio.o \ bcmsdh.o \ - bcmsdh_sdmmc.o \ sdio_chip.o brcmfmac-$(CONFIG_BRCMFMAC_USB) += \ usb.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c index 06848e426f9a..12c27d13df7f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcdc.c @@ -101,35 +101,41 @@ struct brcmf_proto_bcdc_header { * plus any space that might be needed * for bus alignment padding. */ -#define ROUND_UP_MARGIN 2048 /* Biggest bus block size possible for - * round off at the end of buffer - * Currently is SDIO - */ - struct brcmf_bcdc { u16 reqid; u8 bus_header[BUS_HEADER_LEN]; struct brcmf_proto_bcdc_dcmd msg; - unsigned char buf[BRCMF_DCMD_MAXLEN + ROUND_UP_MARGIN]; + unsigned char buf[BRCMF_DCMD_MAXLEN]; }; -static int brcmf_proto_bcdc_msg(struct brcmf_pub *drvr) + +static int +brcmf_proto_bcdc_msg(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, + uint len, bool set) { struct brcmf_bcdc *bcdc = (struct brcmf_bcdc *)drvr->proto->pd; - int len = le32_to_cpu(bcdc->msg.len) + - sizeof(struct brcmf_proto_bcdc_dcmd); + struct brcmf_proto_bcdc_dcmd *msg = &bcdc->msg; + u32 flags; brcmf_dbg(BCDC, "Enter\n"); - /* NOTE : bcdc->msg.len holds the desired length of the buffer to be - * returned. Only up to BCDC_MAX_MSG_SIZE of this buffer area - * is actually sent to the dongle - */ - if (len > BCDC_MAX_MSG_SIZE) - len = BCDC_MAX_MSG_SIZE; + memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd)); + + msg->cmd = cpu_to_le32(cmd); + msg->len = cpu_to_le32(len); + flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT); + if (set) + flags |= BCDC_DCMD_SET; + flags = (flags & ~BCDC_DCMD_IF_MASK) | + (ifidx << BCDC_DCMD_IF_SHIFT); + msg->flags = cpu_to_le32(flags); + + if (buf) + memcpy(bcdc->buf, buf, len); /* Send request */ - return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, len); + return brcmf_bus_txctl(drvr->bus_if, (unsigned char *)&bcdc->msg, + len + sizeof(struct brcmf_proto_bcdc_dcmd)); } static int brcmf_proto_bcdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len) @@ -161,19 +167,7 @@ brcmf_proto_bcdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len); - memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd)); - - msg->cmd = cpu_to_le32(cmd); - msg->len = cpu_to_le32(len); - flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT); - flags = (flags & ~BCDC_DCMD_IF_MASK) | - (ifidx << BCDC_DCMD_IF_SHIFT); - msg->flags = cpu_to_le32(flags); - - if (buf) - memcpy(bcdc->buf, buf, len); - - ret = brcmf_proto_bcdc_msg(drvr); + ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, false); if (ret < 0) { brcmf_err("brcmf_proto_bcdc_msg failed w/status %d\n", ret); @@ -227,19 +221,7 @@ brcmf_proto_bcdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, brcmf_dbg(BCDC, "Enter, cmd %d len %d\n", cmd, len); - memset(msg, 0, sizeof(struct brcmf_proto_bcdc_dcmd)); - - msg->cmd = cpu_to_le32(cmd); - msg->len = cpu_to_le32(len); - flags = (++bcdc->reqid << BCDC_DCMD_ID_SHIFT) | BCDC_DCMD_SET; - flags = (flags & ~BCDC_DCMD_IF_MASK) | - (ifidx << BCDC_DCMD_IF_SHIFT); - msg->flags = cpu_to_le32(flags); - - if (buf) - memcpy(bcdc->buf, buf, len); - - ret = brcmf_proto_bcdc_msg(drvr); + ret = brcmf_proto_bcdc_msg(drvr, ifidx, cmd, buf, len, true); if (ret < 0) goto done; @@ -347,6 +329,15 @@ brcmf_proto_bcdc_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, return 0; } +static int +brcmf_proto_bcdc_txdata(struct brcmf_pub *drvr, int ifidx, u8 offset, + struct sk_buff *pktbuf) +{ + brcmf_proto_bcdc_hdrpush(drvr, ifidx, offset, pktbuf); + return brcmf_bus_txdata(drvr->bus_if, pktbuf); +} + + int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { struct brcmf_bcdc *bcdc; @@ -361,15 +352,15 @@ int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) goto fail; } - drvr->proto->hdrpush = brcmf_proto_bcdc_hdrpush; drvr->proto->hdrpull = brcmf_proto_bcdc_hdrpull; drvr->proto->query_dcmd = brcmf_proto_bcdc_query_dcmd; drvr->proto->set_dcmd = brcmf_proto_bcdc_set_dcmd; + drvr->proto->txdata = brcmf_proto_bcdc_txdata; drvr->proto->pd = bcdc; drvr->hdrlen += BCDC_HEADER_LEN + BRCMF_PROT_FW_SIGNAL_MAX_TXBYTES; drvr->bus_if->maxctl = BRCMF_DCMD_MAXLEN + - sizeof(struct brcmf_proto_bcdc_dcmd) + ROUND_UP_MARGIN; + sizeof(struct brcmf_proto_bcdc_dcmd); return 0; fail: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 91651ec7f13f..2b5cde67e728 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -23,9 +23,17 @@ #include <linux/completion.h> #include <linux/scatterlist.h> #include <linux/mmc/sdio.h> +#include <linux/mmc/core.h> #include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio_ids.h> #include <linux/mmc/card.h> +#include <linux/mmc/host.h> +#include <linux/platform_device.h> #include <linux/platform_data/brcmfmac-sdio.h> +#include <linux/suspend.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <net/cfg80211.h> #include <defs.h> #include <brcm_hw_ids.h> @@ -35,11 +43,21 @@ #include "dhd_bus.h" #include "dhd_dbg.h" #include "sdio_host.h" +#include "sdio_chip.h" #define SDIOH_API_ACCESS_RETRY_LIMIT 2 +#define SDIO_VENDOR_ID_BROADCOM 0x02d0 -static irqreturn_t brcmf_sdio_oob_irqhandler(int irq, void *dev_id) +#define DMA_ALIGN_MASK 0x03 + +#define SDIO_FUNC1_BLOCKSIZE 64 +#define SDIO_FUNC2_BLOCKSIZE 512 +/* Maximum milliseconds to wait for F2 to come up */ +#define SDIO_WAIT_F2RDY 3000 + + +static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id) { struct brcmf_bus *bus_if = dev_get_drvdata(dev_id); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; @@ -54,27 +72,46 @@ static irqreturn_t brcmf_sdio_oob_irqhandler(int irq, void *dev_id) sdiodev->irq_en = false; } - brcmf_sdbrcm_isr(sdiodev->bus); + brcmf_sdio_isr(sdiodev->bus); return IRQ_HANDLED; } -static void brcmf_sdio_ib_irqhandler(struct sdio_func *func) +static void brcmf_sdiod_ib_irqhandler(struct sdio_func *func) { struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; brcmf_dbg(INTR, "IB intr triggered\n"); - brcmf_sdbrcm_isr(sdiodev->bus); + brcmf_sdio_isr(sdiodev->bus); } /* dummy handler for SDIO function 2 interrupt */ -static void brcmf_sdio_dummy_irqhandler(struct sdio_func *func) +static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func) +{ +} + +static bool brcmf_sdiod_pm_resume_error(struct brcmf_sdio_dev *sdiodev) { + bool is_err = false; +#ifdef CONFIG_PM_SLEEP + is_err = atomic_read(&sdiodev->suspend); +#endif + return is_err; } -int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) +static void brcmf_sdiod_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, + wait_queue_head_t *wq) +{ +#ifdef CONFIG_PM_SLEEP + int retry = 0; + while (atomic_read(&sdiodev->suspend) && retry++ != 30) + wait_event_timeout(*wq, false, HZ/100); +#endif +} + +int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) { int ret = 0; u8 data; @@ -84,7 +121,7 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n", sdiodev->pdata->oob_irq_nr); ret = request_irq(sdiodev->pdata->oob_irq_nr, - brcmf_sdio_oob_irqhandler, + brcmf_sdiod_oob_irqhandler, sdiodev->pdata->oob_irq_flags, "brcmf_oob_intr", &sdiodev->func[1]->dev); @@ -108,36 +145,36 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) sdio_claim_host(sdiodev->func[1]); /* must configure SDIO_CCCR_IENx to enable irq */ - data = brcmf_sdio_regrb(sdiodev, SDIO_CCCR_IENx, &ret); + data = brcmf_sdiod_regrb(sdiodev, SDIO_CCCR_IENx, &ret); data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, data, &ret); /* redirect, configure and enable io for interrupt signal */ data = SDIO_SEPINT_MASK | SDIO_SEPINT_OE; if (sdiodev->pdata->oob_irq_flags & IRQF_TRIGGER_HIGH) data |= SDIO_SEPINT_ACT_HI; - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); sdio_release_host(sdiodev->func[1]); } else { brcmf_dbg(SDIO, "Entering\n"); sdio_claim_host(sdiodev->func[1]); - sdio_claim_irq(sdiodev->func[1], brcmf_sdio_ib_irqhandler); - sdio_claim_irq(sdiodev->func[2], brcmf_sdio_dummy_irqhandler); + sdio_claim_irq(sdiodev->func[1], brcmf_sdiod_ib_irqhandler); + sdio_claim_irq(sdiodev->func[2], brcmf_sdiod_dummy_irqhandler); sdio_release_host(sdiodev->func[1]); } return 0; } -int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) +int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev) { brcmf_dbg(SDIO, "Entering\n"); if ((sdiodev->pdata) && (sdiodev->pdata->oob_irq_supported)) { sdio_claim_host(sdiodev->func[1]); - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); - brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); + brcmf_sdiod_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); sdio_release_host(sdiodev->func[1]); if (sdiodev->oob_irq_requested) { @@ -160,8 +197,117 @@ int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) return 0; } +static inline int brcmf_sdiod_f0_writeb(struct sdio_func *func, + uint regaddr, u8 byte) +{ + int err_ret; + + /* + * Can only directly write to some F0 registers. + * Handle CCCR_IENx and CCCR_ABORT command + * as a special case. + */ + if ((regaddr == SDIO_CCCR_ABORT) || + (regaddr == SDIO_CCCR_IENx)) + sdio_writeb(func, byte, regaddr, &err_ret); + else + sdio_f0_writeb(func, byte, regaddr, &err_ret); + + return err_ret; +} + +static int brcmf_sdiod_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, + uint func, uint regaddr, u8 *byte) +{ + int err_ret; + + brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr); + + brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_byte_wait); + if (brcmf_sdiod_pm_resume_error(sdiodev)) + return -EIO; + + if (rw && func == 0) { + /* handle F0 separately */ + err_ret = brcmf_sdiod_f0_writeb(sdiodev->func[func], + regaddr, *byte); + } else { + if (rw) /* CMD52 Write */ + sdio_writeb(sdiodev->func[func], *byte, regaddr, + &err_ret); + else if (func == 0) { + *byte = sdio_f0_readb(sdiodev->func[func], regaddr, + &err_ret); + } else { + *byte = sdio_readb(sdiodev->func[func], regaddr, + &err_ret); + } + } + + if (err_ret) { + /* + * SleepCSR register access can fail when + * waking up the device so reduce this noise + * in the logs. + */ + if (regaddr != SBSDIO_FUNC1_SLEEPCSR) + brcmf_err("Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", + rw ? "write" : "read", func, regaddr, *byte, + err_ret); + else + brcmf_dbg(SDIO, "Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", + rw ? "write" : "read", func, regaddr, *byte, + err_ret); + } + return err_ret; +} + +static int brcmf_sdiod_request_word(struct brcmf_sdio_dev *sdiodev, uint rw, + uint func, uint addr, u32 *word, + uint nbytes) +{ + int err_ret = -EIO; + + if (func == 0) { + brcmf_err("Only CMD52 allowed to F0\n"); + return -EINVAL; + } + + brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", + rw, func, addr, nbytes); + + brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); + if (brcmf_sdiod_pm_resume_error(sdiodev)) + return -EIO; + + if (rw) { /* CMD52 Write */ + if (nbytes == 4) + sdio_writel(sdiodev->func[func], *word, addr, + &err_ret); + else if (nbytes == 2) + sdio_writew(sdiodev->func[func], (*word & 0xFFFF), + addr, &err_ret); + else + brcmf_err("Invalid nbytes: %d\n", nbytes); + } else { /* CMD52 Read */ + if (nbytes == 4) + *word = sdio_readl(sdiodev->func[func], addr, &err_ret); + else if (nbytes == 2) + *word = sdio_readw(sdiodev->func[func], addr, + &err_ret) & 0xFFFF; + else + brcmf_err("Invalid nbytes: %d\n", nbytes); + } + + if (err_ret) + brcmf_err("Failed to %s word, Err: 0x%08x\n", + rw ? "write" : "read", err_ret); + + return err_ret; +} + static int -brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) +brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) { int err = 0, i; u8 addr[3]; @@ -176,7 +322,7 @@ brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) do { if (retry) usleep_range(1000, 2000); - err = brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, + err = brcmf_sdiod_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW + i, &addr[i]); } while (err != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); @@ -192,13 +338,13 @@ brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) } static int -brcmf_sdio_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr) +brcmf_sdiod_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr) { uint bar0 = *addr & ~SBSDIO_SB_OFT_ADDR_MASK; int err = 0; if (bar0 != sdiodev->sbwad) { - err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); + err = brcmf_sdiod_set_sbaddr_window(sdiodev, bar0); if (err) return err; @@ -213,9 +359,8 @@ brcmf_sdio_addrprep(struct brcmf_sdio_dev *sdiodev, uint width, u32 *addr) return 0; } -int -brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, - void *data, bool write) +static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, + void *data, bool write) { u8 func_num, reg_size; s32 retry = 0; @@ -237,7 +382,7 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, func_num = SDIO_FUNC_1; reg_size = 4; - ret = brcmf_sdio_addrprep(sdiodev, reg_size, &addr); + ret = brcmf_sdiod_addrprep(sdiodev, reg_size, &addr); if (ret) goto done; } @@ -248,10 +393,10 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, if (retry) /* wait for 1 ms till bus get settled down */ usleep_range(1000, 2000); if (reg_size == 1) - ret = brcmf_sdioh_request_byte(sdiodev, write, + ret = brcmf_sdiod_request_byte(sdiodev, write, func_num, addr, data); else - ret = brcmf_sdioh_request_word(sdiodev, write, + ret = brcmf_sdiod_request_word(sdiodev, write, func_num, addr, data, 4); } while (ret != 0 && retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); @@ -262,13 +407,13 @@ done: return ret; } -u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) +u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) { u8 data; int retval; brcmf_dbg(SDIO, "addr:0x%08x\n", addr); - retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, &data, false); brcmf_dbg(SDIO, "data:0x%02x\n", data); if (ret) @@ -277,13 +422,13 @@ u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) return data; } -u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) +u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) { u32 data; int retval; brcmf_dbg(SDIO, "addr:0x%08x\n", addr); - retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, &data, false); brcmf_dbg(SDIO, "data:0x%08x\n", data); if (ret) @@ -292,37 +437,37 @@ u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) return data; } -void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, +void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, int *ret) { int retval; brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data); - retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, &data, true); if (ret) *ret = retval; } -void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, +void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, int *ret) { int retval; brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data); - retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); + retval = brcmf_sdiod_regrw_helper(sdiodev, addr, &data, true); if (ret) *ret = retval; } -static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, +static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, bool write, u32 addr, struct sk_buff *pkt) { unsigned int req_sz; - brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); - if (brcmf_pm_resume_error(sdiodev)) + brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); + if (brcmf_sdiod_pm_resume_error(sdiodev)) return -EIO; /* Single skb use the standard mmc interface */ @@ -345,7 +490,7 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, } /** - * brcmf_sdio_sglist_rw - SDIO interface function for block data access + * brcmf_sdiod_sglist_rw - SDIO interface function for block data access * @sdiodev: brcmfmac sdio device * @fn: SDIO function number * @write: direction flag @@ -356,9 +501,9 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, * stack for block data access. It assumes that the skb passed down by the * caller has already been padded and aligned. */ -static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, - bool write, u32 addr, - struct sk_buff_head *pktlist) +static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, + bool write, u32 addr, + struct sk_buff_head *pktlist) { unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset; unsigned int max_req_sz, orig_offset, dst_offset; @@ -376,8 +521,8 @@ static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, if (!pktlist->qlen) return -EINVAL; - brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); - if (brcmf_pm_resume_error(sdiodev)) + brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); + if (brcmf_sdiod_pm_resume_error(sdiodev)) return -EIO; target_list = pktlist; @@ -524,9 +669,7 @@ exit: return ret; } -int -brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, u8 *buf, uint nbytes) +int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes) { struct sk_buff *mypkt; int err; @@ -538,7 +681,7 @@ brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, return -EIO; } - err = brcmf_sdcard_recv_pkt(sdiodev, addr, fn, flags, mypkt); + err = brcmf_sdiod_recv_pkt(sdiodev, mypkt); if (!err) memcpy(buf, mypkt->data, nbytes); @@ -546,50 +689,47 @@ brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, return err; } -int -brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt) +int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt) { - uint width; + u32 addr = sdiodev->sbwad; int err = 0; - brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", - fn, addr, pkt->len); + brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len); - width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - err = brcmf_sdio_addrprep(sdiodev, width, &addr); + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); if (err) goto done; - err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pkt); + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, pkt); done: return err; } -int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq, uint totlen) +int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq, uint totlen) { struct sk_buff *glom_skb; struct sk_buff *skb; - uint width; + u32 addr = sdiodev->sbwad; int err = 0; - brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", - fn, addr, pktq->qlen); + brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", + addr, pktq->qlen); - width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - err = brcmf_sdio_addrprep(sdiodev, width, &addr); + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); if (err) goto done; if (pktq->qlen == 1) - err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq->next); + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, + pktq->next); else if (!sdiodev->sg_support) { glom_skb = brcmu_pkt_buf_get_skb(totlen); if (!glom_skb) return -ENOMEM; - err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, glom_skb); + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, + glom_skb); if (err) goto done; @@ -598,18 +738,17 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, skb_pull(glom_skb, skb->len); } } else - err = brcmf_sdio_sglist_rw(sdiodev, fn, false, addr, pktq); + err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, false, addr, + pktq); done: return err; } -int -brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, u8 *buf, uint nbytes) +int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes) { struct sk_buff *mypkt; - uint width; + u32 addr = sdiodev->sbwad; int err; mypkt = brcmu_pkt_buf_get_skb(nbytes); @@ -621,48 +760,47 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, memcpy(mypkt->data, buf, nbytes); - width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - err = brcmf_sdio_addrprep(sdiodev, width, &addr); + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); if (!err) - err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, mypkt); + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, addr, + mypkt); brcmu_pkt_buf_free_skb(mypkt); return err; } -int -brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq) +int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq) { struct sk_buff *skb; - uint width; + u32 addr = sdiodev->sbwad; int err; - brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n", - fn, addr, pktq->qlen); + brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen); - width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; - err = brcmf_sdio_addrprep(sdiodev, width, &addr); + err = brcmf_sdiod_addrprep(sdiodev, 4, &addr); if (err) return err; if (pktq->qlen == 1 || !sdiodev->sg_support) skb_queue_walk(pktq, skb) { - err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, skb); + err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, + addr, skb); if (err) break; } else - err = brcmf_sdio_sglist_rw(sdiodev, fn, true, addr, pktq); + err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, true, addr, + pktq); return err; } int -brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, - u8 *data, uint size) +brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, + u8 *data, uint size) { int bcmerror = 0; struct sk_buff *pkt; @@ -689,7 +827,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, /* Do the transfer(s) */ while (size) { /* Set the backplane window to include the start address */ - bcmerror = brcmf_sdcard_set_sbaddr_window(sdiodev, address); + bcmerror = brcmf_sdiod_set_sbaddr_window(sdiodev, address); if (bcmerror) break; @@ -703,8 +841,8 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, skb_put(pkt, dsize); if (write) memcpy(pkt->data, data, dsize); - bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write, - sdaddr, pkt); + bcmerror = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_1, write, + sdaddr, pkt); if (bcmerror) { brcmf_err("membytes transfer failed\n"); break; @@ -726,7 +864,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, dev_kfree_skb(pkt); /* Return the window to backplane enumeration space for core access */ - if (brcmf_sdcard_set_sbaddr_window(sdiodev, sdiodev->sbwad)) + if (brcmf_sdiod_set_sbaddr_window(sdiodev, sdiodev->sbwad)) brcmf_err("FAILED to set window back to 0x%x\n", sdiodev->sbwad); @@ -735,65 +873,337 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, return bcmerror; } -int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn) +int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn) { char t_func = (char)fn; brcmf_dbg(SDIO, "Enter\n"); /* issue abort cmd52 command through F0 */ - brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, + brcmf_sdiod_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, SDIO_CCCR_ABORT, &t_func); brcmf_dbg(SDIO, "Exit\n"); return 0; } -int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) +static int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev) +{ + sdiodev->bus_if->state = BRCMF_BUS_DOWN; + + if (sdiodev->bus) { + brcmf_sdio_remove(sdiodev->bus); + sdiodev->bus = NULL; + } + + /* Disable Function 2 */ + sdio_claim_host(sdiodev->func[2]); + sdio_disable_func(sdiodev->func[2]); + sdio_release_host(sdiodev->func[2]); + + /* Disable Function 1 */ + sdio_claim_host(sdiodev->func[1]); + sdio_disable_func(sdiodev->func[1]); + sdio_release_host(sdiodev->func[1]); + + sdiodev->sbwad = 0; + + return 0; +} + +static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev) { - u32 regs = 0; + struct sdio_func *func; + struct mmc_host *host; + uint max_blocks; int ret = 0; - ret = brcmf_sdioh_attach(sdiodev); - if (ret) + sdiodev->num_funcs = 2; + + sdio_claim_host(sdiodev->func[1]); + + ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE); + if (ret) { + brcmf_err("Failed to set F1 blocksize\n"); + sdio_release_host(sdiodev->func[1]); + goto out; + } + ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE); + if (ret) { + brcmf_err("Failed to set F2 blocksize\n"); + sdio_release_host(sdiodev->func[1]); + goto out; + } + + /* increase F2 timeout */ + sdiodev->func[2]->enable_timeout = SDIO_WAIT_F2RDY; + + /* Enable Function 1 */ + ret = sdio_enable_func(sdiodev->func[1]); + sdio_release_host(sdiodev->func[1]); + if (ret) { + brcmf_err("Failed to enable F1: err=%d\n", ret); goto out; + } - regs = SI_ENUM_BASE; + /* + * determine host related variables after brcmf_sdiod_probe() + * as func->cur_blksize is properly set and F2 init has been + * completed successfully. + */ + func = sdiodev->func[2]; + host = func->card->host; + sdiodev->sg_support = host->max_segs > 1; + max_blocks = min_t(uint, host->max_blk_count, 511u); + sdiodev->max_request_size = min_t(uint, host->max_req_size, + max_blocks * func->cur_blksize); + sdiodev->max_segment_count = min_t(uint, host->max_segs, + SG_MAX_SINGLE_ALLOC); + sdiodev->max_segment_size = host->max_seg_size; /* try to attach to the target device */ - sdiodev->bus = brcmf_sdbrcm_probe(regs, sdiodev); + sdiodev->bus = brcmf_sdio_probe(sdiodev); if (!sdiodev->bus) { - brcmf_err("device attach failed\n"); ret = -ENODEV; goto out; } out: if (ret) - brcmf_sdio_remove(sdiodev); + brcmf_sdiod_remove(sdiodev); return ret; } -int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev) +/* devices we support, null terminated */ +static const struct sdio_device_id brcmf_sdmmc_ids[] = { + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)}, + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, + SDIO_DEVICE_ID_BROADCOM_4335_4339)}, + { /* end: all zeroes */ }, +}; +MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); + +static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; + + +static int brcmf_ops_sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) { - sdiodev->bus_if->state = BRCMF_BUS_DOWN; + int err; + struct brcmf_sdio_dev *sdiodev; + struct brcmf_bus *bus_if; - if (sdiodev->bus) { - brcmf_sdbrcm_disconnect(sdiodev->bus); - sdiodev->bus = NULL; + brcmf_dbg(SDIO, "Enter\n"); + brcmf_dbg(SDIO, "Class=%x\n", func->class); + brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); + brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); + brcmf_dbg(SDIO, "Function#: %d\n", func->num); + + /* Consume func num 1 but dont do anything with it. */ + if (func->num == 1) + return 0; + + /* Ignore anything but func 2 */ + if (func->num != 2) + return -ENODEV; + + bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); + if (!bus_if) + return -ENOMEM; + sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); + if (!sdiodev) { + kfree(bus_if); + return -ENOMEM; } - brcmf_sdioh_detach(sdiodev); + /* store refs to functions used. mmc_card does + * not hold the F0 function pointer. + */ + sdiodev->func[0] = kmemdup(func, sizeof(*func), GFP_KERNEL); + sdiodev->func[0]->num = 0; + sdiodev->func[1] = func->card->sdio_func[0]; + sdiodev->func[2] = func; + + sdiodev->bus_if = bus_if; + bus_if->bus_priv.sdio = sdiodev; + bus_if->proto_type = BRCMF_PROTO_BCDC; + dev_set_drvdata(&func->dev, bus_if); + dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); + sdiodev->dev = &sdiodev->func[1]->dev; + sdiodev->pdata = brcmfmac_sdio_pdata; + + atomic_set(&sdiodev->suspend, false); + init_waitqueue_head(&sdiodev->request_byte_wait); + init_waitqueue_head(&sdiodev->request_word_wait); + init_waitqueue_head(&sdiodev->request_buffer_wait); + + brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); + err = brcmf_sdiod_probe(sdiodev); + if (err) { + brcmf_err("F2 error, probe failed %d...\n", err); + goto fail; + } - sdiodev->sbwad = 0; + brcmf_dbg(SDIO, "F2 init completed...\n"); + return 0; + +fail: + dev_set_drvdata(&func->dev, NULL); + dev_set_drvdata(&sdiodev->func[1]->dev, NULL); + kfree(sdiodev->func[0]); + kfree(sdiodev); + kfree(bus_if); + return err; +} + +static void brcmf_ops_sdio_remove(struct sdio_func *func) +{ + struct brcmf_bus *bus_if; + struct brcmf_sdio_dev *sdiodev; + + brcmf_dbg(SDIO, "Enter\n"); + brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); + brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); + brcmf_dbg(SDIO, "Function: %d\n", func->num); + + if (func->num != 1 && func->num != 2) + return; + + bus_if = dev_get_drvdata(&func->dev); + if (bus_if) { + sdiodev = bus_if->bus_priv.sdio; + brcmf_sdiod_remove(sdiodev); + + dev_set_drvdata(&sdiodev->func[1]->dev, NULL); + dev_set_drvdata(&sdiodev->func[2]->dev, NULL); + + kfree(bus_if); + kfree(sdiodev->func[0]); + kfree(sdiodev); + } + + brcmf_dbg(SDIO, "Exit\n"); +} + +#ifdef CONFIG_PM_SLEEP +static int brcmf_ops_sdio_suspend(struct device *dev) +{ + mmc_pm_flag_t sdio_flags; + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + int ret = 0; + + brcmf_dbg(SDIO, "\n"); + + atomic_set(&sdiodev->suspend, true); + + sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]); + if (!(sdio_flags & MMC_PM_KEEP_POWER)) { + brcmf_err("Host can't keep power while suspended\n"); + return -EINVAL; + } + + ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER); + if (ret) { + brcmf_err("Failed to set pm_flags\n"); + return ret; + } + + brcmf_sdio_wd_timer(sdiodev->bus, 0); + + return ret; +} + +static int brcmf_ops_sdio_resume(struct device *dev) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + + brcmf_sdio_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); + atomic_set(&sdiodev->suspend, false); + return 0; +} + +static const struct dev_pm_ops brcmf_sdio_pm_ops = { + .suspend = brcmf_ops_sdio_suspend, + .resume = brcmf_ops_sdio_resume, +}; +#endif /* CONFIG_PM_SLEEP */ + +static struct sdio_driver brcmf_sdmmc_driver = { + .probe = brcmf_ops_sdio_probe, + .remove = brcmf_ops_sdio_remove, + .name = BRCMFMAC_SDIO_PDATA_NAME, + .id_table = brcmf_sdmmc_ids, +#ifdef CONFIG_PM_SLEEP + .drv = { + .pm = &brcmf_sdio_pm_ops, + }, +#endif /* CONFIG_PM_SLEEP */ +}; + +static int brcmf_sdio_pd_probe(struct platform_device *pdev) +{ + brcmf_dbg(SDIO, "Enter\n"); + + brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev); + + if (brcmfmac_sdio_pdata->power_on) + brcmfmac_sdio_pdata->power_on(); + + return 0; +} + +static int brcmf_sdio_pd_remove(struct platform_device *pdev) +{ + brcmf_dbg(SDIO, "Enter\n"); + + if (brcmfmac_sdio_pdata->power_off) + brcmfmac_sdio_pdata->power_off(); + + sdio_unregister_driver(&brcmf_sdmmc_driver); return 0; } -void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable) +static struct platform_driver brcmf_sdio_pd = { + .remove = brcmf_sdio_pd_remove, + .driver = { + .name = BRCMFMAC_SDIO_PDATA_NAME, + .owner = THIS_MODULE, + } +}; + +void brcmf_sdio_register(void) +{ + int ret; + + ret = sdio_register_driver(&brcmf_sdmmc_driver); + if (ret) + brcmf_err("sdio_register_driver failed: %d\n", ret); +} + +void brcmf_sdio_exit(void) { - if (enable) - brcmf_sdbrcm_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS); + brcmf_dbg(SDIO, "Enter\n"); + + if (brcmfmac_sdio_pdata) + platform_driver_unregister(&brcmf_sdio_pd); else - brcmf_sdbrcm_wd_timer(sdiodev->bus, 0); + sdio_unregister_driver(&brcmf_sdmmc_driver); +} + +void __init brcmf_sdio_init(void) +{ + int ret; + + brcmf_dbg(SDIO, "Enter\n"); + + ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe); + if (ret == -ENODEV) + brcmf_dbg(SDIO, "No platform data available.\n"); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c deleted file mode 100644 index a511c27122b8..000000000000 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ /dev/null @@ -1,552 +0,0 @@ -/* - * 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. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <linux/types.h> -#include <linux/netdevice.h> -#include <linux/mmc/sdio.h> -#include <linux/mmc/core.h> -#include <linux/mmc/sdio_func.h> -#include <linux/mmc/sdio_ids.h> -#include <linux/mmc/card.h> -#include <linux/mmc/host.h> -#include <linux/suspend.h> -#include <linux/errno.h> -#include <linux/sched.h> /* request_irq() */ -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/platform_data/brcmfmac-sdio.h> -#include <net/cfg80211.h> - -#include <defs.h> -#include <brcm_hw_ids.h> -#include <brcmu_utils.h> -#include <brcmu_wifi.h> -#include "sdio_host.h" -#include "sdio_chip.h" -#include "dhd_dbg.h" -#include "dhd_bus.h" - -#define SDIO_VENDOR_ID_BROADCOM 0x02d0 - -#define DMA_ALIGN_MASK 0x03 - -#define SDIO_FUNC1_BLOCKSIZE 64 -#define SDIO_FUNC2_BLOCKSIZE 512 - -/* devices we support, null terminated */ -static const struct sdio_device_id brcmf_sdmmc_ids[] = { - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, - SDIO_DEVICE_ID_BROADCOM_4335_4339)}, - { /* end: all zeroes */ }, -}; -MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); - -static struct brcmfmac_sdio_platform_data *brcmfmac_sdio_pdata; - - -bool -brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev) -{ - bool is_err = false; -#ifdef CONFIG_PM_SLEEP - is_err = atomic_read(&sdiodev->suspend); -#endif - return is_err; -} - -void -brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, wait_queue_head_t *wq) -{ -#ifdef CONFIG_PM_SLEEP - int retry = 0; - while (atomic_read(&sdiodev->suspend) && retry++ != 30) - wait_event_timeout(*wq, false, HZ/100); -#endif -} - -static inline int brcmf_sdioh_f0_write_byte(struct brcmf_sdio_dev *sdiodev, - uint regaddr, u8 *byte) -{ - struct sdio_func *sdfunc = sdiodev->func[0]; - int err_ret; - - /* - * Can only directly write to some F0 registers. - * Handle F2 enable/disable and Abort command - * as a special case. - */ - if (regaddr == SDIO_CCCR_IOEx) { - sdfunc = sdiodev->func[2]; - if (sdfunc) { - if (*byte & SDIO_FUNC_ENABLE_2) { - /* Enable Function 2 */ - err_ret = sdio_enable_func(sdfunc); - if (err_ret) - brcmf_err("enable F2 failed:%d\n", - err_ret); - } else { - /* Disable Function 2 */ - err_ret = sdio_disable_func(sdfunc); - if (err_ret) - brcmf_err("Disable F2 failed:%d\n", - err_ret); - } - } else { - err_ret = -ENOENT; - } - } else if ((regaddr == SDIO_CCCR_ABORT) || - (regaddr == SDIO_CCCR_IENx)) { - sdfunc = kmemdup(sdiodev->func[0], sizeof(struct sdio_func), - GFP_KERNEL); - if (!sdfunc) - return -ENOMEM; - sdfunc->num = 0; - sdio_writeb(sdfunc, *byte, regaddr, &err_ret); - kfree(sdfunc); - } else if (regaddr < 0xF0) { - brcmf_err("F0 Wr:0x%02x: write disallowed\n", regaddr); - err_ret = -EPERM; - } else { - sdio_f0_writeb(sdfunc, *byte, regaddr, &err_ret); - } - - return err_ret; -} - -int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint func, - uint regaddr, u8 *byte) -{ - int err_ret; - - brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x\n", rw, func, regaddr); - - brcmf_pm_resume_wait(sdiodev, &sdiodev->request_byte_wait); - if (brcmf_pm_resume_error(sdiodev)) - return -EIO; - - if (rw && func == 0) { - /* handle F0 separately */ - err_ret = brcmf_sdioh_f0_write_byte(sdiodev, regaddr, byte); - } else { - if (rw) /* CMD52 Write */ - sdio_writeb(sdiodev->func[func], *byte, regaddr, - &err_ret); - else if (func == 0) { - *byte = sdio_f0_readb(sdiodev->func[func], regaddr, - &err_ret); - } else { - *byte = sdio_readb(sdiodev->func[func], regaddr, - &err_ret); - } - } - - if (err_ret) { - /* - * SleepCSR register access can fail when - * waking up the device so reduce this noise - * in the logs. - */ - if (regaddr != SBSDIO_FUNC1_SLEEPCSR) - brcmf_err("Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", - rw ? "write" : "read", func, regaddr, *byte, - err_ret); - else - brcmf_dbg(SDIO, "Failed to %s byte F%d:@0x%05x=%02x, Err: %d\n", - rw ? "write" : "read", func, regaddr, *byte, - err_ret); - } - return err_ret; -} - -int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, - uint rw, uint func, uint addr, u32 *word, - uint nbytes) -{ - int err_ret = -EIO; - - if (func == 0) { - brcmf_err("Only CMD52 allowed to F0\n"); - return -EINVAL; - } - - brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", - rw, func, addr, nbytes); - - brcmf_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); - if (brcmf_pm_resume_error(sdiodev)) - return -EIO; - - if (rw) { /* CMD52 Write */ - if (nbytes == 4) - sdio_writel(sdiodev->func[func], *word, addr, - &err_ret); - else if (nbytes == 2) - sdio_writew(sdiodev->func[func], (*word & 0xFFFF), - addr, &err_ret); - else - brcmf_err("Invalid nbytes: %d\n", nbytes); - } else { /* CMD52 Read */ - if (nbytes == 4) - *word = sdio_readl(sdiodev->func[func], addr, &err_ret); - else if (nbytes == 2) - *word = sdio_readw(sdiodev->func[func], addr, - &err_ret) & 0xFFFF; - else - brcmf_err("Invalid nbytes: %d\n", nbytes); - } - - if (err_ret) - brcmf_err("Failed to %s word, Err: 0x%08x\n", - rw ? "write" : "read", err_ret); - - return err_ret; -} - -static int brcmf_sdioh_get_cisaddr(struct brcmf_sdio_dev *sdiodev, u32 regaddr) -{ - /* read 24 bits and return valid 17 bit addr */ - int i, ret; - u32 scratch, regdata; - __le32 scratch_le; - u8 *ptr = (u8 *)&scratch_le; - - for (i = 0; i < 3; i++) { - regdata = brcmf_sdio_regrl(sdiodev, regaddr, &ret); - if (ret != 0) - brcmf_err("Can't read!\n"); - - *ptr++ = (u8) regdata; - regaddr++; - } - - /* Only the lower 17-bits are valid */ - scratch = le32_to_cpu(scratch_le); - scratch &= 0x0001FFFF; - return scratch; -} - -static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev) -{ - int err_ret; - u32 fbraddr; - u8 func; - - brcmf_dbg(SDIO, "\n"); - - /* Get the Card's common CIS address */ - sdiodev->func_cis_ptr[0] = brcmf_sdioh_get_cisaddr(sdiodev, - SDIO_CCCR_CIS); - brcmf_dbg(SDIO, "Card's Common CIS Ptr = 0x%x\n", - sdiodev->func_cis_ptr[0]); - - /* Get the Card's function CIS (for each function) */ - for (fbraddr = SDIO_FBR_BASE(1), func = 1; - func <= sdiodev->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) { - sdiodev->func_cis_ptr[func] = - brcmf_sdioh_get_cisaddr(sdiodev, SDIO_FBR_CIS + fbraddr); - brcmf_dbg(SDIO, "Function %d CIS Ptr = 0x%x\n", - func, sdiodev->func_cis_ptr[func]); - } - - /* Enable Function 1 */ - err_ret = sdio_enable_func(sdiodev->func[1]); - if (err_ret) - brcmf_err("Failed to enable F1 Err: 0x%08x\n", err_ret); - - return false; -} - -/* - * Public entry points & extern's - */ -int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev) -{ - int err_ret = 0; - struct mmc_host *host; - struct sdio_func *func; - uint max_blocks; - - brcmf_dbg(SDIO, "\n"); - - sdiodev->num_funcs = 2; - - sdio_claim_host(sdiodev->func[1]); - - err_ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE); - if (err_ret) { - brcmf_err("Failed to set F1 blocksize\n"); - goto out; - } - - err_ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE); - if (err_ret) { - brcmf_err("Failed to set F2 blocksize\n"); - goto out; - } - - brcmf_sdioh_enablefuncs(sdiodev); - - /* - * determine host related variables after brcmf_sdio_probe() - * as func->cur_blksize is properly set and F2 init has been - * completed successfully. - */ - func = sdiodev->func[2]; - host = func->card->host; - sdiodev->sg_support = host->max_segs > 1; - max_blocks = min_t(uint, host->max_blk_count, 511u); - sdiodev->max_request_size = min_t(uint, host->max_req_size, - max_blocks * func->cur_blksize); - sdiodev->max_segment_count = min_t(uint, host->max_segs, - SG_MAX_SINGLE_ALLOC); - sdiodev->max_segment_size = host->max_seg_size; -out: - sdio_release_host(sdiodev->func[1]); - brcmf_dbg(SDIO, "Done\n"); - return err_ret; -} - -void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev) -{ - brcmf_dbg(SDIO, "\n"); - - /* Disable Function 2 */ - sdio_claim_host(sdiodev->func[2]); - sdio_disable_func(sdiodev->func[2]); - sdio_release_host(sdiodev->func[2]); - - /* Disable Function 1 */ - sdio_claim_host(sdiodev->func[1]); - sdio_disable_func(sdiodev->func[1]); - sdio_release_host(sdiodev->func[1]); - -} - -static int brcmf_ops_sdio_probe(struct sdio_func *func, - const struct sdio_device_id *id) -{ - int err; - struct brcmf_sdio_dev *sdiodev; - struct brcmf_bus *bus_if; - - brcmf_dbg(SDIO, "Enter\n"); - brcmf_dbg(SDIO, "Class=%x\n", func->class); - brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); - brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); - brcmf_dbg(SDIO, "Function#: %d\n", func->num); - - /* Consume func num 1 but dont do anything with it. */ - if (func->num == 1) - return 0; - - /* Ignore anything but func 2 */ - if (func->num != 2) - return -ENODEV; - - bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); - if (!bus_if) - return -ENOMEM; - sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); - if (!sdiodev) { - kfree(bus_if); - return -ENOMEM; - } - - sdiodev->func[0] = func->card->sdio_func[0]; - sdiodev->func[1] = func->card->sdio_func[0]; - sdiodev->func[2] = func; - - sdiodev->bus_if = bus_if; - bus_if->bus_priv.sdio = sdiodev; - dev_set_drvdata(&func->dev, bus_if); - dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); - sdiodev->dev = &sdiodev->func[1]->dev; - sdiodev->pdata = brcmfmac_sdio_pdata; - - atomic_set(&sdiodev->suspend, false); - init_waitqueue_head(&sdiodev->request_byte_wait); - init_waitqueue_head(&sdiodev->request_word_wait); - init_waitqueue_head(&sdiodev->request_buffer_wait); - - brcmf_dbg(SDIO, "F2 found, calling brcmf_sdio_probe...\n"); - err = brcmf_sdio_probe(sdiodev); - if (err) { - brcmf_err("F2 error, probe failed %d...\n", err); - goto fail; - } - - brcmf_dbg(SDIO, "F2 init completed...\n"); - return 0; - -fail: - dev_set_drvdata(&func->dev, NULL); - dev_set_drvdata(&sdiodev->func[1]->dev, NULL); - kfree(sdiodev); - kfree(bus_if); - return err; -} - -static void brcmf_ops_sdio_remove(struct sdio_func *func) -{ - struct brcmf_bus *bus_if; - struct brcmf_sdio_dev *sdiodev; - - brcmf_dbg(SDIO, "Enter\n"); - brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor); - brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device); - brcmf_dbg(SDIO, "Function: %d\n", func->num); - - if (func->num != 1 && func->num != 2) - return; - - bus_if = dev_get_drvdata(&func->dev); - if (bus_if) { - sdiodev = bus_if->bus_priv.sdio; - brcmf_sdio_remove(sdiodev); - - dev_set_drvdata(&sdiodev->func[1]->dev, NULL); - dev_set_drvdata(&sdiodev->func[2]->dev, NULL); - - kfree(bus_if); - kfree(sdiodev); - } - - brcmf_dbg(SDIO, "Exit\n"); -} - -#ifdef CONFIG_PM_SLEEP -static int brcmf_sdio_suspend(struct device *dev) -{ - mmc_pm_flag_t sdio_flags; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - int ret = 0; - - brcmf_dbg(SDIO, "\n"); - - atomic_set(&sdiodev->suspend, true); - - sdio_flags = sdio_get_host_pm_caps(sdiodev->func[1]); - if (!(sdio_flags & MMC_PM_KEEP_POWER)) { - brcmf_err("Host can't keep power while suspended\n"); - return -EINVAL; - } - - ret = sdio_set_host_pm_flags(sdiodev->func[1], MMC_PM_KEEP_POWER); - if (ret) { - brcmf_err("Failed to set pm_flags\n"); - return ret; - } - - brcmf_sdio_wdtmr_enable(sdiodev, false); - - return ret; -} - -static int brcmf_sdio_resume(struct device *dev) -{ - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; - - brcmf_sdio_wdtmr_enable(sdiodev, true); - atomic_set(&sdiodev->suspend, false); - return 0; -} - -static const struct dev_pm_ops brcmf_sdio_pm_ops = { - .suspend = brcmf_sdio_suspend, - .resume = brcmf_sdio_resume, -}; -#endif /* CONFIG_PM_SLEEP */ - -static struct sdio_driver brcmf_sdmmc_driver = { - .probe = brcmf_ops_sdio_probe, - .remove = brcmf_ops_sdio_remove, - .name = BRCMFMAC_SDIO_PDATA_NAME, - .id_table = brcmf_sdmmc_ids, -#ifdef CONFIG_PM_SLEEP - .drv = { - .pm = &brcmf_sdio_pm_ops, - }, -#endif /* CONFIG_PM_SLEEP */ -}; - -static int brcmf_sdio_pd_probe(struct platform_device *pdev) -{ - brcmf_dbg(SDIO, "Enter\n"); - - brcmfmac_sdio_pdata = dev_get_platdata(&pdev->dev); - - if (brcmfmac_sdio_pdata->power_on) - brcmfmac_sdio_pdata->power_on(); - - return 0; -} - -static int brcmf_sdio_pd_remove(struct platform_device *pdev) -{ - brcmf_dbg(SDIO, "Enter\n"); - - if (brcmfmac_sdio_pdata->power_off) - brcmfmac_sdio_pdata->power_off(); - - sdio_unregister_driver(&brcmf_sdmmc_driver); - - return 0; -} - -static struct platform_driver brcmf_sdio_pd = { - .remove = brcmf_sdio_pd_remove, - .driver = { - .name = BRCMFMAC_SDIO_PDATA_NAME, - .owner = THIS_MODULE, - } -}; - -void brcmf_sdio_register(void) -{ - int ret; - - ret = sdio_register_driver(&brcmf_sdmmc_driver); - if (ret) - brcmf_err("sdio_register_driver failed: %d\n", ret); -} - -void brcmf_sdio_exit(void) -{ - brcmf_dbg(SDIO, "Enter\n"); - - if (brcmfmac_sdio_pdata) - platform_driver_unregister(&brcmf_sdio_pd); - else - sdio_unregister_driver(&brcmf_sdmmc_driver); -} - -void __init brcmf_sdio_init(void) -{ - int ret; - - brcmf_dbg(SDIO, "Enter\n"); - - ret = platform_driver_probe(&brcmf_sdio_pd, brcmf_sdio_pd_probe); - if (ret == -ENODEV) - brcmf_dbg(SDIO, "No platform data available.\n"); -} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 6a54905528be..5c12a07673fa 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -24,6 +24,12 @@ enum brcmf_bus_state { BRCMF_BUS_DATA /* Ready for frame transfers */ }; +/* The level of bus communication with the dongle */ +enum brcmf_bus_protocol_type { + BRCMF_PROTO_BCDC, + BRCMF_PROTO_MSGBUF +}; + struct brcmf_bus_dcmd { char *name; char *param; @@ -65,6 +71,7 @@ struct brcmf_bus_ops { * struct brcmf_bus - interface structure between common and bus layer * * @bus_priv: pointer to private bus device. + * @proto_type: protocol type, bcdc or msgbuf * @dev: device pointer of bus device. * @drvr: public driver information. * @state: operational state of the bus interface. @@ -80,6 +87,7 @@ struct brcmf_bus { struct brcmf_sdio_dev *sdio; struct brcmf_usbdev *usb; } bus_priv; + enum brcmf_bus_protocol_type proto_type; struct device *dev; struct brcmf_pub *drvr; enum brcmf_bus_state state; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 0f95f3e79c10..f214510e3bee 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -260,9 +260,6 @@ struct rte_console { #define MAX_HDR_READ (1 << 6) #define MAX_RX_DATASZ 2048 -/* Maximum milliseconds to wait for F2 to come up */ -#define BRCMF_WAIT_F2RDY 3000 - /* Bump up limit on waiting for HT to account for first startup; * if the image is doing a CRC calculation before programming the PMU * for HT availability, it could take a couple hundred ms more, so @@ -559,7 +556,7 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { }; -static const struct firmware *brcmf_sdbrcm_get_fw(struct brcmf_sdio *bus, +static const struct firmware *brcmf_sdio_get_fw(struct brcmf_sdio *bus, enum brcmf_firmware_type type) { const struct firmware *fw; @@ -624,8 +621,8 @@ r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); int ret; - *regvar = brcmf_sdio_regrl(bus->sdiodev, - bus->ci->c_inf[idx].base + offset, &ret); + *regvar = brcmf_sdiod_regrl(bus->sdiodev, + bus->ci->c_inf[idx].base + offset, &ret); return ret; } @@ -636,15 +633,15 @@ w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); int ret; - brcmf_sdio_regwl(bus->sdiodev, - bus->ci->c_inf[idx].base + reg_offset, - regval, &ret); + brcmf_sdiod_regwl(bus->sdiodev, + bus->ci->c_inf[idx].base + reg_offset, + regval, &ret); return ret; } static int -brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on) +brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on) { u8 wr_val = 0, rd_val, cmp_val, bmask; int err = 0; @@ -654,8 +651,8 @@ brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on) wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); /* 1st KSO write goes to AOS wake up core if device is asleep */ - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - wr_val, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + wr_val, &err); if (err) { brcmf_err("SDIO_AOS KSO write error: %d\n", err); return err; @@ -685,15 +682,15 @@ brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on) * just one write attempt may fail, * read it back until it matches written value */ - rd_val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - &err); + rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + &err); if (((rd_val & bmask) == cmp_val) && !err) break; brcmf_dbg(SDIO, "KSO wr/rd retry:%d (max: %d) ERR:%x\n", try_cnt, MAX_KSO_ATTEMPTS, err); udelay(KSO_WAIT_US); - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - wr_val, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + wr_val, &err); } while (try_cnt++ < MAX_KSO_ATTEMPTS); return err; @@ -704,7 +701,7 @@ brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on) #define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE) /* Turn backplane clock on or off */ -static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) +static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok) { int err; u8 clkctl, clkreq, devctl; @@ -724,16 +721,16 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) clkreq = bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - clkreq, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + clkreq, &err); if (err) { brcmf_err("HT Avail request error: %d\n", err); return -EBADE; } /* Check current status */ - clkctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); if (err) { brcmf_err("HT Avail read error: %d\n", err); return -EBADE; @@ -742,8 +739,8 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) /* Go to pending and await interrupt if appropriate */ if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { /* Allow only clock-available interrupt */ - devctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); if (err) { brcmf_err("Devctl error setting CA: %d\n", err); @@ -751,28 +748,28 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) } devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); brcmf_dbg(SDIO, "CLKCTL: set PENDING\n"); bus->clkstate = CLK_PENDING; return 0; } else if (bus->clkstate == CLK_PENDING) { /* Cancel CA-only interrupt filter */ - devctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); } /* Otherwise, wait here (polling) for HT Avail */ timeout = jiffies + msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000); while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { - clkctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, - &err); + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, + &err); if (time_after(jiffies, timeout)) break; else @@ -805,16 +802,16 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) if (bus->clkstate == CLK_PENDING) { /* Cancel CA-only interrupt filter */ - devctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); } bus->clkstate = CLK_SDONLY; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - clkreq, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + clkreq, &err); brcmf_dbg(SDIO, "CLKCTL: turned OFF\n"); if (err) { brcmf_err("Failed access turning clock off: %d\n", @@ -826,7 +823,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) } /* Change idle/active SD state */ -static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on) +static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on) { brcmf_dbg(SDIO, "Enter\n"); @@ -839,7 +836,7 @@ static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on) } /* Transition SD and backplane clock readiness */ -static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) +static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) { #ifdef DEBUG uint oldstate = bus->clkstate; @@ -850,7 +847,7 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) /* Early exit if we're already there */ if (bus->clkstate == target) { if (target == CLK_AVAIL) { - brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); bus->activity = true; } return 0; @@ -860,32 +857,32 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) case CLK_AVAIL: /* Make sure SD clock is available */ if (bus->clkstate == CLK_NONE) - brcmf_sdbrcm_sdclk(bus, true); + brcmf_sdio_sdclk(bus, true); /* Now request HT Avail on the backplane */ - brcmf_sdbrcm_htclk(bus, true, pendok); - brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_htclk(bus, true, pendok); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); bus->activity = true; break; case CLK_SDONLY: /* Remove HT request, or bring up SD clock */ if (bus->clkstate == CLK_NONE) - brcmf_sdbrcm_sdclk(bus, true); + brcmf_sdio_sdclk(bus, true); else if (bus->clkstate == CLK_AVAIL) - brcmf_sdbrcm_htclk(bus, false, false); + brcmf_sdio_htclk(bus, false, false); else brcmf_err("request for %d -> %d\n", bus->clkstate, target); - brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); break; case CLK_NONE: /* Make sure to remove HT request */ if (bus->clkstate == CLK_AVAIL) - brcmf_sdbrcm_htclk(bus, false, false); + brcmf_sdio_htclk(bus, false, false); /* Now remove the SD clock */ - brcmf_sdbrcm_sdclk(bus, false); - brcmf_sdbrcm_wd_timer(bus, 0); + brcmf_sdio_sdclk(bus, false); + brcmf_sdio_wd_timer(bus, 0); break; } #ifdef DEBUG @@ -896,7 +893,7 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) } static int -brcmf_sdbrcm_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) +brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) { int err = 0; brcmf_dbg(TRACE, "Enter\n"); @@ -919,13 +916,13 @@ brcmf_sdbrcm_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && data_ok(bus))) return -EBUSY; - err = brcmf_sdbrcm_kso_control(bus, false); + err = brcmf_sdio_kso_control(bus, false); /* disable watchdog */ if (!err) - brcmf_sdbrcm_wd_timer(bus, 0); + brcmf_sdio_wd_timer(bus, 0); } else { bus->idlecount = 0; - err = brcmf_sdbrcm_kso_control(bus, true); + err = brcmf_sdio_kso_control(bus, true); } if (!err) { /* Change state */ @@ -943,16 +940,16 @@ end: /* control clocks */ if (sleep) { if (!bus->sr_enabled) - brcmf_sdbrcm_clkctl(bus, CLK_NONE, pendok); + brcmf_sdio_clkctl(bus, CLK_NONE, pendok); } else { - brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, pendok); + brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); } return err; } -static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) +static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus) { u32 intstatus = 0; u32 hmb_data; @@ -1028,7 +1025,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) return intstatus; } -static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) +static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) { uint retries = 0; u16 lastrbc; @@ -1040,18 +1037,18 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) rtx ? ", send NAK" : ""); if (abort) - brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); + brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_RF_TERM, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, + SFC_RF_TERM, &err); bus->sdcnt.f1regdata++; /* Wait until the packet has been flushed (device/FIFO stable) */ for (lastrbc = retries = 0xffff; retries > 0; retries--) { - hi = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_RFRAMEBCHI, &err); - lo = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_RFRAMEBCLO, &err); + hi = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_RFRAMEBCHI, &err); + lo = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_RFRAMEBCLO, &err); bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) @@ -1088,7 +1085,7 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) } /* return total length of buffer chain */ -static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus) +static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus) { struct sk_buff *p; uint total; @@ -1099,7 +1096,7 @@ static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus) return total; } -static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) +static void brcmf_sdio_free_glom(struct brcmf_sdio *bus) { struct sk_buff *cur, *next; @@ -1187,7 +1184,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, if ((u16)(~(len ^ checksum))) { brcmf_err("HW header checksum error\n"); bus->sdcnt.rx_badhdr++; - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); return -EIO; } if (len < SDPCM_HDRLEN) { @@ -1219,7 +1216,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, type != BRCMF_SDIO_FT_SUPER) { brcmf_err("HW header length too long\n"); bus->sdcnt.rx_toolong++; - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); rd->len = 0; return -EPROTO; } @@ -1238,7 +1235,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { brcmf_err("seq %d: bad data offset\n", rx_seq); bus->sdcnt.rx_badhdr++; - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); rd->len = 0; return -ENXIO; } @@ -1311,7 +1308,7 @@ static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header, trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header); } -static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) +static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq) { u16 dlen, totlen; u8 *dptr, num = 0; @@ -1391,7 +1388,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) } pfirst = pnext = NULL; } else { - brcmf_sdbrcm_free_glom(bus); + brcmf_sdio_free_glom(bus); num = 0; } @@ -1414,16 +1411,15 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) } pfirst = skb_peek(&bus->glom); - dlen = (u16) brcmf_sdbrcm_glom_len(bus); + dlen = (u16) brcmf_sdio_glom_len(bus); /* Do an SDIO read for the superframe. Configurable iovar to * read directly into the chained packet, or allocate a large * packet and and copy into the chain. */ sdio_claim_host(bus->sdiodev->func[1]); - errcode = brcmf_sdcard_recv_chain(bus->sdiodev, - bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, &bus->glom, dlen); + errcode = brcmf_sdiod_recv_chain(bus->sdiodev, + &bus->glom, dlen); sdio_release_host(bus->sdiodev->func[1]); bus->sdcnt.f2rxdata++; @@ -1434,12 +1430,12 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) sdio_claim_host(bus->sdiodev->func[1]); if (bus->glomerr++ < 3) { - brcmf_sdbrcm_rxfail(bus, true, true); + brcmf_sdio_rxfail(bus, true, true); } else { bus->glomerr = 0; - brcmf_sdbrcm_rxfail(bus, true, false); + brcmf_sdio_rxfail(bus, true, false); bus->sdcnt.rxglomfail++; - brcmf_sdbrcm_free_glom(bus); + brcmf_sdio_free_glom(bus); } sdio_release_host(bus->sdiodev->func[1]); return 0; @@ -1487,12 +1483,12 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) if (bus->glomerr++ < 3) { /* Restore superframe header space */ skb_push(pfirst, sfdoff); - brcmf_sdbrcm_rxfail(bus, true, true); + brcmf_sdio_rxfail(bus, true, true); } else { bus->glomerr = 0; - brcmf_sdbrcm_rxfail(bus, true, false); + brcmf_sdio_rxfail(bus, true, false); bus->sdcnt.rxglomfail++; - brcmf_sdbrcm_free_glom(bus); + brcmf_sdio_free_glom(bus); } sdio_release_host(bus->sdiodev->func[1]); bus->cur_read.len = 0; @@ -1536,8 +1532,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) return num; } -static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, - bool *pending) +static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, + bool *pending) { DECLARE_WAITQUEUE(wait, current); int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); @@ -1558,7 +1554,7 @@ static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, return timeout; } -static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus) +static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus) { if (waitqueue_active(&bus->dcmd_resp_wait)) wake_up_interruptible(&bus->dcmd_resp_wait); @@ -1566,7 +1562,7 @@ static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus) return 0; } static void -brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) +brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) { uint rdlen, pad; u8 *buf = NULL, *rbuf; @@ -1604,7 +1600,7 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) { brcmf_err("%d-byte control read exceeds %d-byte buffer\n", rdlen, bus->sdiodev->bus_if->maxctl); - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); goto done; } @@ -1612,15 +1608,12 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", len, len - doff, bus->sdiodev->bus_if->maxctl); bus->sdcnt.rx_toolong++; - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); goto done; } /* Read remain of frame body */ - sdret = brcmf_sdcard_recv_buf(bus->sdiodev, - bus->sdiodev->sbwad, - SDIO_FUNC_2, - F2SYNC, rbuf, rdlen); + sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen); bus->sdcnt.f2rxdata++; /* Control frame failures need retransmission */ @@ -1628,7 +1621,7 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) brcmf_err("read %d control bytes failed: %d\n", rdlen, sdret); bus->sdcnt.rxc_errors++; - brcmf_sdbrcm_rxfail(bus, true, true); + brcmf_sdio_rxfail(bus, true, true); goto done; } else memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen); @@ -1653,11 +1646,11 @@ gotpkt: done: /* Awake any waiters */ - brcmf_sdbrcm_dcmd_resp_wake(bus); + brcmf_sdio_dcmd_resp_wake(bus); } /* Pad read to blocksize for efficiency */ -static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) +static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) { if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) { *pad = bus->blocksize - (*rdlen % bus->blocksize); @@ -1694,7 +1687,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) u8 cnt; brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n", bus->glomd, skb_peek(&bus->glom)); - cnt = brcmf_sdbrcm_rxglom(bus, rd->seq_num); + cnt = brcmf_sdio_rxglom(bus, rd->seq_num); brcmf_dbg(GLOM, "rxglom returned %d\n", cnt); rd->seq_num += cnt - 1; rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; @@ -1705,17 +1698,14 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) /* read header first for unknow frame length */ sdio_claim_host(bus->sdiodev->func[1]); if (!rd->len) { - ret = brcmf_sdcard_recv_buf(bus->sdiodev, - bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, - bus->rxhdr, - BRCMF_FIRSTREAD); + ret = brcmf_sdiod_recv_buf(bus->sdiodev, + bus->rxhdr, BRCMF_FIRSTREAD); bus->sdcnt.f2rxhdrs++; if (ret < 0) { brcmf_err("RXHEADER FAILED: %d\n", ret); bus->sdcnt.rx_hdrfail++; - brcmf_sdbrcm_rxfail(bus, true, true); + brcmf_sdio_rxfail(bus, true, true); sdio_release_host(bus->sdiodev->func[1]); continue; } @@ -1734,9 +1724,9 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) } if (rd->channel == SDPCM_CONTROL_CHANNEL) { - brcmf_sdbrcm_read_control(bus, bus->rxhdr, - rd->len, - rd->dat_offset); + brcmf_sdio_read_control(bus, bus->rxhdr, + rd->len, + rd->dat_offset); /* prepare the descriptor for the next read */ rd->len = rd->len_nxtfrm << 4; rd->len_nxtfrm = 0; @@ -1750,14 +1740,14 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) head_read = BRCMF_FIRSTREAD; } - brcmf_pad(bus, &pad, &rd->len_left); + brcmf_sdio_pad(bus, &pad, &rd->len_left); pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read + bus->head_align); if (!pkt) { /* Give up on data, request rtx of events */ brcmf_err("brcmu_pkt_buf_get_skb failed\n"); - brcmf_sdbrcm_rxfail(bus, false, + brcmf_sdio_rxfail(bus, false, RETRYCHAN(rd->channel)); sdio_release_host(bus->sdiodev->func[1]); continue; @@ -1765,8 +1755,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) skb_pull(pkt, head_read); pkt_align(pkt, rd->len_left, bus->head_align); - ret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, pkt); + ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt); bus->sdcnt.f2rxdata++; sdio_release_host(bus->sdiodev->func[1]); @@ -1775,7 +1764,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) rd->len, rd->channel, ret); brcmu_pkt_buf_free_skb(pkt); sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_rxfail(bus, true, + brcmf_sdio_rxfail(bus, true, RETRYCHAN(rd->channel)); sdio_release_host(bus->sdiodev->func[1]); continue; @@ -1800,7 +1789,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) rd->len, roundup(rd_new.len, 16) >> 4); rd->len = 0; - brcmf_sdbrcm_rxfail(bus, true, true); + brcmf_sdio_rxfail(bus, true, true); sdio_release_host(bus->sdiodev->func[1]); brcmu_pkt_buf_free_skb(pkt); continue; @@ -1822,7 +1811,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) /* Force retry w/normal header read */ rd->len = 0; sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_rxfail(bus, false, true); + brcmf_sdio_rxfail(bus, false, true); sdio_release_host(bus->sdiodev->func[1]); brcmu_pkt_buf_free_skb(pkt); continue; @@ -1847,7 +1836,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) brcmf_err("%s: glom superframe w/o " "descriptor!\n", __func__); sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_rxfail(bus, false, false); + brcmf_sdio_rxfail(bus, false, false); sdio_release_host(bus->sdiodev->func[1]); } /* prepare the descriptor for the next read */ @@ -1891,7 +1880,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) } static void -brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus) +brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus) { if (waitqueue_active(&bus->ctrl_wait)) wake_up_interruptible(&bus->ctrl_wait); @@ -2107,8 +2096,8 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) /* Writes a HW/SW header into the packet and sends it. */ /* Assumes: (a) header space already there, (b) caller holds lock */ -static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, - uint chan) +static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, + uint chan) { int ret; int i; @@ -2121,8 +2110,7 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, goto done; sdio_claim_host(bus->sdiodev->func[1]); - ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, pktq); + ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq); bus->sdcnt.f2txdata++; if (ret < 0) { @@ -2131,17 +2119,17 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, ret); bus->sdcnt.tx_sderrs++; - brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); + brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, + SFC_WF_TERM, NULL); bus->sdcnt.f1regdata++; for (i = 0; i < 3; i++) { u8 hi, lo; - hi = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, NULL); + hi = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_WFRAMEBCLO, NULL); bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) break; @@ -2160,7 +2148,7 @@ done: return ret; } -static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) +static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) { struct sk_buff *pkt; struct sk_buff_head pktq; @@ -2194,7 +2182,7 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) if (i == 0) break; - ret = brcmf_sdbrcm_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); + ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); cnt += i; /* In poll mode, need to check for other events */ @@ -2223,7 +2211,7 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) return cnt; } -static void brcmf_sdbrcm_bus_stop(struct device *dev) +static void brcmf_sdio_bus_stop(struct device *dev) { u32 local_hostintmask; u8 saveclk; @@ -2243,7 +2231,7 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev) sdio_claim_host(bus->sdiodev->func[1]); /* Enable clock for device interrupts */ - brcmf_sdbrcm_bus_sleep(bus, false, false); + brcmf_sdio_bus_sleep(bus, false, false); /* Disable and clear interrupts at the chip level also */ w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); @@ -2254,26 +2242,25 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev) bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; /* Force clocks on backplane to be sure F2 interrupt propagates */ - saveclk = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); + saveclk = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); if (!err) { - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - (saveclk | SBSDIO_FORCE_HT), &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); } if (err) brcmf_err("Failed to force clock for F2: err %d\n", err); /* Turn off the bus (F2), free any pending packets */ brcmf_dbg(INTR, "disable SDIO interrupts\n"); - brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, SDIO_FUNC_ENABLE_1, - NULL); + sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); /* Clear any pending interrupts now that F2 is disabled */ w_sdreg32(bus, local_hostintmask, offsetof(struct sdpcmd_regs, intstatus)); /* Turn off the backplane clock (only) */ - brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); + brcmf_sdio_clkctl(bus, CLK_SDONLY, false); sdio_release_host(bus->sdiodev->func[1]); /* Clear the data packet queues */ @@ -2282,20 +2269,20 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev) /* Clear any held glomming stuff */ if (bus->glomd) brcmu_pkt_buf_free_skb(bus->glomd); - brcmf_sdbrcm_free_glom(bus); + brcmf_sdio_free_glom(bus); /* Clear rx control and wake any waiters */ spin_lock_bh(&bus->rxctl_lock); bus->rxlen = 0; spin_unlock_bh(&bus->rxctl_lock); - brcmf_sdbrcm_dcmd_resp_wake(bus); + brcmf_sdio_dcmd_resp_wake(bus); /* Reset some F2 state stuff */ bus->rxskip = false; bus->tx_seq = bus->rx_seq = 0; } -static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) +static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus) { unsigned long flags; @@ -2320,7 +2307,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) addr = bus->ci->c_inf[idx].base + offsetof(struct sdpcmd_regs, intstatus); - ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, false); + val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret); bus->sdcnt.f1regdata++; if (ret != 0) val = 0; @@ -2330,7 +2317,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) /* Clear interrupts */ if (val) { - ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, true); + brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret); bus->sdcnt.f1regdata++; } @@ -2344,7 +2331,7 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) return ret; } -static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) +static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { u32 newstatus = 0; unsigned long intstatus; @@ -2363,8 +2350,8 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) #ifdef DEBUG /* Check for inconsistent device control */ - devctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); if (err) { brcmf_err("error reading DEVCTL: %d\n", err); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; @@ -2372,8 +2359,8 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) #endif /* DEBUG */ /* Read CSR, if clock on switch to AVAIL, else ignore */ - clkctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); if (err) { brcmf_err("error reading CSR: %d\n", err); @@ -2384,16 +2371,16 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) devctl, clkctl); if (SBSDIO_HTAV(clkctl)) { - devctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_DEVICE_CTL, &err); + devctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_DEVICE_CTL, &err); if (err) { brcmf_err("error reading DEVCTL: %d\n", err); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; } devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, - devctl, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, + devctl, &err); if (err) { brcmf_err("error writing DEVCTL: %d\n", err); @@ -2404,7 +2391,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) } /* Make sure backplane clock is on */ - brcmf_sdbrcm_bus_sleep(bus, false, true); + brcmf_sdio_bus_sleep(bus, false, true); /* Pending interrupt indicates new device status */ if (atomic_read(&bus->ipend) > 0) { @@ -2435,7 +2422,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) /* Handle host mailbox indication */ if (intstatus & I_HMB_HOST_INT) { intstatus &= ~I_HMB_HOST_INT; - intstatus |= brcmf_sdbrcm_hostmail(bus); + intstatus |= brcmf_sdio_hostmail(bus); } sdio_release_host(bus->sdiodev->func[1]); @@ -2480,16 +2467,15 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) set_bit(n, (unsigned long *)&bus->intstatus.counter); } - brcmf_sdbrcm_clrintr(bus); + brcmf_sdio_clrintr(bus); if (data_ok(bus) && bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL)) { int i; sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, bus->ctrl_frame_buf, - (u32) bus->ctrl_frame_len); + err = brcmf_sdiod_send_buf(bus->sdiodev, bus->ctrl_frame_buf, + (u32)bus->ctrl_frame_len); if (err < 0) { /* On failure, abort the command and @@ -2498,20 +2484,20 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) err); bus->sdcnt.tx_sderrs++; - brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); + brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, + SFC_WF_TERM, &err); bus->sdcnt.f1regdata++; for (i = 0; i < 3; i++) { u8 hi, lo; - hi = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, - &err); - lo = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, - &err); + hi = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_WFRAMEBCHI, + &err); + lo = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_WFRAMEBCLO, + &err); bus->sdcnt.f1regdata += 2; if ((hi == 0) && (lo == 0)) break; @@ -2522,7 +2508,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) } sdio_release_host(bus->sdiodev->func[1]); bus->ctrl_frame_stat = false; - brcmf_sdbrcm_wait_event_wakeup(bus); + brcmf_sdio_wait_event_wakeup(bus); } /* Send queued frames (limit 1 if rx may still be pending) */ else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && @@ -2530,7 +2516,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) && data_ok(bus)) { framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : txlimit; - framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt); + framecnt = brcmf_sdio_sendfromq(bus, framecnt); txlimit -= framecnt; } @@ -2552,12 +2538,12 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) bus->activity = false; brcmf_dbg(SDIO, "idle state\n"); sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_bus_sleep(bus, true, false); + brcmf_sdio_bus_sleep(bus, true, false); sdio_release_host(bus->sdiodev->func[1]); } } -static struct pktq *brcmf_sdbrcm_bus_gettxq(struct device *dev) +static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; @@ -2566,7 +2552,7 @@ static struct pktq *brcmf_sdbrcm_bus_gettxq(struct device *dev) return &bus->txq; } -static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) +static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt) { int ret = -EBADE; uint datalen, prec; @@ -2622,7 +2608,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) #ifdef DEBUG #define CONSOLE_LINE_MAX 192 -static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus) +static int brcmf_sdio_readconsole(struct brcmf_sdio *bus) { struct brcmf_console *c = &bus->console; u8 line[CONSOLE_LINE_MAX], ch; @@ -2635,8 +2621,8 @@ static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus) /* Read console log struct */ addr = bus->console_addr + offsetof(struct rte_console, log_le); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, - sizeof(c->log_le)); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, + sizeof(c->log_le)); if (rv < 0) return rv; @@ -2661,7 +2647,7 @@ static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus) /* Read the console buffer */ addr = le32_to_cpu(c->log_le.buf); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize); if (rv < 0) return rv; @@ -2699,14 +2685,13 @@ break2: } #endif /* DEBUG */ -static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) +static int brcmf_sdio_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) { int i; int ret; bus->ctrl_frame_stat = false; - ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, - SDIO_FUNC_2, F2SYNC, frame, len); + ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); if (ret < 0) { /* On failure, abort the command and terminate the frame */ @@ -2714,18 +2699,18 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) ret); bus->sdcnt.tx_sderrs++; - brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); + brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, - SFC_WF_TERM, NULL); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, + SFC_WF_TERM, NULL); bus->sdcnt.f1regdata++; for (i = 0; i < 3; i++) { u8 hi, lo; - hi = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCHI, NULL); - lo = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_WFRAMEBCLO, NULL); + hi = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_WFRAMEBCHI, NULL); + lo = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_WFRAMEBCLO, NULL); bus->sdcnt.f1regdata += 2; if (hi == 0 && lo == 0) break; @@ -2739,7 +2724,7 @@ static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) } static int -brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) +brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) { u8 *frame; u16 len, pad; @@ -2783,7 +2768,7 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) /* Make sure backplane clock is on */ sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_bus_sleep(bus, false, false); + brcmf_sdio_bus_sleep(bus, false, false); sdio_release_host(bus->sdiodev->func[1]); hd_info.len = (u16)msglen; @@ -2827,7 +2812,7 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) do { sdio_claim_host(bus->sdiodev->func[1]); - ret = brcmf_tx_frame(bus, frame, len); + ret = brcmf_sdio_tx_frame(bus, frame, len); sdio_release_host(bus->sdiodev->func[1]); } while (ret < 0 && retries++ < TXRETRIES); } @@ -2837,7 +2822,7 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) bus->activity = false; sdio_claim_host(bus->sdiodev->func[1]); brcmf_dbg(INFO, "idle\n"); - brcmf_sdbrcm_clkctl(bus, CLK_NONE, true); + brcmf_sdio_clkctl(bus, CLK_NONE, true); sdio_release_host(bus->sdiodev->func[1]); } @@ -2871,8 +2856,8 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus, * address of sdpcm_shared structure */ sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_bus_sleep(bus, false, false); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); + brcmf_sdio_bus_sleep(bus, false, false); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); sdio_release_host(bus->sdiodev->func[1]); if (rv < 0) return rv; @@ -2892,8 +2877,8 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus, } /* Read hndrte_shared structure */ - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, - sizeof(struct sdpcm_shared_le)); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, + sizeof(struct sdpcm_shared_le)); if (rv < 0) return rv; @@ -2929,22 +2914,22 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, /* obtain console information from device memory */ addr = sh->console_addr + offsetof(struct rte_console, log_le); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); if (rv < 0) return rv; console_ptr = le32_to_cpu(sh_val); addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); if (rv < 0) return rv; console_size = le32_to_cpu(sh_val); addr = sh->console_addr + offsetof(struct rte_console, log_le.idx); - rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, - (u8 *)&sh_val, sizeof(u32)); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, + (u8 *)&sh_val, sizeof(u32)); if (rv < 0) return rv; console_index = le32_to_cpu(sh_val); @@ -2958,8 +2943,8 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, /* obtain the console data from device */ conbuf[console_size] = '\0'; - rv = brcmf_sdio_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, - console_size); + rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, + console_size); if (rv < 0) goto done; @@ -2996,8 +2981,8 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, return 0; } - error = brcmf_sdio_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, - sizeof(struct brcmf_trap_info)); + error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, + sizeof(struct brcmf_trap_info)); if (error < 0) return error; @@ -3040,14 +3025,14 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, sdio_claim_host(bus->sdiodev->func[1]); if (sh->assert_file_addr != 0) { - error = brcmf_sdio_ramrw(bus->sdiodev, false, - sh->assert_file_addr, (u8 *)file, 80); + error = brcmf_sdiod_ramrw(bus->sdiodev, false, + sh->assert_file_addr, (u8 *)file, 80); if (error < 0) return error; } if (sh->assert_exp_addr != 0) { - error = brcmf_sdio_ramrw(bus->sdiodev, false, - sh->assert_exp_addr, (u8 *)expr, 80); + error = brcmf_sdiod_ramrw(bus->sdiodev, false, + sh->assert_exp_addr, (u8 *)expr, 80); if (error < 0) return error; } @@ -3059,7 +3044,7 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, return simple_read_from_buffer(data, count, &pos, buf, res); } -static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) { int error; struct sdpcm_shared sh; @@ -3080,8 +3065,8 @@ static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) return 0; } -static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_sdio_died_dump(struct brcmf_sdio *bus, char __user *data, + size_t count, loff_t *ppos) { int error = 0; struct sdpcm_shared sh; @@ -3122,7 +3107,7 @@ static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data, struct brcmf_sdio *bus = f->private_data; int res; - res = brcmf_sdbrcm_died_dump(bus, data, count, ppos); + res = brcmf_sdio_died_dump(bus, data, count, ppos); if (res > 0) *ppos += res; return (ssize_t)res; @@ -3147,7 +3132,7 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); } #else -static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) { return 0; } @@ -3158,7 +3143,7 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) #endif /* DEBUG */ static int -brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) +brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) { int timeleft; uint rxlen = 0; @@ -3171,7 +3156,7 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) brcmf_dbg(TRACE, "Enter\n"); /* Wait until control frame is available */ - timeleft = brcmf_sdbrcm_dcmd_resp_wait(bus, &bus->rxlen, &pending); + timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending); spin_lock_bh(&bus->rxctl_lock); rxlen = bus->rxlen; @@ -3188,13 +3173,13 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) rxlen, msglen); } else if (timeleft == 0) { brcmf_err("resumed on timeout\n"); - brcmf_sdbrcm_checkdied(bus); + brcmf_sdio_checkdied(bus); } else if (pending) { brcmf_dbg(CTL, "cancelled\n"); return -ERESTARTSYS; } else { brcmf_dbg(CTL, "resumed for unknown reason?\n"); - brcmf_sdbrcm_checkdied(bus); + brcmf_sdio_checkdied(bus); } if (rxlen) @@ -3205,7 +3190,7 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) return rxlen ? (int)rxlen : -ETIMEDOUT; } -static bool brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter) +static bool brcmf_sdio_download_state(struct brcmf_sdio *bus, bool enter) { struct chip_info *ci = bus->ci; @@ -3230,7 +3215,7 @@ static bool brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter) return true; } -static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus) +static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus) { const struct firmware *fw; int err; @@ -3238,7 +3223,7 @@ static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus) int address; int len; - fw = brcmf_sdbrcm_get_fw(bus, BRCMF_FIRMWARE_BIN); + fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN); if (fw == NULL) return -ENOENT; @@ -3252,8 +3237,8 @@ static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus) while (offset < fw->size) { len = ((offset + MEMBLOCK) < fw->size) ? MEMBLOCK : fw->size - offset; - err = brcmf_sdio_ramrw(bus->sdiodev, true, address, - (u8 *)&fw->data[offset], len); + err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, + (u8 *)&fw->data[offset], len); if (err) { brcmf_err("error %d on writing %d membytes at 0x%08x\n", err, len, address); @@ -3278,8 +3263,8 @@ failure: * by two NULs. */ -static int brcmf_process_nvram_vars(struct brcmf_sdio *bus, - const struct firmware *nv) +static int brcmf_sdio_strip_nvram(struct brcmf_sdio *bus, + const struct firmware *nv) { char *varbuf; char *dp; @@ -3343,44 +3328,48 @@ err: return ret; } -static int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus) +static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus) { const struct firmware *nv; int ret; - nv = brcmf_sdbrcm_get_fw(bus, BRCMF_FIRMWARE_NVRAM); + nv = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM); if (nv == NULL) return -ENOENT; - ret = brcmf_process_nvram_vars(bus, nv); + ret = brcmf_sdio_strip_nvram(bus, nv); release_firmware(nv); return ret; } -static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) +static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus) { - int bcmerror = -1; + int bcmerror = -EFAULT; + + + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Keep arm in reset */ - if (!brcmf_sdbrcm_download_state(bus, true)) { + if (!brcmf_sdio_download_state(bus, true)) { brcmf_err("error placing ARM core in reset\n"); goto err; } - if (brcmf_sdbrcm_download_code_file(bus)) { + if (brcmf_sdio_download_code_file(bus)) { brcmf_err("dongle image file download failed\n"); goto err; } - if (brcmf_sdbrcm_download_nvram(bus)) { + if (brcmf_sdio_download_nvram(bus)) { brcmf_err("dongle nvram file download failed\n"); goto err; } /* Take arm out of reset */ - if (!brcmf_sdbrcm_download_state(bus, false)) { + if (!brcmf_sdio_download_state(bus, false)) { brcmf_err("error getting out of ARM core reset\n"); goto err; } @@ -3388,10 +3377,12 @@ static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) bcmerror = 0; err: + brcmf_sdio_clkctl(bus, CLK_SDONLY, false); + sdio_release_host(bus->sdiodev->func[1]); return bcmerror; } -static bool brcmf_sdbrcm_sr_capable(struct brcmf_sdio *bus) +static bool brcmf_sdio_sr_capable(struct brcmf_sdio *bus) { u32 addr, reg; @@ -3403,47 +3394,45 @@ static bool brcmf_sdbrcm_sr_capable(struct brcmf_sdio *bus) /* read PMU chipcontrol register 3*/ addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr); - brcmf_sdio_regwl(bus->sdiodev, addr, 3, NULL); + brcmf_sdiod_regwl(bus->sdiodev, addr, 3, NULL); addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data); - reg = brcmf_sdio_regrl(bus->sdiodev, addr, NULL); + reg = brcmf_sdiod_regrl(bus->sdiodev, addr, NULL); return (bool)reg; } -static void brcmf_sdbrcm_sr_init(struct brcmf_sdio *bus) +static void brcmf_sdio_sr_init(struct brcmf_sdio *bus) { int err = 0; u8 val; brcmf_dbg(TRACE, "Enter\n"); - val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, - &err); + val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err); if (err) { brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n"); return; } val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, - val, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err); if (err) { brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n"); return; } /* Add CMD14 Support */ - brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, - (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | - SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), - &err); + brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, + (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | + SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), + &err); if (err) { brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n"); return; } - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - SBSDIO_FORCE_HT, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + SBSDIO_FORCE_HT, &err); if (err) { brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n"); return; @@ -3455,7 +3444,7 @@ static void brcmf_sdbrcm_sr_init(struct brcmf_sdio *bus) } /* enable KSO bit */ -static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus) +static int brcmf_sdio_kso_init(struct brcmf_sdio *bus) { u8 val; int err = 0; @@ -3466,8 +3455,7 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus) if (bus->ci->c_inf[1].rev < 12) return 0; - val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - &err); + val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err); if (err) { brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n"); return err; @@ -3476,8 +3464,8 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus) if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, - val, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, + val, &err); if (err) { brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n"); return err; @@ -3488,25 +3476,7 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus) } -static bool -brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) -{ - bool ret; - - sdio_claim_host(bus->sdiodev->func[1]); - - brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); - - ret = _brcmf_sdbrcm_download_firmware(bus) == 0; - - brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); - - sdio_release_host(bus->sdiodev->func[1]); - - return ret; -} - -static int brcmf_sdbrcm_bus_preinit(struct device *dev) +static int brcmf_sdio_bus_preinit(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; @@ -3565,13 +3535,11 @@ done: return err; } -static int brcmf_sdbrcm_bus_init(struct device *dev) +static int brcmf_sdio_bus_init(struct device *dev) { struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - unsigned long timeout; - u8 ready, enable; int err, ret = 0; u8 saveclk; @@ -3579,8 +3547,9 @@ static int brcmf_sdbrcm_bus_init(struct device *dev) /* try to download image and nvram to the dongle */ if (bus_if->state == BRCMF_BUS_DOWN) { - if (!(brcmf_sdbrcm_download_firmware(bus))) - return -1; + err = brcmf_sdio_download_firmware(bus); + if (err) + return err; } if (!bus->sdiodev->bus_if->drvr) @@ -3588,21 +3557,21 @@ static int brcmf_sdbrcm_bus_init(struct device *dev) /* Start the watchdog timer */ bus->sdcnt.tickcnt = 0; - brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); sdio_claim_host(bus->sdiodev->func[1]); /* Make sure backplane clock is on, needed to generate F2 interrupt */ - brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); if (bus->clkstate != CLK_AVAIL) goto exit; /* Force clocks on backplane to be sure F2 interrupt propagates */ - saveclk = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); + saveclk = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); if (!err) { - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - (saveclk | SBSDIO_FORCE_HT), &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + (saveclk | SBSDIO_FORCE_HT), &err); } if (err) { brcmf_err("Failed to force clock for F2: err %d\n", err); @@ -3612,56 +3581,42 @@ static int brcmf_sdbrcm_bus_init(struct device *dev) /* Enable function 2 (frame transfers) */ w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, offsetof(struct sdpcmd_regs, tosbmailboxdata)); - enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2); - - brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, enable, NULL); + err = sdio_enable_func(bus->sdiodev->func[SDIO_FUNC_2]); - timeout = jiffies + msecs_to_jiffies(BRCMF_WAIT_F2RDY); - ready = 0; - while (enable != ready) { - ready = brcmf_sdio_regrb(bus->sdiodev, - SDIO_CCCR_IORx, NULL); - if (time_after(jiffies, timeout)) - break; - else if (time_after(jiffies, timeout - BRCMF_WAIT_F2RDY + 50)) - /* prevent busy waiting if it takes too long */ - msleep_interruptible(20); - } - brcmf_dbg(INFO, "enable 0x%02x, ready 0x%02x\n", enable, ready); + brcmf_dbg(INFO, "enable F2: err=%d\n", err); /* If F2 successfully enabled, set core and enable interrupts */ - if (ready == enable) { + if (!err) { /* Set up the interrupt mask and enable interrupts */ bus->hostintmask = HOSTINTMASK; w_sdreg32(bus, bus->hostintmask, offsetof(struct sdpcmd_regs, hostintmask)); - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err); } else { /* Disable F2 again */ - enable = SDIO_FUNC_ENABLE_1; - brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, enable, NULL); + sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); ret = -ENODEV; } - if (brcmf_sdbrcm_sr_capable(bus)) { - brcmf_sdbrcm_sr_init(bus); + if (brcmf_sdio_sr_capable(bus)) { + brcmf_sdio_sr_init(bus); } else { /* Restore previous clock setting */ - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - saveclk, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + saveclk, &err); } if (ret == 0) { - ret = brcmf_sdio_intr_register(bus->sdiodev); + ret = brcmf_sdiod_intr_register(bus->sdiodev); if (ret != 0) brcmf_err("intr register failed:%d\n", ret); } /* If we didn't come up, turn off backplane clock */ if (bus_if->state != BRCMF_BUS_DATA) - brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); + brcmf_sdio_clkctl(bus, CLK_NONE, false); exit: sdio_release_host(bus->sdiodev->func[1]); @@ -3669,10 +3624,8 @@ exit: return ret; } -void brcmf_sdbrcm_isr(void *arg) +void brcmf_sdio_isr(struct brcmf_sdio *bus) { - struct brcmf_sdio *bus = (struct brcmf_sdio *) arg; - brcmf_dbg(TRACE, "Enter\n"); if (!bus) { @@ -3702,7 +3655,7 @@ void brcmf_sdbrcm_isr(void *arg) queue_work(bus->brcmf_wq, &bus->datawork); } -static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) +static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) { #ifdef DEBUG struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); @@ -3726,9 +3679,9 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) u8 devpend; sdio_claim_host(bus->sdiodev->func[1]); - devpend = brcmf_sdio_regrb(bus->sdiodev, - SDIO_CCCR_INTx, - NULL); + devpend = brcmf_sdiod_regrb(bus->sdiodev, + SDIO_CCCR_INTx, + NULL); sdio_release_host(bus->sdiodev->func[1]); intstatus = devpend & (INTR_STATUS_FUNC1 | @@ -3758,8 +3711,8 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) bus->console.count -= bus->console_interval; sdio_claim_host(bus->sdiodev->func[1]); /* Make sure backplane clock is on */ - brcmf_sdbrcm_bus_sleep(bus, false, false); - if (brcmf_sdbrcm_readconsole(bus) < 0) + brcmf_sdio_bus_sleep(bus, false, false); + if (brcmf_sdio_readconsole(bus) < 0) /* stop on error */ bus->console_interval = 0; sdio_release_host(bus->sdiodev->func[1]); @@ -3773,11 +3726,11 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) bus->idlecount = 0; if (bus->activity) { bus->activity = false; - brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); + brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); } else { brcmf_dbg(SDIO, "idle\n"); sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_bus_sleep(bus, true, false); + brcmf_sdio_bus_sleep(bus, true, false); sdio_release_host(bus->sdiodev->func[1]); } } @@ -3792,12 +3745,12 @@ static void brcmf_sdio_dataworker(struct work_struct *work) datawork); while (atomic_read(&bus->dpc_tskcnt)) { - brcmf_sdbrcm_dpc(bus); + brcmf_sdio_dpc(bus); atomic_dec(&bus->dpc_tskcnt); } } -static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus) +static void brcmf_sdio_release_malloc(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); @@ -3806,7 +3759,7 @@ static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus) bus->rxlen = 0; } -static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus) +static bool brcmf_sdio_probe_malloc(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); @@ -3823,7 +3776,7 @@ static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus) } static bool -brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) +brcmf_sdio_probe_attach(struct brcmf_sdio *bus) { u8 clkctl = 0; int err = 0; @@ -3836,18 +3789,18 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) sdio_claim_host(bus->sdiodev->func[1]); pr_debug("F1 signature read @0x18000000=0x%4x\n", - brcmf_sdio_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); + brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); /* * Force PLL off until brcmf_sdio_chip_attach() * programs PLL control regs */ - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, - BRCMF_INIT_CLKCTL1, &err); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, + BRCMF_INIT_CLKCTL1, &err); if (!err) - clkctl = brcmf_sdio_regrb(bus->sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, &err); + clkctl = brcmf_sdiod_regrb(bus->sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, &err); if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) { brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", @@ -3855,12 +3808,12 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) goto fail; } - if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci, regsva)) { + if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci)) { brcmf_err("brcmf_sdio_chip_attach failed!\n"); goto fail; } - if (brcmf_sdbrcm_kso_init(bus)) { + if (brcmf_sdio_kso_init(bus)) { brcmf_err("error enabling KSO\n"); goto fail; } @@ -3879,33 +3832,33 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) } /* Set card control so an SDIO card reset does a WLAN backplane reset */ - reg_val = brcmf_sdio_regrb(bus->sdiodev, - SDIO_CCCR_BRCM_CARDCTRL, &err); + reg_val = brcmf_sdiod_regrb(bus->sdiodev, + SDIO_CCCR_BRCM_CARDCTRL, &err); if (err) goto fail; reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET; - brcmf_sdio_regwb(bus->sdiodev, - SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); + brcmf_sdiod_regwb(bus->sdiodev, + SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); if (err) goto fail; /* set PMUControl so a backplane reset does PMU state reload */ reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base, pmucontrol); - reg_val = brcmf_sdio_regrl(bus->sdiodev, - reg_addr, - &err); + reg_val = brcmf_sdiod_regrl(bus->sdiodev, + reg_addr, + &err); if (err) goto fail; reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); - brcmf_sdio_regwl(bus->sdiodev, - reg_addr, - reg_val, - &err); + brcmf_sdiod_regwl(bus->sdiodev, + reg_addr, + reg_val, + &err); if (err) goto fail; @@ -3935,21 +3888,20 @@ fail: return false; } -static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus) +static bool brcmf_sdio_probe_init(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); sdio_claim_host(bus->sdiodev->func[1]); /* Disable F2 to clear any intermediate frame state on the dongle */ - brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, - SDIO_FUNC_ENABLE_1, NULL); + sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; bus->rxflow = false; /* Done with backplane-dependent accesses, can drop clock... */ - brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); + brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); sdio_release_host(bus->sdiodev->func[1]); @@ -3970,7 +3922,7 @@ static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus) } static int -brcmf_sdbrcm_watchdog_thread(void *data) +brcmf_sdio_watchdog_thread(void *data) { struct brcmf_sdio *bus = (struct brcmf_sdio *)data; @@ -3980,7 +3932,7 @@ brcmf_sdbrcm_watchdog_thread(void *data) if (kthread_should_stop()) break; if (!wait_for_completion_interruptible(&bus->watchdog_wait)) { - brcmf_sdbrcm_bus_watchdog(bus); + brcmf_sdio_bus_watchdog(bus); /* Count the tick for reference */ bus->sdcnt.tickcnt++; } else @@ -3990,7 +3942,7 @@ brcmf_sdbrcm_watchdog_thread(void *data) } static void -brcmf_sdbrcm_watchdog(unsigned long data) +brcmf_sdio_watchdog(unsigned long data) { struct brcmf_sdio *bus = (struct brcmf_sdio *)data; @@ -4003,14 +3955,14 @@ brcmf_sdbrcm_watchdog(unsigned long data) } } -static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus) +static void brcmf_sdio_release_dongle(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); if (bus->ci) { sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); - brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); + brcmf_sdio_clkctl(bus, CLK_AVAIL, false); + brcmf_sdio_clkctl(bus, CLK_NONE, false); sdio_release_host(bus->sdiodev->func[1]); brcmf_sdio_chip_detach(&bus->ci); if (bus->vars && bus->varsz) @@ -4021,53 +3973,23 @@ static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Disconnected\n"); } -/* Detach and free everything */ -static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) -{ - brcmf_dbg(TRACE, "Enter\n"); - - if (bus) { - /* De-register interrupt handler */ - brcmf_sdio_intr_unregister(bus->sdiodev); - - cancel_work_sync(&bus->datawork); - if (bus->brcmf_wq) - destroy_workqueue(bus->brcmf_wq); - - if (bus->sdiodev->bus_if->drvr) { - brcmf_detach(bus->sdiodev->dev); - brcmf_sdbrcm_release_dongle(bus); - } - - brcmu_pkt_buf_free_skb(bus->txglom_sgpad); - brcmf_sdbrcm_release_malloc(bus); - kfree(bus->hdrbuf); - kfree(bus); - } - - brcmf_dbg(TRACE, "Disconnected\n"); -} - static struct brcmf_bus_ops brcmf_sdio_bus_ops = { - .stop = brcmf_sdbrcm_bus_stop, - .preinit = brcmf_sdbrcm_bus_preinit, - .init = brcmf_sdbrcm_bus_init, - .txdata = brcmf_sdbrcm_bus_txdata, - .txctl = brcmf_sdbrcm_bus_txctl, - .rxctl = brcmf_sdbrcm_bus_rxctl, - .gettxq = brcmf_sdbrcm_bus_gettxq, + .stop = brcmf_sdio_bus_stop, + .preinit = brcmf_sdio_bus_preinit, + .init = brcmf_sdio_bus_init, + .txdata = brcmf_sdio_bus_txdata, + .txctl = brcmf_sdio_bus_txctl, + .rxctl = brcmf_sdio_bus_rxctl, + .gettxq = brcmf_sdio_bus_gettxq, }; -void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) +struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) { int ret; struct brcmf_sdio *bus; brcmf_dbg(TRACE, "Enter\n"); - /* We make an assumption about address window mappings: - * regsva == SI_ENUM_BASE*/ - /* Allocate private bus interface state */ bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC); if (!bus) @@ -4101,8 +4023,8 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) } /* attempt to attach to the dongle */ - if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) { - brcmf_err("brcmf_sdbrcm_probe_attach failed\n"); + if (!(brcmf_sdio_probe_attach(bus))) { + brcmf_err("brcmf_sdio_probe_attach failed\n"); goto fail; } @@ -4114,11 +4036,11 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) /* Set up the watchdog timer */ init_timer(&bus->timer); bus->timer.data = (unsigned long)bus; - bus->timer.function = brcmf_sdbrcm_watchdog; + bus->timer.function = brcmf_sdio_watchdog; /* Initialize watchdog thread */ init_completion(&bus->watchdog_wait); - bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread, + bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread, bus, "brcmf_watchdog"); if (IS_ERR(bus->watchdog_tsk)) { pr_warn("brcmf_watchdog thread failed to start\n"); @@ -4144,13 +4066,13 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) } /* Allocate buffers */ - if (!(brcmf_sdbrcm_probe_malloc(bus))) { - brcmf_err("brcmf_sdbrcm_probe_malloc failed\n"); + if (!(brcmf_sdio_probe_malloc(bus))) { + brcmf_err("brcmf_sdio_probe_malloc failed\n"); goto fail; } - if (!(brcmf_sdbrcm_probe_init(bus))) { - brcmf_err("brcmf_sdbrcm_probe_init failed\n"); + if (!(brcmf_sdio_probe_init(bus))) { + brcmf_err("brcmf_sdio_probe_init failed\n"); goto fail; } @@ -4167,24 +4089,38 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) return bus; fail: - brcmf_sdbrcm_release(bus); + brcmf_sdio_remove(bus); return NULL; } -void brcmf_sdbrcm_disconnect(void *ptr) +/* Detach and free everything */ +void brcmf_sdio_remove(struct brcmf_sdio *bus) { - struct brcmf_sdio *bus = (struct brcmf_sdio *)ptr; - brcmf_dbg(TRACE, "Enter\n"); - if (bus) - brcmf_sdbrcm_release(bus); + if (bus) { + /* De-register interrupt handler */ + brcmf_sdiod_intr_unregister(bus->sdiodev); + + cancel_work_sync(&bus->datawork); + if (bus->brcmf_wq) + destroy_workqueue(bus->brcmf_wq); + + if (bus->sdiodev->bus_if->drvr) { + brcmf_detach(bus->sdiodev->dev); + brcmf_sdio_release_dongle(bus); + } + + brcmu_pkt_buf_free_skb(bus->txglom_sgpad); + brcmf_sdio_release_malloc(bus); + kfree(bus->hdrbuf); + kfree(bus); + } brcmf_dbg(TRACE, "Disconnected\n"); } -void -brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick) +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) { /* Totally stop the timer */ if (!wdtick && bus->wd_timer_valid) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index b72d3395499a..22adbe311d20 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -68,7 +68,7 @@ brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); err = brcmf_fil_cmd_data(ifp, cmd, data, len, true); mutex_unlock(&ifp->drvr->proto_block); @@ -86,7 +86,7 @@ brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); mutex_unlock(&ifp->drvr->proto_block); @@ -155,7 +155,7 @@ brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); @@ -195,7 +195,7 @@ brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); mutex_unlock(&drvr->proto_block); return err; @@ -278,7 +278,7 @@ brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); @@ -317,7 +317,7 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, } brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, - min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); + min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); mutex_unlock(&drvr->proto_block); return err; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index e9bdfdb95d8f..7918c1033662 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -838,7 +838,7 @@ static void brcmf_fws_cleanup(struct brcmf_fws_info *fws, int ifidx) brcmf_fws_hanger_cleanup(fws, matchfn, ifidx); } -static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) +static u8 brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) { struct brcmf_fws_mac_descriptor *entry = brcmf_skbcb(skb)->mac; u8 *wlh; @@ -887,9 +887,7 @@ static int brcmf_fws_hdrpush(struct brcmf_fws_info *fws, struct sk_buff *skb) if (fillers) memset(wlh, BRCMF_FWS_TYPE_FILLER, fillers); - brcmf_proto_hdrpush(fws->drvr, brcmf_skb_if_flags_get_field(skb, INDEX), - data_offset >> 2, skb); - return 0; + return (u8)(data_offset >> 2); } static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, @@ -897,10 +895,11 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, int fifo, bool send_immediately) { struct sk_buff *skb; - struct brcmf_bus *bus; struct brcmf_skbuff_cb *skcb; s32 err; u32 len; + u8 data_offset; + int ifidx; /* check delayedQ and suppressQ in one call using bitmap */ if (brcmu_pktq_mlen(&entry->psq, 3 << (fifo * 2)) == 0) @@ -928,13 +927,11 @@ static bool brcmf_fws_tim_update(struct brcmf_fws_info *fws, skcb->state = BRCMF_FWS_SKBSTATE_TIM; skcb->htod = 0; skcb->htod_seq = 0; - bus = fws->drvr->bus_if; - err = brcmf_fws_hdrpush(fws, skb); - if (err == 0) { - brcmf_fws_unlock(fws); - err = brcmf_bus_txdata(bus, skb); - brcmf_fws_lock(fws); - } + data_offset = brcmf_fws_hdrpush(fws, skb); + ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); + brcmf_fws_unlock(fws); + err = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb); + brcmf_fws_lock(fws); if (err) brcmu_pkt_buf_free_skb(skb); return true; @@ -1393,7 +1390,7 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, entry->generation = genbit; ret = brcmf_proto_hdrpull(fws->drvr, false, &ifidx, skb); - if (ret == 0) + if (ret == 0) { brcmf_skb_htod_tag_set_field(skb, GENERATION, genbit); brcmf_skbcb(skb)->htod_seq = seq; if (brcmf_skb_htod_seq_get_field(skb, FROMFW)) { @@ -1404,6 +1401,8 @@ static int brcmf_fws_txstatus_suppressed(struct brcmf_fws_info *fws, int fifo, } ret = brcmf_fws_enq(fws, BRCMF_FWS_SKBSTATE_SUPPRESSED, fifo, skb); + } + if (ret != 0) { /* suppress q is full or hdrpull failed, drop this packet */ brcmf_fws_hanger_poppkt(&fws->hanger, hslot, &skb, @@ -1717,7 +1716,7 @@ int brcmf_fws_hdrpull(struct brcmf_pub *drvr, int ifidx, s16 signal_len, return 0; } -static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, +static u8 brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, struct sk_buff *p) { struct brcmf_skbuff_cb *skcb = brcmf_skbcb(p); @@ -1735,7 +1734,7 @@ static void brcmf_fws_precommit_skb(struct brcmf_fws_info *fws, int fifo, flags |= BRCMF_FWS_HTOD_FLAG_PKT_REQUESTED; } brcmf_skb_htod_tag_set_field(p, FLAGS, flags); - brcmf_fws_hdrpush(fws, p); + return brcmf_fws_hdrpush(fws, p); } static void brcmf_fws_rollback_toq(struct brcmf_fws_info *fws, @@ -1803,20 +1802,21 @@ static int brcmf_fws_commit_skb(struct brcmf_fws_info *fws, int fifo, { struct brcmf_skbuff_cb *skcb = brcmf_skbcb(skb); struct brcmf_fws_mac_descriptor *entry; - struct brcmf_bus *bus = fws->drvr->bus_if; int rc; u8 ifidx; + u8 data_offset; entry = skcb->mac; if (IS_ERR(entry)) return PTR_ERR(entry); - brcmf_fws_precommit_skb(fws, fifo, skb); + data_offset = brcmf_fws_precommit_skb(fws, fifo, skb); entry->transit_count++; if (entry->suppressed) entry->suppr_transit_count++; + ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); brcmf_fws_unlock(fws); - rc = brcmf_bus_txdata(bus, skb); + rc = brcmf_proto_txdata(fws->drvr, ifidx, data_offset, skb); brcmf_fws_lock(fws); brcmf_dbg(DATA, "%s flags %X htod %X bus_tx %d\n", entry->name, skcb->if_flags, skcb->htod, rc); @@ -1977,10 +1977,9 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) &skb, true); ifidx = brcmf_skb_if_flags_get_field(skb, INDEX); - brcmf_proto_hdrpush(drvr, ifidx, 0, skb); - /* Use bus module to send data frame */ + /* Use proto layer to send data frame */ brcmf_fws_unlock(fws); - ret = brcmf_bus_txdata(drvr->bus_if, skb); + ret = brcmf_proto_txdata(drvr, ifidx, 0, skb); brcmf_fws_lock(fws); if (ret < 0) brcmf_txfinalize(drvr, skb, false); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.c b/drivers/net/wireless/brcm80211/brcmfmac/proto.c index 87eb2bd4c072..b6b464184946 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.c @@ -39,7 +39,7 @@ int brcmf_proto_attach(struct brcmf_pub *drvr) if (brcmf_proto_bcdc_attach(drvr)) goto fail; - if ((proto->hdrpush == NULL) || (proto->hdrpull == NULL) || + if ((proto->txdata == NULL) || (proto->hdrpull == NULL) || (proto->query_dcmd == NULL) || (proto->set_dcmd == NULL)) { brcmf_err("Not all proto handlers have been installed\n"); goto fail; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/proto.h b/drivers/net/wireless/brcm80211/brcmfmac/proto.h index 8de1b3bce228..482fb0ba4a30 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/proto.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/proto.h @@ -17,14 +17,14 @@ #define BRCMFMAC_PROTO_H struct brcmf_proto { - void (*hdrpush)(struct brcmf_pub *drvr, int ifidx, u8 offset, - struct sk_buff *skb); int (*hdrpull)(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, struct sk_buff *skb); int (*query_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len); int (*set_dcmd)(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len); + int (*txdata)(struct brcmf_pub *drvr, int ifidx, u8 offset, + struct sk_buff *skb); void *pd; }; @@ -32,11 +32,6 @@ struct brcmf_proto { int brcmf_proto_attach(struct brcmf_pub *drvr); void brcmf_proto_detach(struct brcmf_pub *drvr); -static inline void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx, - u8 offset, struct sk_buff *skb) -{ - drvr->proto->hdrpush(drvr, ifidx, offset, skb); -} static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, u8 *ifidx, struct sk_buff *skb) { @@ -52,6 +47,11 @@ static inline int brcmf_proto_set_dcmd(struct brcmf_pub *drvr, int ifidx, { return drvr->proto->set_dcmd(drvr, ifidx, cmd, buf, len); } +static inline int brcmf_proto_txdata(struct brcmf_pub *drvr, int ifidx, + u8 offset, struct sk_buff *skb) +{ + return drvr->proto->txdata(drvr, ifidx, offset, skb); +} #endif /* BRCMFMAC_PROTO_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c index 2096a14ef1fb..5f39f28e6efb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.c @@ -112,9 +112,9 @@ brcmf_sdio_sb_corerev(struct brcmf_sdio_dev *sdiodev, idx = brcmf_sdio_chip_getinfidx(ci, coreid); - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbidhigh), - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbidhigh), + NULL); return SBCOREREV(regdata); } @@ -140,9 +140,9 @@ brcmf_sdio_sb_iscoreup(struct brcmf_sdio_dev *sdiodev, if (idx == BRCMF_MAX_CORENUM) return false; - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + NULL); regdata &= (SSB_TMSLOW_RESET | SSB_TMSLOW_REJECT | SSB_IMSTATE_REJECT | SSB_TMSLOW_CLOCK); return (SSB_TMSLOW_CLOCK == regdata); @@ -160,13 +160,13 @@ brcmf_sdio_ai_iscoreup(struct brcmf_sdio_dev *sdiodev, if (idx == BRCMF_MAX_CORENUM) return false; - regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, + NULL); ret = (regdata & (BCMA_IOCTL_FGC | BCMA_IOCTL_CLK)) == BCMA_IOCTL_CLK; - regdata = brcmf_sdio_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, + NULL); ret = ret && ((regdata & BCMA_RESET_CTL_RESET) == 0); return ret; @@ -182,79 +182,79 @@ brcmf_sdio_sb_coredisable(struct brcmf_sdio_dev *sdiodev, idx = brcmf_sdio_chip_getinfidx(ci, coreid); base = ci->c_inf[idx].base; - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); + regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); if (regdata & SSB_TMSLOW_RESET) return; - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); + regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbtmstatelow), NULL); if ((regdata & SSB_TMSLOW_CLOCK) != 0) { /* * set target reject and spin until busy is clear * (preserve core-specific bits) */ - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), - NULL); - brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - regdata | SSB_TMSLOW_REJECT, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbtmstatelow), NULL); + brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), + regdata | SSB_TMSLOW_REJECT, NULL); - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbtmstatelow), NULL); udelay(1); - SPINWAIT((brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbtmstatehigh), - NULL) & - SSB_TMSHIGH_BUSY), 100000); - - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbtmstatehigh), - NULL); + SPINWAIT((brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbtmstatehigh), + NULL) & + SSB_TMSHIGH_BUSY), 100000); + + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbtmstatehigh), + NULL); if (regdata & SSB_TMSHIGH_BUSY) brcmf_err("core state still busy\n"); - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow), - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow), + NULL); if (regdata & SSB_IDLOW_INITIATOR) { - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbimstate), + NULL); regdata |= SSB_IMSTATE_REJECT; - brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate), - regdata, NULL); - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); + brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate), + regdata, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbimstate), + NULL); udelay(1); - SPINWAIT((brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL) & - SSB_IMSTATE_BUSY), 100000); + SPINWAIT((brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbimstate), + NULL) & + SSB_IMSTATE_BUSY), 100000); } /* set reset and reject while enabling the clocks */ regdata = SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET; - brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - regdata, NULL); - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbtmstatelow), - NULL); + brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), + regdata, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbtmstatelow), NULL); udelay(10); /* clear the initiator reject bit */ - regdata = brcmf_sdio_regrl(sdiodev, CORE_SB(base, sbidlow), - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, CORE_SB(base, sbidlow), + NULL); if (regdata & SSB_IDLOW_INITIATOR) { - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(base, sbimstate), - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(base, sbimstate), + NULL); regdata &= ~SSB_IMSTATE_REJECT; - brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbimstate), - regdata, NULL); + brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbimstate), + regdata, NULL); } } /* leave reset and reject asserted */ - brcmf_sdio_regwl(sdiodev, CORE_SB(base, sbtmstatelow), - (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL); + brcmf_sdiod_regwl(sdiodev, CORE_SB(base, sbtmstatelow), + (SSB_TMSLOW_REJECT | SSB_TMSLOW_RESET), NULL); udelay(1); } @@ -270,9 +270,9 @@ brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, return; /* if core is already in reset, just return */ - regdata = brcmf_sdio_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, + NULL); if ((regdata & BCMA_RESET_CTL_RESET) != 0) return; @@ -281,24 +281,24 @@ brcmf_sdio_ai_coredisable(struct brcmf_sdio_dev *sdiodev, * extra 10ms is taken into account for firmware load stage * after 10300us carry on disabling the core anyway */ - SPINWAIT(brcmf_sdio_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_ST, - NULL), 10300); - regdata = brcmf_sdio_regrl(sdiodev, + SPINWAIT(brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_ST, - NULL); + NULL), 10300); + regdata = brcmf_sdiod_regrl(sdiodev, + ci->c_inf[idx].wrapbase+BCMA_RESET_ST, + NULL); if (regdata) brcmf_err("disabling core 0x%x with reset status %x\n", coreid, regdata); - brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - BCMA_RESET_CTL_RESET, NULL); + brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, + BCMA_RESET_CTL_RESET, NULL); udelay(1); - brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - core_bits, NULL); - regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); + brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, + core_bits, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, + NULL); usleep_range(10, 20); } @@ -325,47 +325,47 @@ brcmf_sdio_sb_resetcore(struct brcmf_sdio_dev *sdiodev, * set reset while enabling the clock and * forcing them on throughout the core */ - brcmf_sdio_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET, - NULL); - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); + brcmf_sdiod_regwl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK | SSB_TMSLOW_RESET, + NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + NULL); udelay(1); /* clear any serror */ - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), + NULL); if (regdata & SSB_TMSHIGH_SERR) - brcmf_sdio_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), - 0, NULL); + brcmf_sdiod_regwl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatehigh), + 0, NULL); - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbimstate), - NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbimstate), + NULL); if (regdata & (SSB_IMSTATE_IBE | SSB_IMSTATE_TO)) - brcmf_sdio_regwl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbimstate), - regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO), - NULL); + brcmf_sdiod_regwl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbimstate), + regdata & ~(SSB_IMSTATE_IBE | SSB_IMSTATE_TO), + NULL); /* clear reset and allow it to propagate throughout the core */ - brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL); - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); + brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + SSB_TMSLOW_FGC | SSB_TMSLOW_CLOCK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + NULL); udelay(1); /* leave clock enabled */ - brcmf_sdio_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - SSB_TMSLOW_CLOCK, NULL); - regdata = brcmf_sdio_regrl(sdiodev, - CORE_SB(ci->c_inf[idx].base, sbtmstatelow), - NULL); + brcmf_sdiod_regwl(sdiodev, CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + SSB_TMSLOW_CLOCK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_SB(ci->c_inf[idx].base, sbtmstatelow), + NULL); udelay(1); } @@ -384,21 +384,21 @@ brcmf_sdio_ai_resetcore(struct brcmf_sdio_dev *sdiodev, brcmf_sdio_ai_coredisable(sdiodev, ci, coreid, core_bits); /* now do initialization sequence */ - brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); - brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - 0, NULL); - regdata = brcmf_sdio_regrl(sdiodev, - ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, - NULL); + brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, + core_bits | BCMA_IOCTL_FGC | BCMA_IOCTL_CLK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, + NULL); + brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, + 0, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, + ci->c_inf[idx].wrapbase+BCMA_RESET_CTL, + NULL); udelay(1); - brcmf_sdio_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - core_bits | BCMA_IOCTL_CLK, NULL); - regdata = brcmf_sdio_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, - NULL); + brcmf_sdiod_regwl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, + core_bits | BCMA_IOCTL_CLK, NULL); + regdata = brcmf_sdiod_regrl(sdiodev, ci->c_inf[idx].wrapbase+BCMA_IOCTL, + NULL); udelay(1); } @@ -438,7 +438,7 @@ static inline int brcmf_sdio_chip_cichk(struct chip_info *ci) #endif static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, - struct chip_info *ci, u32 regs) + struct chip_info *ci) { u32 regdata; int ret; @@ -449,10 +449,10 @@ static int brcmf_sdio_chip_recognition(struct brcmf_sdio_dev *sdiodev, * other ways of recognition should be added here. */ ci->c_inf[0].id = BCMA_CORE_CHIPCOMMON; - ci->c_inf[0].base = regs; - regdata = brcmf_sdio_regrl(sdiodev, - CORE_CC_REG(ci->c_inf[0].base, chipid), - NULL); + ci->c_inf[0].base = SI_ENUM_BASE; + regdata = brcmf_sdiod_regrl(sdiodev, + CORE_CC_REG(ci->c_inf[0].base, chipid), + NULL); ci->chip = regdata & CID_ID_MASK; ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT; if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && @@ -607,7 +607,7 @@ brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) /* Try forcing SDIO core to do ALPAvail request only */ clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; - brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); if (err) { brcmf_err("error writing for HT off\n"); return err; @@ -615,8 +615,8 @@ brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) /* If register supported, wait for ALPAvail and then force ALP */ /* This may take up to 15 milliseconds */ - clkval = brcmf_sdio_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL); + clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL); if ((clkval & ~SBSDIO_AVBITS) != clkset) { brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", @@ -624,8 +624,8 @@ brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) return -EACCES; } - SPINWAIT(((clkval = brcmf_sdio_regrb(sdiodev, - SBSDIO_FUNC1_CHIPCLKCSR, NULL)), + SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, + SBSDIO_FUNC1_CHIPCLKCSR, NULL)), !SBSDIO_ALPAV(clkval)), PMU_MAX_TRANSITION_DLY); if (!SBSDIO_ALPAV(clkval)) { @@ -635,11 +635,11 @@ brcmf_sdio_chip_buscoreprep(struct brcmf_sdio_dev *sdiodev) } clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; - brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); udelay(65); /* Also, disable the extra SDIO pull-ups */ - brcmf_sdio_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); return 0; } @@ -654,16 +654,16 @@ brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, ci->c_inf[0].rev = ci->corerev(sdiodev, ci, ci->c_inf[0].id); /* get chipcommon capabilites */ - ci->c_inf[0].caps = brcmf_sdio_regrl(sdiodev, - CORE_CC_REG(base, capabilities), - NULL); + ci->c_inf[0].caps = brcmf_sdiod_regrl(sdiodev, + CORE_CC_REG(base, capabilities), + NULL); /* get pmu caps & rev */ if (ci->c_inf[0].caps & CC_CAP_PMU) { ci->pmucaps = - brcmf_sdio_regrl(sdiodev, - CORE_CC_REG(base, pmucapabilities), - NULL); + brcmf_sdiod_regrl(sdiodev, + CORE_CC_REG(base, pmucapabilities), + NULL); ci->pmurev = ci->pmucaps & PCAP_REV_MASK; } @@ -681,7 +681,7 @@ brcmf_sdio_chip_buscoresetup(struct brcmf_sdio_dev *sdiodev, } int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct chip_info **ci_ptr, u32 regs) + struct chip_info **ci_ptr) { int ret; struct chip_info *ci; @@ -697,16 +697,16 @@ int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, if (ret != 0) goto err; - ret = brcmf_sdio_chip_recognition(sdiodev, ci, regs); + ret = brcmf_sdio_chip_recognition(sdiodev, ci); if (ret != 0) goto err; brcmf_sdio_chip_buscoresetup(sdiodev, ci); - brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup), - 0, NULL); - brcmf_sdio_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown), - 0, NULL); + brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopullup), + 0, NULL); + brcmf_sdiod_regwl(sdiodev, CORE_CC_REG(ci->c_inf[0].base, gpiopulldown), + 0, NULL); *ci_ptr = ci; return 0; @@ -784,12 +784,12 @@ brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, } } addr = CORE_CC_REG(base, chipcontrol_addr); - brcmf_sdio_regwl(sdiodev, addr, 1, NULL); - cc_data_temp = brcmf_sdio_regrl(sdiodev, addr, NULL); + brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); + cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); cc_data_temp &= ~str_mask; drivestrength_sel <<= str_shift; cc_data_temp |= drivestrength_sel; - brcmf_sdio_regwl(sdiodev, addr, cc_data_temp, NULL); + brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", str_tab[i].strength, drivestrength, cc_data_temp); @@ -816,8 +816,8 @@ brcmf_sdio_chip_verifynvram(struct brcmf_sdio_dev *sdiodev, u32 nvram_addr, memset(nvram_ularray, 0xaa, nvram_sz); /* Read the vars list to temp buffer for comparison */ - err = brcmf_sdio_ramrw(sdiodev, false, nvram_addr, nvram_ularray, - nvram_sz); + err = brcmf_sdiod_ramrw(sdiodev, false, nvram_addr, nvram_ularray, + nvram_sz); if (err) { brcmf_err("error %d on reading %d nvram bytes at 0x%08x\n", err, nvram_sz, nvram_addr); @@ -850,7 +850,7 @@ static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev, nvram_addr = (ci->ramsize - 4) - nvram_sz + ci->rambase; /* Write the vars list */ - err = brcmf_sdio_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz); + err = brcmf_sdiod_ramrw(sdiodev, true, nvram_addr, nvram_dat, nvram_sz); if (err) { brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n", err, nvram_sz, nvram_addr); @@ -874,8 +874,8 @@ static bool brcmf_sdio_chip_writenvram(struct brcmf_sdio_dev *sdiodev, nvram_addr, nvram_sz, token); /* Write the length token to the last word */ - if (brcmf_sdio_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase), - (u8 *)&token_le, 4)) + if (brcmf_sdiod_ramrw(sdiodev, true, (ci->ramsize - 4 + ci->rambase), + (u8 *)&token_le, 4)) return false; return true; @@ -891,7 +891,7 @@ brcmf_sdio_chip_cm3_enterdl(struct brcmf_sdio_dev *sdiodev, ci->resetcore(sdiodev, ci, BCMA_CORE_INTERNAL_MEM, 0); /* clear length token */ - brcmf_sdio_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4); + brcmf_sdiod_ramrw(sdiodev, true, ci->ramsize - 4, (u8 *)&zeros, 4); } static bool @@ -913,7 +913,7 @@ brcmf_sdio_chip_cm3_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); reg_addr = ci->c_inf[core_idx].base; reg_addr += offsetof(struct sdpcmd_regs, intstatus); - brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CM3, 0); @@ -942,11 +942,11 @@ brcmf_sdio_chip_cr4_exitdl(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, core_idx = brcmf_sdio_chip_getinfidx(ci, BCMA_CORE_SDIO_DEV); reg_addr = ci->c_inf[core_idx].base; reg_addr += offsetof(struct sdpcmd_regs, intstatus); - brcmf_sdio_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); /* Write reset vector to address 0 */ - brcmf_sdio_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec, - sizeof(ci->rst_vec)); + brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&ci->rst_vec, + sizeof(ci->rst_vec)); /* restore ARM */ ci->resetcore(sdiodev, ci, BCMA_CORE_ARM_CR4, 0); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h index 507c61c991fa..d0f4b45b24c7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_chip.h @@ -224,7 +224,7 @@ struct sdpcmd_regs { }; int brcmf_sdio_chip_attach(struct brcmf_sdio_dev *sdiodev, - struct chip_info **ci_ptr, u32 regs); + struct chip_info **ci_ptr); void brcmf_sdio_chip_detach(struct chip_info **ci_ptr); void brcmf_sdio_chip_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, struct chip_info *ci, u32 drivestrength); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index fc0d4f0129db..a0981b32c729 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -164,9 +164,8 @@ struct brcmf_sdio; struct brcmf_sdio_dev { struct sdio_func *func[SDIO_MAX_FUNCS]; u8 num_funcs; /* Supported funcs on client */ - u32 func_cis_ptr[SDIOD_MAX_IOFUNCS]; u32 sbwad; /* Save backplane window address */ - void *bus; + struct brcmf_sdio *bus; atomic_t suspend; /* suspend flag */ wait_queue_head_t request_byte_wait; wait_queue_head_t request_word_wait; @@ -185,22 +184,19 @@ struct brcmf_sdio_dev { }; /* Register/deregister interrupt handler. */ -int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev); -int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev); +int brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev); /* sdio device register access interface */ -u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); -u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); -void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, - int *ret); -void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, - int *ret); -int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, - void *data, bool write); +u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); +u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret); +void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 data, + int *ret); +void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, u32 data, + int *ret); /* Buffer transfer to/from device (client) core via cmd53. * fn: function number - * addr: backplane address (i.e. >= regsva from attach) * flags: backplane width, address increment, sync/async * buf: pointer to memory data buffer * nbytes: number of bytes to transfer to/from buf @@ -210,17 +206,14 @@ int brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, * Returns 0 or error code. * NOTE: Async operation is not currently supported. */ -int brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq); -int brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, u8 *buf, uint nbytes); - -int brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff *pkt); -int brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, u8 *buf, uint nbytes); -int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, - uint flags, struct sk_buff_head *pktq, uint totlen); +int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq); +int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); + +int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt); +int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes); +int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev, + struct sk_buff_head *pktq, uint totlen); /* Flags bits */ @@ -236,43 +229,16 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, * nbytes: number of bytes to transfer to/from buf * Returns 0 or error code. */ -int brcmf_sdcard_rwdata(struct brcmf_sdio_dev *sdiodev, uint rw, u32 addr, - u8 *buf, uint nbytes); -int brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, - u8 *data, uint size); +int brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address, + u8 *data, uint size); /* Issue an abort to the specified function */ -int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn); - -/* platform specific/high level functions */ -int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); -int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev); - -/* attach, return handler on success, NULL if failed. - * The handler shall be provided by all subsequent calls. No local cache - * cfghdl points to the starting address of pci device mapped memory - */ -int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev); -void brcmf_sdioh_detach(struct brcmf_sdio_dev *sdiodev); - -/* read or write one byte using cmd52 */ -int brcmf_sdioh_request_byte(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc, - uint addr, u8 *byte); - -/* read or write 2/4 bytes using cmd53 */ -int brcmf_sdioh_request_word(struct brcmf_sdio_dev *sdiodev, uint rw, uint fnc, - uint addr, u32 *word, uint nbyte); - -/* Watchdog timer interface for pm ops */ -void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable); +int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn); -void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev); -void brcmf_sdbrcm_disconnect(void *ptr); -void brcmf_sdbrcm_isr(void *arg); +struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); +void brcmf_sdio_remove(struct brcmf_sdio *bus); +void brcmf_sdio_isr(struct brcmf_sdio *bus); -void brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick); +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick); -void brcmf_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, - wait_queue_head_t *wq); -bool brcmf_pm_resume_error(struct brcmf_sdio_dev *sdiodev); #endif /* _BRCM_SDH_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 51c4de054b15..c345c32eb631 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1253,6 +1253,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->ops = &brcmf_usb_bus_ops; bus->chip = bus_pub->devid; bus->chiprev = bus_pub->chiprev; + bus->proto_type = BRCMF_PROTO_BCDC; /* Attach to the common driver interface */ ret = brcmf_attach(dev); diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 217f1ca321a0..9f4239d31c08 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -322,12 +322,6 @@ static void iwlagn_mac_stop(struct ieee80211_hw *hw) flush_workqueue(priv->workqueue); - /* User space software may expect getting rfkill changes - * even if interface is down, trans->down will leave the RF - * kill interrupt enabled - */ - iwl_trans_stop_hw(priv->trans, false); - IWL_DEBUG_MAC80211(priv, "leave\n"); } diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 7aad766865cf..fd9f6cf96cfd 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -1313,7 +1313,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, } /* Reset chip to save power until we load uCode during "up". */ - iwl_trans_stop_hw(priv->trans, false); + iwl_trans_stop_device(priv->trans); priv->nvm_data = iwl_parse_eeprom_data(priv->trans->dev, priv->cfg, priv->eeprom_blob, @@ -1458,7 +1458,7 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) dev_kfree_skb(priv->beacon_skb); - iwl_trans_stop_hw(priv->trans, true); + iwl_trans_op_mode_leave(priv->trans); ieee80211_free_hw(priv->hw); } diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index 3c34a72a5d64..5fb37724c096 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -108,7 +108,7 @@ static const struct iwl_base_params iwl7000_base_params = { }; static const struct iwl_ht_params iwl7000_ht_params = { - .use_rts_for_aggregation = true, /* use rts/cts protection */ + .stbc = true, .ht40_bands = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ), }; diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 0b916249669e..e05440ff5cd4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -162,12 +162,14 @@ struct iwl_base_params { }; /* + * @stbc: support Tx STBC and 1*SS Rx STBC * @use_rts_for_aggregation: use rts/cts protection for HT traffic * @ht40_bands: bitmap of bands (using %IEEE80211_BAND_*) that support HT40 */ struct iwl_ht_params { enum ieee80211_smps_mode smps_mode; const bool ht_greenfield_support; /* if used set to true */ + const bool stbc; bool use_rts_for_aggregation; u8 ht40_bands; }; diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index ff570027e9dd..4bebfb58fc7b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -322,6 +322,41 @@ static void set_sec_offset(struct iwl_firmware_pieces *pieces, pieces->img[type].sec[sec].offset = offset; } +static int iwl_store_cscheme(struct iwl_fw *fw, const u8 *data, const u32 len) +{ + int i, j; + struct iwl_fw_cscheme_list *l = (struct iwl_fw_cscheme_list *)data; + struct iwl_fw_cipher_scheme *fwcs; + struct ieee80211_cipher_scheme *cs; + u32 cipher; + + if (len < sizeof(*l) || + len < sizeof(l->size) + l->size * sizeof(l->cs[0])) + return -EINVAL; + + for (i = 0, j = 0; i < IWL_UCODE_MAX_CS && i < l->size; i++) { + fwcs = &l->cs[j]; + cipher = le32_to_cpu(fwcs->cipher); + + /* we skip schemes with zero cipher suite selector */ + if (!cipher) + continue; + + cs = &fw->cs[j++]; + cs->cipher = cipher; + cs->iftype = BIT(NL80211_IFTYPE_STATION); + cs->hdr_len = fwcs->hdr_len; + cs->pn_len = fwcs->pn_len; + cs->pn_off = fwcs->pn_off; + cs->key_idx_off = fwcs->key_idx_off; + cs->key_idx_mask = fwcs->key_idx_mask; + cs->key_idx_shift = fwcs->key_idx_shift; + cs->mic_len = fwcs->mic_len; + } + + return 0; +} + /* * Gets uCode section from tlv. */ @@ -729,6 +764,10 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, return -EINVAL; } break; + case IWL_UCODE_TLV_CSCHEME: + if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len)) + goto invalid_tlv_len; + break; default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); break; diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index f4a6d317a023..4380c16580eb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -751,6 +751,13 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, ht_info->ht_supported = true; ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40; + if (cfg->ht_params->stbc) { + ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); + + if (tx_chains > 1) + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + } + if (iwlwifi_mod_params.amsdu_size_8K) ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index 6c6c35c5228c..4f95734b2811 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -125,6 +125,7 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_SECURE_SEC_INIT = 25, IWL_UCODE_TLV_SECURE_SEC_WOWLAN = 26, IWL_UCODE_TLV_NUM_OF_CPU = 27, + IWL_UCODE_TLV_CSCHEME = 28, }; struct iwl_ucode_tlv { diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 75db087120c3..8704e3042ca1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -92,6 +92,9 @@ * @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API * @IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command * containing CAM (Continuous Active Mode) indication. + * @IWL_UCODE_TLV_FLAGS_P2P_PS: P2P client power save is supported (only on a + * single bound interface). + * @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save */ enum iwl_ucode_tlv_flag { IWL_UCODE_TLV_FLAGS_PAN = BIT(0), @@ -113,7 +116,9 @@ enum iwl_ucode_tlv_flag { IWL_UCODE_TLV_FLAGS_SCHED_SCAN = BIT(17), IWL_UCODE_TLV_FLAGS_STA_KEY_CMD = BIT(19), IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD = BIT(20), + IWL_UCODE_TLV_FLAGS_P2P_PS = BIT(21), IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT = BIT(24), + IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD = BIT(26), }; /* The default calibrate table size if not specified by firmware file */ @@ -209,6 +214,44 @@ enum iwl_fw_phy_cfg { FW_PHY_CFG_RX_CHAIN = 0xf << FW_PHY_CFG_RX_CHAIN_POS, }; +#define IWL_UCODE_MAX_CS 1 + +/** + * struct iwl_fw_cipher_scheme - a cipher scheme supported by FW. + * @cipher: a cipher suite selector + * @flags: cipher scheme flags (currently reserved for a future use) + * @hdr_len: a size of MPDU security header + * @pn_len: a size of PN + * @pn_off: an offset of pn from the beginning of the security header + * @key_idx_off: an offset of key index byte in the security header + * @key_idx_mask: a bit mask of key_idx bits + * @key_idx_shift: bit shift needed to get key_idx + * @mic_len: mic length in bytes + * @hw_cipher: a HW cipher index used in host commands + */ +struct iwl_fw_cipher_scheme { + __le32 cipher; + u8 flags; + u8 hdr_len; + u8 pn_len; + u8 pn_off; + u8 key_idx_off; + u8 key_idx_mask; + u8 key_idx_shift; + u8 mic_len; + u8 hw_cipher; +} __packed; + +/** + * struct iwl_fw_cscheme_list - a cipher scheme list + * @size: a number of entries + * @cs: cipher scheme entries + */ +struct iwl_fw_cscheme_list { + u8 size; + struct iwl_fw_cipher_scheme cs[]; +} __packed; + /** * struct iwl_fw - variables associated with the firmware * @@ -224,6 +267,7 @@ enum iwl_fw_phy_cfg { * @inst_evtlog_size: event log size for runtime ucode. * @inst_errlog_ptr: error log offfset for runtime ucode. * @mvm_fw: indicates this is MVM firmware + * @cipher_scheme: optional external cipher scheme. */ struct iwl_fw { u32 ucode_ver; @@ -243,6 +287,8 @@ struct iwl_fw { u32 phy_config; bool mvm_fw; + + struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; }; static inline u8 iwl_fw_valid_tx_ant(const struct iwl_fw *fw) diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 94aef22df73a..a48decc6c68f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -263,13 +263,20 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, struct iwl_nvm_data *data, struct ieee80211_sta_vht_cap *vht_cap) { + int num_ants = num_of_ant(data->valid_rx_ant); + int bf_sts_cap = num_ants - 1; + vht_cap->vht_supported = true; vht_cap->cap = IEEE80211_VHT_CAP_SHORT_GI_80 | IEEE80211_VHT_CAP_RXSTBC_1 | IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | + bf_sts_cap << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT | 7 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; + if (num_ants > 1) + vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC; + if (iwlwifi_mod_params.amsdu_size_8K) vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991; @@ -283,16 +290,22 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | IEEE80211_VHT_MCS_NOT_SUPPORTED << 14); - if (num_of_ant(data->valid_rx_ant) == 1 || + /* Max rate for Long GI NSS=2 80Mhz is 780Mbps */ + vht_cap->vht_mcs.rx_highest = cpu_to_le16(780); + + if (num_ants == 1 || cfg->rx_with_siso_diversity) { vht_cap->cap |= IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN | IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; /* this works because NOT_SUPPORTED == 3 */ vht_cap->vht_mcs.rx_mcs_map |= cpu_to_le16(IEEE80211_VHT_MCS_NOT_SUPPORTED << 2); + /* Max rate for Long GI NSS=1 80Mhz is 390Mbps */ + vht_cap->vht_mcs.rx_highest = cpu_to_le16(390); } vht_cap->vht_mcs.tx_mcs_map = vht_cap->vht_mcs.rx_mcs_map; + vht_cap->vht_mcs.tx_highest = vht_cap->vht_mcs.rx_highest; } static void iwl_init_sbands(struct device *dev, const struct iwl_cfg *cfg, diff --git a/drivers/net/wireless/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/iwlwifi/iwl-op-mode.h index 976448a57d02..f50e6c62ebc5 100644 --- a/drivers/net/wireless/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/iwlwifi/iwl-op-mode.h @@ -155,14 +155,12 @@ void iwl_opmode_deregister(const char *name); /** * struct iwl_op_mode - operational mode + * @ops - pointer to its own ops * * This holds an implementation of the mac80211 / fw API. - * - * @ops - pointer to its own ops */ struct iwl_op_mode { const struct iwl_op_mode_ops *ops; - const struct iwl_trans *trans; char op_mode_specific[0] __aligned(sizeof(void *)); }; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 143292b4dbbf..0c3647858909 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -70,6 +70,7 @@ #include "iwl-debug.h" #include "iwl-config.h" #include "iwl-fw.h" +#include "iwl-op-mode.h" /** * DOC: Transport layer - what is it ? @@ -100,8 +101,7 @@ * start_fw * * 5) Then when finished (or reset): - * stop_fw (a.k.a. stop device for the moment) - * stop_hw + * stop_device * * 6) Eventually, the free function will be called. */ @@ -318,6 +318,24 @@ enum iwl_d3_status { }; /** + * enum iwl_trans_status: transport status flags + * @STATUS_SYNC_HCMD_ACTIVE: a SYNC command is being processed + * @STATUS_DEVICE_ENABLED: APM is enabled + * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up) + * @STATUS_INT_ENABLED: interrupts are enabled + * @STATUS_RFKILL: the HW RFkill switch is in KILL position + * @STATUS_FW_ERROR: the fw is in error state + */ +enum iwl_trans_status { + STATUS_SYNC_HCMD_ACTIVE, + STATUS_DEVICE_ENABLED, + STATUS_TPOWER_PMI, + STATUS_INT_ENABLED, + STATUS_RFKILL, + STATUS_FW_ERROR, +}; + +/** * struct iwl_trans_config - transport configuration * * @op_mode: pointer to the upper layer. @@ -361,9 +379,7 @@ struct iwl_trans; * * @start_hw: starts the HW- from that point on, the HW can send interrupts * May sleep - * @stop_hw: stops the HW- from that point on, the HW will be in low power but - * will still issue interrupt if the HW RF kill is triggered unless - * op_mode_leaving is true. + * @op_mode_leave: Turn off the HW RF kill indication if on * May sleep * @start_fw: allocates and inits all the resources for the transport * layer. Also kick a fw image. @@ -371,8 +387,11 @@ struct iwl_trans; * @fw_alive: called when the fw sends alive notification. If the fw provides * the SCD base address in SRAM, then provide it here, or 0 otherwise. * May sleep - * @stop_device:stops the whole device (embedded CPU put to reset) - * May sleep + * @stop_device: stops the whole device (embedded CPU put to reset) and stops + * the HW. From that point on, the HW will be in low power but will still + * issue interrupt if the HW RF kill is triggered. This callback must do + * the right thing and not crash even if start_hw() was called but not + * start_fw(). May sleep * @d3_suspend: put the device into the correct mode for WoWLAN during * suspend. This is optional, if not implemented WoWLAN will not be * supported. This callback may sleep. @@ -418,7 +437,7 @@ struct iwl_trans; struct iwl_trans_ops { int (*start_hw)(struct iwl_trans *iwl_trans); - void (*stop_hw)(struct iwl_trans *iwl_trans, bool op_mode_leaving); + void (*op_mode_leave)(struct iwl_trans *iwl_trans); int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill); void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); @@ -479,6 +498,7 @@ enum iwl_trans_state { * @ops - pointer to iwl_trans_ops * @op_mode - pointer to the op_mode * @cfg - pointer to the configuration + * @status: a bit-mask of transport status flags * @dev - pointer to struct device * that represents the device * @hw_id: a u32 with the ID of the device / subdevice. * Set during transport allocation. @@ -499,6 +519,7 @@ struct iwl_trans { struct iwl_op_mode *op_mode; const struct iwl_cfg *cfg; enum iwl_trans_state state; + unsigned long status; struct device *dev; u32 hw_rev; @@ -540,15 +561,14 @@ static inline int iwl_trans_start_hw(struct iwl_trans *trans) return trans->ops->start_hw(trans); } -static inline void iwl_trans_stop_hw(struct iwl_trans *trans, - bool op_mode_leaving) +static inline void iwl_trans_op_mode_leave(struct iwl_trans *trans) { might_sleep(); - trans->ops->stop_hw(trans, op_mode_leaving); + if (trans->ops->op_mode_leave) + trans->ops->op_mode_leave(trans); - if (op_mode_leaving) - trans->op_mode = NULL; + trans->op_mode = NULL; trans->state = IWL_TRANS_NO_FW; } @@ -570,6 +590,7 @@ static inline int iwl_trans_start_fw(struct iwl_trans *trans, WARN_ON_ONCE(!trans->rx_mpdu_cmd); + clear_bit(STATUS_FW_ERROR, &trans->status); return trans->ops->start_fw(trans, fw, run_in_rfkill); } @@ -601,6 +622,9 @@ static inline int iwl_trans_send_cmd(struct iwl_trans *trans, { int ret; + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return -EIO; + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) { IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); return -EIO; @@ -640,6 +664,9 @@ static inline void iwl_trans_free_tx_cmd(struct iwl_trans *trans, static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, int queue) { + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return -EIO; + if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) IWL_ERR(trans, "%s bad state = %d", __func__, trans->state); @@ -760,7 +787,8 @@ static inline u32 iwl_trans_write_mem32(struct iwl_trans *trans, u32 addr, static inline void iwl_trans_set_pmi(struct iwl_trans *trans, bool state) { - trans->ops->set_pmi(trans, state); + if (trans->ops->set_pmi) + trans->ops->set_pmi(trans, state); } static inline void @@ -780,6 +808,16 @@ iwl_trans_release_nic_access(struct iwl_trans *trans, unsigned long *flags) __release(nic_access); } +static inline void iwl_trans_fw_error(struct iwl_trans *trans) +{ + if (WARN_ON_ONCE(!trans->op_mode)) + return; + + /* prevent double restarts due to the same erroneous FW */ + if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status)) + iwl_op_mode_nic_error(trans->op_mode); +} + /***************************************************** * driver (transport) register/unregister functions ******************************************************/ diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index 285d8c7486f5..f98ec2b23898 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile @@ -1,10 +1,9 @@ obj-$(CONFIG_IWLMVM) += iwlmvm.o iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o -iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o +iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o iwlmvm-y += scan.o time-event.o rs.o iwlmvm-y += power.o power_legacy.o bt-coex.o iwlmvm-y += led.o tt.o -iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o iwlmvm-$(CONFIG_PM_SLEEP) += d3.o diff --git a/drivers/net/wireless/iwlwifi/mvm/binding.c b/drivers/net/wireless/iwlwifi/mvm/binding.c index 93fd1457954b..57d3eed86efa 100644 --- a/drivers/net/wireless/iwlwifi/mvm/binding.c +++ b/drivers/net/wireless/iwlwifi/mvm/binding.c @@ -183,15 +183,29 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) return -EINVAL; + /* + * Update SF - Disable if needed. if this fails, SF might still be on + * while many macs are bound, which is forbidden - so fail the binding. + */ + if (iwl_mvm_sf_update(mvm, vif, false)) + return -EINVAL; + return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, true); } int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int ret; if (WARN_ON_ONCE(!mvmvif->phy_ctxt)) return -EINVAL; - return iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); + ret = iwl_mvm_binding_update(mvm, vif, mvmvif->phy_ctxt, false); + + if (!ret) + if (iwl_mvm_sf_update(mvm, vif, true)) + IWL_ERR(mvm, "Failed to update SF state\n"); + + return ret; } diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c index 67f6a2071653..b8667575bc10 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -63,6 +63,150 @@ #include "mvm.h" #include "debugfs.h" +static void iwl_dbgfs_update_pm(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + enum iwl_dbgfs_pm_mask param, int val) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_pm *dbgfs_pm = &mvmvif->dbgfs_pm; + + dbgfs_pm->mask |= param; + + switch (param) { + case MVM_DEBUGFS_PM_KEEP_ALIVE: { + struct ieee80211_hw *hw = mvm->hw; + int dtimper = hw->conf.ps_dtim_period ?: 1; + int dtimper_msec = dtimper * vif->bss_conf.beacon_int; + + IWL_DEBUG_POWER(mvm, "debugfs: set keep_alive= %d sec\n", val); + if (val * MSEC_PER_SEC < 3 * dtimper_msec) + IWL_WARN(mvm, + "debugfs: keep alive period (%ld msec) is less than minimum required (%d msec)\n", + val * MSEC_PER_SEC, 3 * dtimper_msec); + dbgfs_pm->keep_alive_seconds = val; + break; + } + case MVM_DEBUGFS_PM_SKIP_OVER_DTIM: + IWL_DEBUG_POWER(mvm, "skip_over_dtim %s\n", + val ? "enabled" : "disabled"); + dbgfs_pm->skip_over_dtim = val; + break; + case MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS: + IWL_DEBUG_POWER(mvm, "skip_dtim_periods=%d\n", val); + dbgfs_pm->skip_dtim_periods = val; + break; + case MVM_DEBUGFS_PM_RX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "rx_data_timeout=%d\n", val); + dbgfs_pm->rx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_TX_DATA_TIMEOUT: + IWL_DEBUG_POWER(mvm, "tx_data_timeout=%d\n", val); + dbgfs_pm->tx_data_timeout = val; + break; + case MVM_DEBUGFS_PM_DISABLE_POWER_OFF: + IWL_DEBUG_POWER(mvm, "disable_power_off=%d\n", val); + dbgfs_pm->disable_power_off = val; + break; + case MVM_DEBUGFS_PM_LPRX_ENA: + IWL_DEBUG_POWER(mvm, "lprx %s\n", val ? "enabled" : "disabled"); + dbgfs_pm->lprx_ena = val; + break; + case MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD: + IWL_DEBUG_POWER(mvm, "lprx_rssi_threshold=%d\n", val); + dbgfs_pm->lprx_rssi_threshold = val; + break; + case MVM_DEBUGFS_PM_SNOOZE_ENABLE: + IWL_DEBUG_POWER(mvm, "snooze_enable=%d\n", val); + dbgfs_pm->snooze_ena = val; + break; + case MVM_DEBUGFS_PM_UAPSD_MISBEHAVING: + IWL_DEBUG_POWER(mvm, "uapsd_misbehaving_enable=%d\n", val); + dbgfs_pm->uapsd_misbehaving = val; + break; + } +} + +static ssize_t iwl_dbgfs_pm_params_write(struct ieee80211_vif *vif, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + enum iwl_dbgfs_pm_mask param; + int val, ret; + + if (!strncmp("keep_alive=", buf, 11)) { + if (sscanf(buf + 11, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_KEEP_ALIVE; + } else if (!strncmp("skip_over_dtim=", buf, 15)) { + if (sscanf(buf + 15, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_OVER_DTIM; + } else if (!strncmp("skip_dtim_periods=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SKIP_DTIM_PERIODS; + } else if (!strncmp("rx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_RX_DATA_TIMEOUT; + } else if (!strncmp("tx_data_timeout=", buf, 16)) { + if (sscanf(buf + 16, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_TX_DATA_TIMEOUT; + } else if (!strncmp("disable_power_off=", buf, 18) && + !(mvm->fw->ucode_capa.flags & + IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_DISABLE_POWER_OFF; + } else if (!strncmp("lprx=", buf, 5)) { + if (sscanf(buf + 5, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_LPRX_ENA; + } else if (!strncmp("lprx_rssi_threshold=", buf, 20)) { + if (sscanf(buf + 20, "%d", &val) != 1) + return -EINVAL; + if (val > POWER_LPRX_RSSI_THRESHOLD_MAX || val < + POWER_LPRX_RSSI_THRESHOLD_MIN) + return -EINVAL; + param = MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD; + } else if (!strncmp("snooze_enable=", buf, 14)) { + if (sscanf(buf + 14, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_SNOOZE_ENABLE; + } else if (!strncmp("uapsd_misbehaving=", buf, 18)) { + if (sscanf(buf + 18, "%d", &val) != 1) + return -EINVAL; + param = MVM_DEBUGFS_PM_UAPSD_MISBEHAVING; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_pm(mvm, vif, param, val); + ret = iwl_mvm_power_update_mode(mvm, vif); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_pm_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + char buf[512]; + int bufsz = sizeof(buf); + int pos; + + pos = iwl_mvm_power_dbgfs_read(mvm, vif, buf, bufsz); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + static ssize_t iwl_dbgfs_mac_params_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -98,14 +242,17 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file, if (vif->type == NL80211_IFTYPE_STATION && ap_sta_id != IWL_MVM_STATION_COUNT) { struct ieee80211_sta *sta; - struct iwl_mvm_sta *mvm_sta; sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], lockdep_is_held(&mvm->mutex)); - mvm_sta = (void *)sta->drv_priv; - pos += scnprintf(buf+pos, bufsz-pos, - "ap_sta_id %d - reduced Tx power %d\n", - ap_sta_id, mvm_sta->bt_reduced_txpower); + if (!IS_ERR_OR_NULL(sta)) { + struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + + pos += scnprintf(buf+pos, bufsz-pos, + "ap_sta_id %d - reduced Tx power %d\n", + ap_sta_id, + mvm_sta->bt_reduced_txpower); + } } rcu_read_lock(); @@ -122,6 +269,201 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } +static void iwl_dbgfs_update_bf(struct ieee80211_vif *vif, + enum iwl_dbgfs_bf_mask param, int value) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_dbgfs_bf *dbgfs_bf = &mvmvif->dbgfs_bf; + + dbgfs_bf->mask |= param; + + switch (param) { + case MVM_DEBUGFS_BF_ENERGY_DELTA: + dbgfs_bf->bf_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA: + dbgfs_bf->bf_roaming_energy_delta = value; + break; + case MVM_DEBUGFS_BF_ROAMING_STATE: + dbgfs_bf->bf_roaming_state = value; + break; + case MVM_DEBUGFS_BF_TEMP_THRESHOLD: + dbgfs_bf->bf_temp_threshold = value; + break; + case MVM_DEBUGFS_BF_TEMP_FAST_FILTER: + dbgfs_bf->bf_temp_fast_filter = value; + break; + case MVM_DEBUGFS_BF_TEMP_SLOW_FILTER: + dbgfs_bf->bf_temp_slow_filter = value; + break; + case MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER: + dbgfs_bf->bf_enable_beacon_filter = value; + break; + case MVM_DEBUGFS_BF_DEBUG_FLAG: + dbgfs_bf->bf_debug_flag = value; + break; + case MVM_DEBUGFS_BF_ESCAPE_TIMER: + dbgfs_bf->bf_escape_timer = value; + break; + case MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT: + dbgfs_bf->ba_enable_beacon_abort = value; + break; + case MVM_DEBUGFS_BA_ESCAPE_TIMER: + dbgfs_bf->ba_escape_timer = value; + break; + } +} + +static ssize_t iwl_dbgfs_bf_params_write(struct ieee80211_vif *vif, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + enum iwl_dbgfs_bf_mask param; + int value, ret = 0; + + if (!strncmp("bf_energy_delta=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ENERGY_DELTA_MIN || + value > IWL_BF_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_energy_delta=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_ENERGY_DELTA_MIN || + value > IWL_BF_ROAMING_ENERGY_DELTA_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_ENERGY_DELTA; + } else if (!strncmp("bf_roaming_state=", buf, 17)) { + if (sscanf(buf+17, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ROAMING_STATE_MIN || + value > IWL_BF_ROAMING_STATE_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ROAMING_STATE; + } else if (!strncmp("bf_temp_threshold=", buf, 18)) { + if (sscanf(buf+18, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_THRESHOLD_MIN || + value > IWL_BF_TEMP_THRESHOLD_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_THRESHOLD; + } else if (!strncmp("bf_temp_fast_filter=", buf, 20)) { + if (sscanf(buf+20, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_FAST_FILTER_MIN || + value > IWL_BF_TEMP_FAST_FILTER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_FAST_FILTER; + } else if (!strncmp("bf_temp_slow_filter=", buf, 20)) { + if (sscanf(buf+20, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_TEMP_SLOW_FILTER_MIN || + value > IWL_BF_TEMP_SLOW_FILTER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_TEMP_SLOW_FILTER; + } else if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { + if (sscanf(buf+24, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER; + } else if (!strncmp("bf_debug_flag=", buf, 14)) { + if (sscanf(buf+14, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BF_DEBUG_FLAG; + } else if (!strncmp("bf_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BF_ESCAPE_TIMER_MIN || + value > IWL_BF_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BF_ESCAPE_TIMER; + } else if (!strncmp("ba_escape_timer=", buf, 16)) { + if (sscanf(buf+16, "%d", &value) != 1) + return -EINVAL; + if (value < IWL_BA_ESCAPE_TIMER_MIN || + value > IWL_BA_ESCAPE_TIMER_MAX) + return -EINVAL; + param = MVM_DEBUGFS_BA_ESCAPE_TIMER; + } else if (!strncmp("ba_enable_beacon_abort=", buf, 23)) { + if (sscanf(buf+23, "%d", &value) != 1) + return -EINVAL; + if (value < 0 || value > 1) + return -EINVAL; + param = MVM_DEBUGFS_BA_ENABLE_BEACON_ABORT; + } else { + return -EINVAL; + } + + mutex_lock(&mvm->mutex); + iwl_dbgfs_update_bf(vif, param, value); + if (param == MVM_DEBUGFS_BF_ENABLE_BEACON_FILTER && !value) + ret = iwl_mvm_disable_beacon_filter(mvm, vif); + else + ret = iwl_mvm_enable_beacon_filter(mvm, vif); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_bf_params_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = + cpu_to_le32(IWL_BF_ENABLE_BEACON_FILTER_DEFAULT), + .ba_enable_beacon_abort = + cpu_to_le32(IWL_BA_ENABLE_BEACON_ABORT_DEFAULT), + }; + + iwl_mvm_beacon_filter_debugfs_parameters(vif, &cmd); + if (mvmvif->bf_data.bf_enabled) + cmd.bf_enable_beacon_filter = cpu_to_le32(1); + else + cmd.bf_enable_beacon_filter = 0; + + pos += scnprintf(buf+pos, bufsz-pos, "bf_energy_delta = %d\n", + le32_to_cpu(cmd.bf_energy_delta)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_energy_delta = %d\n", + le32_to_cpu(cmd.bf_roaming_energy_delta)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_roaming_state = %d\n", + le32_to_cpu(cmd.bf_roaming_state)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_threshold = %d\n", + le32_to_cpu(cmd.bf_temp_threshold)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_fast_filter = %d\n", + le32_to_cpu(cmd.bf_temp_fast_filter)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_temp_slow_filter = %d\n", + le32_to_cpu(cmd.bf_temp_slow_filter)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_enable_beacon_filter = %d\n", + le32_to_cpu(cmd.bf_enable_beacon_filter)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_debug_flag = %d\n", + le32_to_cpu(cmd.bf_debug_flag)); + pos += scnprintf(buf+pos, bufsz-pos, "bf_escape_timer = %d\n", + le32_to_cpu(cmd.bf_escape_timer)); + pos += scnprintf(buf+pos, bufsz-pos, "ba_escape_timer = %d\n", + le32_to_cpu(cmd.ba_escape_timer)); + pos += scnprintf(buf+pos, bufsz-pos, "ba_enable_beacon_abort = %d\n", + le32_to_cpu(cmd.ba_enable_beacon_abort)); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) +#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) #define MVM_DEBUGFS_ADD_FILE_VIF(name, parent, mode) do { \ if (!debugfs_create_file(#name, mode, parent, vif, \ &iwl_dbgfs_##name##_ops)) \ @@ -129,6 +471,8 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file, } while (0) MVM_DEBUGFS_READ_FILE_OPS(mac_params); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256); void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -152,9 +496,21 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return; } + if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && + ((vif->type == NL80211_IFTYPE_STATION && !vif->p2p) || + (vif->type == NL80211_IFTYPE_STATION && vif->p2p && + mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS))) + MVM_DEBUGFS_ADD_FILE_VIF(pm_params, mvmvif->dbgfs_dir, S_IWUSR | + S_IRUSR); + MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); + if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && + mvmvif == mvm->bf_allowed_vif) + MVM_DEBUGFS_ADD_FILE_VIF(bf_params, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); + /* * Create symlink for convenience pointing to interface specific * debugfs entries for the driver. For example, under diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index 5cb93ae5cd2f..cb78e5539357 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -85,6 +85,8 @@ * PBW Snoozing enabled * @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. + * @POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving + * detection enablement */ enum iwl_power_flags { POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), @@ -94,6 +96,7 @@ enum iwl_power_flags { POWER_FLAGS_BT_SCO_ENA = BIT(8), POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9), POWER_FLAGS_LPRX_ENA_MSK = BIT(11), + POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12), }; #define IWL_POWER_VEC_SIZE 5 @@ -228,6 +231,19 @@ struct iwl_mac_power_cmd { u8 reserved; } __packed; +/* + * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when + * associated AP is identified as improperly implementing uAPSD protocol. + * PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78 + * @sta_id: index of station in uCode's station table - associated AP ID in + * this context. + */ +struct iwl_uapsd_misbehaving_ap_notif { + __le32 sta_id; + u8 mac_id; + u8 reserved[3]; +} __packed; + /** * struct iwl_beacon_filter_cmd * REPLY_BEACON_FILTERING_CMD = 0xd2 (command) diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h index 4aca5933a65d..8c73ba74b6fd 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h @@ -138,7 +138,14 @@ enum iwl_sta_flags { /** * enum iwl_sta_key_flag - key flags for the ADD_STA host command - * @STA_KEY_FLG_EN_MSK: mask for encryption algorithm + * @STA_KEY_FLG_NO_ENC: no encryption + * @STA_KEY_FLG_WEP: WEP encryption algorithm + * @STA_KEY_FLG_CCM: CCMP encryption algorithm + * @STA_KEY_FLG_TKIP: TKIP encryption algorithm + * @STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support) + * @STA_KEY_FLG_CMAC: CMAC encryption algorithm + * @STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm + * @STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value * @STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from * station info array (1 - n 1X mode) * @STA_KEY_FLG_KEYID_MSK: the index of the key @@ -152,6 +159,7 @@ enum iwl_sta_key_flag { STA_KEY_FLG_WEP = (1 << 0), STA_KEY_FLG_CCM = (2 << 0), STA_KEY_FLG_TKIP = (3 << 0), + STA_KEY_FLG_EXT = (4 << 0), STA_KEY_FLG_CMAC = (6 << 0), STA_KEY_FLG_ENC_UNKNOWN = (7 << 0), STA_KEY_FLG_EN_MSK = (7 << 0), diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index d606197bde8f..22864671d66c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -132,6 +132,7 @@ enum iwl_tx_flags { #define TX_CMD_SEC_WEP 0x01 #define TX_CMD_SEC_CCM 0x02 #define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_EXT 0x04 #define TX_CMD_SEC_MSK 0x07 #define TX_CMD_SEC_WEP_KEY_IDX_POS 6 #define TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index bad5a552dd8d..1c3079714c2b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -141,6 +141,7 @@ enum { /* Power - legacy power table command */ POWER_TABLE_CMD = 0x77, + PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78, /* Thermal Throttling*/ REPLY_THERMAL_MNG_BACKOFF = 0x7e, @@ -183,6 +184,7 @@ enum { BT_PROFILE_NOTIFICATION = 0xce, BT_COEX_CI = 0x5d, + REPLY_SF_CFG_CMD = 0xd1, REPLY_BEACON_FILTERING_CMD = 0xd2, REPLY_DEBUG_CMD = 0xf0, @@ -1052,6 +1054,7 @@ enum iwl_mvm_rx_status { RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8), RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8), RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8), + RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8), RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8), RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8), RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8), @@ -1131,6 +1134,7 @@ struct iwl_set_calib_default_cmd { } __packed; /* PHY_CALIB_OVERRIDE_VALUES_S */ #define MAX_PORT_ID_NUM 2 +#define MAX_MCAST_FILTERING_ADDRESSES 256 /** * struct iwl_mcast_filter_cmd - configure multicast filter. @@ -1363,4 +1367,65 @@ struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */ struct mvm_statistics_general general; } __packed; +/*********************************** + * Smart Fifo API + ***********************************/ +/* Smart Fifo state */ +enum iwl_sf_state { + SF_LONG_DELAY_ON = 0, /* should never be called by driver */ + SF_FULL_ON, + SF_UNINIT, + SF_INIT_OFF, + SF_HW_NUM_STATES +}; + +/* Smart Fifo possible scenario */ +enum iwl_sf_scenario { + SF_SCENARIO_SINGLE_UNICAST, + SF_SCENARIO_AGG_UNICAST, + SF_SCENARIO_MULTICAST, + SF_SCENARIO_BA_RESP, + SF_SCENARIO_TX_RESP, + SF_NUM_SCENARIO +}; + +#define SF_TRANSIENT_STATES_NUMBER 2 /* SF_LONG_DELAY_ON and SF_FULL_ON */ +#define SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */ + +/* smart FIFO default values */ +#define SF_W_MARK_SISO 4096 +#define SF_W_MARK_MIMO2 8192 +#define SF_W_MARK_MIMO3 6144 +#define SF_W_MARK_LEGACY 4096 +#define SF_W_MARK_SCAN 4096 + +/* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */ +#define SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */ +#define SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */ +#define SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */ +#define SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */ +#define SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */ +#define SF_MCAST_AGING_TIMER 10016 /* 10 mSec */ +#define SF_BA_IDLE_TIMER 320 /* 300 uSec */ +#define SF_BA_AGING_TIMER 2016 /* 2 mSec */ +#define SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */ +#define SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */ + +#define SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */ + +/** + * Smart Fifo configuration command. + * @state: smart fifo state, types listed in iwl_sf_sate. + * @watermark: Minimum allowed availabe free space in RXF for transient state. + * @long_delay_timeouts: aging and idle timer values for each scenario + * in long delay state. + * @full_on_timeouts: timer values for each scenario in full on state. + */ +struct iwl_sf_cfg_cmd { + enum iwl_sf_state state; + __le32 watermark[SF_TRANSIENT_STATES_NUMBER]; + __le32 long_delay_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; + __le32 full_on_timeouts[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES]; +} __packed; /* SF_CFG_API_S_VER_2 */ + #endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index 70e5297646b2..27ba104a3540 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -241,7 +241,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) lockdep_assert_held(&mvm->mutex); - if (mvm->init_ucode_complete) + if (WARN_ON_ONCE(mvm->init_ucode_complete)) return 0; iwl_init_notification_wait(&mvm->notif_wait, @@ -287,7 +287,8 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) IWL_DEBUG_RF_KILL(mvm, "jump over all phy activities due to RF kill\n"); iwl_remove_notification(&mvm->notif_wait, &calib_wait); - return 1; + ret = 1; + goto out; } /* Send TX valid antennas before triggering calibrations */ @@ -319,9 +320,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) error: iwl_remove_notification(&mvm->notif_wait, &calib_wait); out: - if (!iwlmvm_mod_params.init_dbg) { - iwl_trans_stop_device(mvm->trans); - } else if (!mvm->nvm_data) { + if (iwlmvm_mod_params.init_dbg && !mvm->nvm_data) { /* we want to debug INIT and we have no NVM - fake */ mvm->nvm_data = kzalloc(sizeof(struct iwl_nvm_data) + sizeof(struct ieee80211_channel) + @@ -370,11 +369,16 @@ int iwl_mvm_up(struct iwl_mvm *mvm) ret = -ERFKILL; goto error; } - /* should stop & start HW since that INIT image just loaded */ - iwl_trans_stop_hw(mvm->trans, false); - ret = iwl_trans_start_hw(mvm->trans); - if (ret) - return ret; + if (!iwlmvm_mod_params.init_dbg) { + /* + * should stop and start HW since that INIT + * image just loaded + */ + iwl_trans_stop_device(mvm->trans); + ret = iwl_trans_start_hw(mvm->trans); + if (ret) + return ret; + } } if (iwlmvm_mod_params.init_dbg) @@ -386,6 +390,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm) goto error; } + ret = iwl_mvm_sf_update(mvm, NULL, false); + if (ret) + IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); + ret = iwl_send_tx_ant_cfg(mvm, iwl_fw_valid_tx_ant(mvm->fw)); if (ret) goto error; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index afc4419be46d..2f5269359dce 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -261,6 +261,12 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; + /* currently FW API supports only one optional cipher scheme */ + if (mvm->fw->cs && mvm->fw->cs->cipher) { + mvm->hw->n_cipher_schemes = 1; + mvm->hw->cipher_schemes = mvm->fw->cs; + } + #ifdef CONFIG_PM_SLEEP if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && mvm->trans->ops->d3_suspend && @@ -399,7 +405,6 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) { iwl_trans_stop_device(mvm->trans); - iwl_trans_stop_hw(mvm->trans, false); mvm->scan_status = IWL_MVM_SCAN_NONE; @@ -471,7 +476,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) cancel_work_sync(&mvm->roc_done_wk); iwl_trans_stop_device(mvm->trans); - iwl_trans_stop_hw(mvm->trans, false); iwl_mvm_async_handlers_purge(mvm); /* async_handlers_list is empty and will stay empty: HW is stopped */ @@ -488,17 +492,6 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) cancel_work_sync(&mvm->async_handlers_wk); } -static void iwl_mvm_pm_disable_iterator(void *data, u8 *mac, - struct ieee80211_vif *vif) -{ - struct iwl_mvm *mvm = data; - int ret; - - ret = iwl_mvm_power_disable(mvm, vif); - if (ret) - IWL_ERR(mvm, "failed to disable power management\n"); -} - static void iwl_mvm_power_update_iterator(void *data, u8 *mac, struct ieee80211_vif *vif) { @@ -521,6 +514,20 @@ static struct iwl_mvm_phy_ctxt *iwl_mvm_get_free_phy_ctxt(struct iwl_mvm *mvm) return NULL; } +static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + s8 tx_power) +{ + /* FW is in charge of regulatory enforcement */ + struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = { + .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id, + .pwr_restriction = cpu_to_le16(tx_power), + }; + + return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC, + sizeof(reduce_txpwr_cmd), + &reduce_txpwr_cmd); +} + static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -541,26 +548,9 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, if (ret) goto out_unlock; - /* - * TODO: remove this temporary code. - * Currently MVM FW supports power management only on single MAC. - * If new interface added, disable PM on existing interface. - * P2P device is a special case, since it is handled by FW similary to - * scan. If P2P deviced is added, PM remains enabled on existing - * interface. - * Note: the method below does not count the new interface being added - * at this moment. - */ + /* Counting number of interfaces is needed for legacy PM */ if (vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count++; - if (mvm->vif_count > 1) { - IWL_DEBUG_MAC80211(mvm, - "Disable power on existing interfaces\n"); - ieee80211_iterate_active_interfaces_atomic( - mvm->hw, - IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_pm_disable_iterator, mvm); - } /* * The AP binding flow can be done only after the beacon @@ -591,11 +581,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, if (ret) goto out_release; - /* - * Update power state on the new interface. Admittedly, based on - * mac80211 logics this power update will disable power management - */ - iwl_mvm_power_update_mode(mvm, vif); + iwl_mvm_power_disable(mvm, vif); /* beacon filtering */ ret = iwl_mvm_disable_beacon_filter(mvm, vif); @@ -656,9 +642,12 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, out_release: if (vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count--; + + /* TODO: remove this when legacy PM will be discarded */ ieee80211_iterate_active_interfaces( mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_power_update_iterator, mvm); + iwl_mvm_mac_ctxt_release(mvm, vif); out_unlock: mutex_unlock(&mvm->mutex); @@ -744,21 +733,13 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, mvmvif->phy_ctxt = NULL; } - /* - * TODO: remove this temporary code. - * Currently MVM FW supports power management only on single MAC. - * Check if only one additional interface remains after removing - * current one. Update power mode on the remaining interface. - */ if (mvm->vif_count && vif->type != NL80211_IFTYPE_P2P_DEVICE) mvm->vif_count--; - IWL_DEBUG_MAC80211(mvm, "Currently %d interfaces active\n", - mvm->vif_count); - if (mvm->vif_count == 1) { - ieee80211_iterate_active_interfaces( - mvm->hw, IEEE80211_IFACE_ITER_NORMAL, - iwl_mvm_power_update_iterator, mvm); - } + + /* TODO: remove this when legacy PM will be discarded */ + ieee80211_iterate_active_interfaces( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_update_iterator, mvm); iwl_mvm_mac_ctxt_remove(mvm, vif); @@ -767,23 +748,91 @@ out_release: mutex_unlock(&mvm->mutex); } -static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - s8 tx_power) +static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) { - /* FW is in charge of regulatory enforcement */ - struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = { - .mac_context_id = iwl_mvm_vif_from_mac80211(vif)->id, - .pwr_restriction = cpu_to_le16(tx_power), + return 0; +} + +struct iwl_mvm_mc_iter_data { + struct iwl_mvm *mvm; + int port_id; +}; + +static void iwl_mvm_mc_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_mc_iter_data *data = _data; + struct iwl_mvm *mvm = data->mvm; + struct iwl_mcast_filter_cmd *cmd = mvm->mcast_filter_cmd; + int ret, len; + + /* if we don't have free ports, mcast frames will be dropped */ + if (WARN_ON_ONCE(data->port_id >= MAX_PORT_ID_NUM)) + return; + + if (vif->type != NL80211_IFTYPE_STATION || + !vif->bss_conf.assoc) + return; + + cmd->port_id = data->port_id++; + memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); + len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4); + + ret = iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC, len, cmd); + if (ret) + IWL_ERR(mvm, "mcast filter cmd error. ret=%d\n", ret); +} + +static void iwl_mvm_recalc_multicast(struct iwl_mvm *mvm) +{ + struct iwl_mvm_mc_iter_data iter_data = { + .mvm = mvm, }; - return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, CMD_SYNC, - sizeof(reduce_txpwr_cmd), - &reduce_txpwr_cmd); + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON_ONCE(!mvm->mcast_filter_cmd)) + return; + + ieee80211_iterate_active_interfaces( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_mc_iface_iterator, &iter_data); } -static int iwl_mvm_mac_config(struct ieee80211_hw *hw, u32 changed) +static u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) { - return 0; + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mcast_filter_cmd *cmd; + struct netdev_hw_addr *addr; + int addr_count = netdev_hw_addr_list_count(mc_list); + bool pass_all = false; + int len; + + if (addr_count > MAX_MCAST_FILTERING_ADDRESSES) { + pass_all = true; + addr_count = 0; + } + + len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4); + cmd = kzalloc(len, GFP_ATOMIC); + if (!cmd) + return 0; + + if (pass_all) { + cmd->pass_all = 1; + return (u64)(unsigned long)cmd; + } + + netdev_hw_addr_list_for_each(addr, mc_list) { + IWL_DEBUG_MAC80211(mvm, "mcast addr (%d): %pM\n", + cmd->count, addr->addr); + memcpy(&cmd->addr_list[cmd->count * ETH_ALEN], + addr->addr, ETH_ALEN); + cmd->count++; + } + + return (u64)(unsigned long)cmd; } static void iwl_mvm_configure_filter(struct ieee80211_hw *hw, @@ -791,21 +840,22 @@ static void iwl_mvm_configure_filter(struct ieee80211_hw *hw, unsigned int *total_flags, u64 multicast) { - *total_flags = 0; -} + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast; -static int iwl_mvm_configure_mcast_filter(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) -{ - struct iwl_mcast_filter_cmd mcast_filter_cmd = { - .pass_all = 1, - }; + mutex_lock(&mvm->mutex); + + /* replace previous configuration */ + kfree(mvm->mcast_filter_cmd); + mvm->mcast_filter_cmd = cmd; - memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN); + if (!cmd) + goto out; - return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC, - sizeof(mcast_filter_cmd), - &mcast_filter_cmd); + iwl_mvm_recalc_multicast(mvm); +out: + mutex_unlock(&mvm->mutex); + *total_flags = 0; } static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, @@ -828,7 +878,6 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, IWL_ERR(mvm, "failed to update quotas\n"); return; } - iwl_mvm_configure_mcast_filter(mvm, vif); if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { @@ -850,7 +899,17 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, iwl_mvm_protect_session(mvm, vif, dur, dur, 5 * dur); } + + iwl_mvm_sf_update(mvm, vif, false); + iwl_mvm_power_vif_assoc(mvm, vif); } else if (mvmvif->ap_sta_id != IWL_MVM_STATION_COUNT) { + /* + * If update fails - SF might be running in associated + * mode while disassociated - which is forbidden. + */ + WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false), + "Failed to update SF upon disassociation\n"); + /* remove AP station now that the MAC is unassoc */ ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); if (ret) @@ -862,6 +921,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, IWL_ERR(mvm, "failed to update quotas\n"); } + iwl_mvm_recalc_multicast(mvm); + /* reset rssi values */ mvmvif->bf_data.ave_beacon_signal = 0; @@ -882,7 +943,8 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, */ iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data); - } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_QOS)) { + } else if (changes & (BSS_CHANGED_PS | BSS_CHANGED_P2P_PS | + BSS_CHANGED_QOS)) { ret = iwl_mvm_power_update_mode(mvm, vif); if (ret) IWL_ERR(mvm, "failed to update power mode\n"); @@ -991,11 +1053,16 @@ iwl_mvm_bss_info_changed_ap_ibss(struct iwl_mvm *mvm, struct ieee80211_bss_conf *bss_conf, u32 changes) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); enum ieee80211_bss_change ht_change = BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT | BSS_CHANGED_BANDWIDTH; int ret; + /* Changes will be applied when the AP/IBSS is started */ + if (!mvmvif->ap_ibss_active) + return; + if (changes & ht_change) { ret = iwl_mvm_mac_ctxt_changed(mvm, vif); if (ret) @@ -1114,6 +1181,28 @@ static void iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, } } +static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + + /* + * This is called before mac80211 does RCU synchronisation, + * so here we already invalidate our internal RCU-protected + * station pointer. The rest of the code will thus no longer + * be able to find the station this way, and we don't rely + * on further RCU synchronisation after the sta_state() + * callback deleted the station. + */ + mutex_lock(&mvm->mutex); + if (sta == rcu_access_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id])) + rcu_assign_pointer(mvm->fw_id_to_mac_id[mvm_sta->sta_id], + ERR_PTR(-ENOENT)); + mutex_unlock(&mvm->mutex); +} + static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -1200,6 +1289,17 @@ static int iwl_mvm_mac_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return 0; } +static void iwl_mvm_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u32 changed) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + if (vif->type == NL80211_IFTYPE_STATION && + changed & IEEE80211_RC_NSS_CHANGED) + iwl_mvm_sf_update(mvm, vif, false); +} + static int iwl_mvm_mac_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 ac, const struct ieee80211_tx_queue_params *params) @@ -1322,7 +1422,12 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, */ return 0; default: - return -EOPNOTSUPP; + /* currently FW supports only one optional cipher scheme */ + if (hw->n_cipher_schemes && + hw->cipher_schemes->cipher == key->cipher) + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + else + return -EOPNOTSUPP; } mutex_lock(&mvm->mutex); @@ -1528,7 +1633,7 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw, goto out; } - ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, + ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, ctx->rx_chains_static, ctx->rx_chains_dynamic); if (ret) { @@ -1572,7 +1677,7 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw, return; mutex_lock(&mvm->mutex); - iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->def, + iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &ctx->min_def, ctx->rx_chains_static, ctx->rx_chains_dynamic); iwl_mvm_bt_coex_vif_change(mvm); @@ -1615,7 +1720,13 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, goto out_unlock; /* - * Setting the quota at this stage is only required for monitor + * Power state must be updated before quotas, + * otherwise fw will complain. + */ + mvm->bound_vif_cnt++; + iwl_mvm_power_update_binding(mvm, vif, true); + + /* Setting the quota at this stage is only required for monitor * interfaces. For the other types, the bss_info changed flow * will handle quota settings. */ @@ -1630,6 +1741,8 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw, out_remove_binding: iwl_mvm_binding_remove_vif(mvm, vif); + mvm->bound_vif_cnt--; + iwl_mvm_power_update_binding(mvm, vif, false); out_unlock: mutex_unlock(&mvm->mutex); if (ret) @@ -1663,6 +1776,9 @@ static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw, iwl_mvm_binding_remove_vif(mvm, vif); out_unlock: mvmvif->phy_ctxt = NULL; + mvm->bound_vif_cnt--; + iwl_mvm_power_update_binding(mvm, vif, false); + mutex_unlock(&mvm->mutex); } @@ -1757,14 +1873,17 @@ struct ieee80211_ops iwl_mvm_hw_ops = { .add_interface = iwl_mvm_mac_add_interface, .remove_interface = iwl_mvm_mac_remove_interface, .config = iwl_mvm_mac_config, + .prepare_multicast = iwl_mvm_prepare_multicast, .configure_filter = iwl_mvm_configure_filter, .bss_info_changed = iwl_mvm_bss_info_changed, .hw_scan = iwl_mvm_mac_hw_scan, .cancel_hw_scan = iwl_mvm_mac_cancel_hw_scan, + .sta_pre_rcu_remove = iwl_mvm_sta_pre_rcu_remove, .sta_state = iwl_mvm_mac_sta_state, .sta_notify = iwl_mvm_mac_sta_notify, .allow_buffered_frames = iwl_mvm_mac_allow_buffered_frames, .set_rts_threshold = iwl_mvm_mac_set_rts_threshold, + .sta_rc_update = iwl_mvm_sta_rc_update, .conf_tx = iwl_mvm_mac_conf_tx, .mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx, .sched_scan_start = iwl_mvm_mac_sched_scan_start, diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index 7dc57cfe5803..84edf3649ad2 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -163,6 +163,8 @@ struct iwl_mvm_power_ops { struct ieee80211_vif *vif); int (*power_update_device_mode)(struct iwl_mvm *mvm); int (*power_disable)(struct iwl_mvm *mvm, struct ieee80211_vif *vif); + void (*power_update_binding)(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, bool assign); #ifdef CONFIG_IWLWIFI_DEBUGFS int (*power_dbgfs_read)(struct iwl_mvm *mvm, struct ieee80211_vif *vif, char *buf, int bufsz); @@ -181,6 +183,7 @@ enum iwl_dbgfs_pm_mask { MVM_DEBUGFS_PM_LPRX_ENA = BIT(6), MVM_DEBUGFS_PM_LPRX_RSSI_THRESHOLD = BIT(7), MVM_DEBUGFS_PM_SNOOZE_ENABLE = BIT(8), + MVM_DEBUGFS_PM_UAPSD_MISBEHAVING = BIT(9), }; struct iwl_dbgfs_pm { @@ -193,6 +196,7 @@ struct iwl_dbgfs_pm { bool lprx_ena; u32 lprx_rssi_threshold; bool snooze_ena; + bool uapsd_misbehaving; int mask; }; @@ -269,8 +273,8 @@ struct iwl_mvm_vif_bf_data { * @bcast_sta: station used for broadcast packets. Used by the following * vifs: P2P_DEVICE, GO and AP. * @beacon_skb: the skb used to hold the AP/GO beacon template - * @smps_requests: the requests of of differents parts of the driver, regard - the desired smps mode. + * @smps_requests: the SMPS requests of differents parts of the driver, + * combined on update to yield the overall request to mac80211. */ struct iwl_mvm_vif { u16 id; @@ -331,6 +335,11 @@ struct iwl_mvm_vif { #endif enum ieee80211_smps_mode smps_requests[NUM_IWL_MVM_SMPS_REQ]; + + /* FW identified misbehaving AP */ + u8 uapsd_misbehaving_bssid[ETH_ALEN]; + + bool pm_prevented; }; static inline struct iwl_mvm_vif * @@ -479,6 +488,7 @@ struct iwl_mvm { /* Scan status, cmd (pre-allocated) and auxiliary station */ enum iwl_scan_status scan_status; struct iwl_scan_cmd *scan_cmd; + struct iwl_mcast_filter_cmd *mcast_filter_cmd; /* rx chain antennas set through debugfs for the scan command */ u8 scan_rx_ant; @@ -489,6 +499,9 @@ struct iwl_mvm { u8 scan_last_antenna_idx; /* to toggle TX between antennas */ u8 mgmt_last_antenna_idx; + /* last smart fifo state that was successfully sent to firmware */ + enum iwl_sf_state sf_state; + #ifdef CONFIG_IWLWIFI_DEBUGFS struct dentry *debugfs_dir; u32 dbgfs_sram_offset, dbgfs_sram_len; @@ -512,12 +525,6 @@ struct iwl_mvm { */ unsigned long fw_key_table[BITS_TO_LONGS(STA_KEY_MAX_NUM)]; - /* - * This counter of created interfaces is referenced only in conjunction - * with FW limitation related to power management. Currently PM is - * supported only on a single interface. - * IMPORTANT: this variable counts all interfaces except P2P device. - */ u8 vif_count; /* -1 for always, 0 for never, >0 for that many times */ @@ -560,6 +567,11 @@ struct iwl_mvm { u8 aux_queue; u8 first_agg_queue; u8 last_agg_queue; + + u8 bound_vif_cnt; + + /* Indicate if device power save is allowed */ + bool ps_prevented; }; /* Extract MVM priv from op_mode and _hw */ @@ -778,6 +790,19 @@ static inline int iwl_mvm_power_update_device_mode(struct iwl_mvm *mvm) return 0; } +static inline void iwl_mvm_power_update_binding(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool assign) +{ + if (mvm->pm_ops->power_update_binding) + mvm->pm_ops->power_update_binding(mvm, vif, assign); +} + +void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif); +int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); + #ifdef CONFIG_IWLWIFI_DEBUGFS static inline int iwl_mvm_power_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, @@ -869,4 +894,8 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm); void iwl_mvm_tt_exit(struct iwl_mvm *mvm); void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state); +/* smart fifo */ +int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool added_vif); + #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index d86083c6f445..a362430477a0 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -236,6 +236,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { false), RX_HANDLER(REPLY_ERROR, iwl_mvm_rx_fw_error, false), + RX_HANDLER(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, + iwl_mvm_power_uapsd_misbehaving_ap_notif, false), }; #undef RX_HANDLER #define CMD(x) [x] = #x @@ -311,6 +313,7 @@ static const char *iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(REPLY_THERMAL_MNG_BACKOFF), CMD(MAC_PM_POWER_TABLE), CMD(BT_COEX_CI), + CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), }; #undef CMD @@ -341,7 +344,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, op_mode = hw->priv; op_mode->ops = &iwl_mvm_ops; - op_mode->trans = trans; mvm = IWL_OP_MODE_GET_MVM(op_mode); mvm->dev = trans->dev; @@ -359,6 +361,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->aux_queue = 11; mvm->first_agg_queue = 12; } + mvm->sf_state = SF_UNINIT; mutex_init(&mvm->mutex); spin_lock_init(&mvm->async_handlers_lock); @@ -424,7 +427,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, * there is no need to unnecessarily power up the NIC at driver load */ if (iwlwifi_mod_params.nvm_file) { - iwl_nvm_init(mvm); + err = iwl_nvm_init(mvm); + if (err) + goto out_free; } else { err = iwl_trans_start_hw(mvm->trans); if (err) @@ -432,16 +437,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mutex_lock(&mvm->mutex); err = iwl_run_init_mvm_ucode(mvm, true); + iwl_trans_stop_device(trans); mutex_unlock(&mvm->mutex); /* returns 0 if successful, 1 if success but in rfkill */ if (err < 0 && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); goto out_free; } - - /* Stop the hw after the ALIVE and NVM has been read */ - if (!iwlmvm_mod_params.init_dbg) - iwl_trans_stop_hw(mvm->trans, false); } scan_size = sizeof(struct iwl_scan_cmd) + @@ -474,7 +476,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwl_phy_db_free(mvm->phy_db); kfree(mvm->scan_cmd); if (!iwlwifi_mod_params.nvm_file) - iwl_trans_stop_hw(trans, true); + iwl_trans_op_mode_leave(trans); ieee80211_free_hw(mvm->hw); return NULL; } @@ -491,12 +493,14 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) ieee80211_unregister_hw(mvm->hw); kfree(mvm->scan_cmd); + kfree(mvm->mcast_filter_cmd); + mvm->mcast_filter_cmd = NULL; #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) kfree(mvm->d3_resume_sram); #endif - iwl_trans_stop_hw(mvm->trans, true); + iwl_trans_op_mode_leave(mvm->trans); iwl_phy_db_free(mvm->phy_db); mvm->phy_db = NULL; diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c index 550824aa84ea..483ecc67501f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/iwlwifi/mvm/power.c @@ -186,6 +186,92 @@ static void iwl_mvm_power_log(struct iwl_mvm *mvm, } } +static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd *cmd) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + enum ieee80211_ac_numbers ac; + bool tid_found = false; + + for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) { + if (!mvmvif->queue_params[ac].uapsd) + continue; + + if (mvm->cur_ucode != IWL_UCODE_WOWLAN) + cmd->flags |= + cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); + + cmd->uapsd_ac_flags |= BIT(ac); + + /* QNDP TID - the highest TID with no admission control */ + if (!tid_found && !mvmvif->queue_params[ac].acm) { + tid_found = true; + switch (ac) { + case IEEE80211_AC_VO: + cmd->qndp_tid = 6; + break; + case IEEE80211_AC_VI: + cmd->qndp_tid = 5; + break; + case IEEE80211_AC_BE: + cmd->qndp_tid = 0; + break; + case IEEE80211_AC_BK: + cmd->qndp_tid = 1; + break; + } + } + } + + if (!(cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) + return; + + cmd->flags |= cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK); + + if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | + BIT(IEEE80211_AC_VI) | + BIT(IEEE80211_AC_BE) | + BIT(IEEE80211_AC_BK))) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); + cmd->snooze_interval = cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL); + cmd->snooze_window = (mvm->cur_ucode == IWL_UCODE_WOWLAN) ? + cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : + cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); + } + + cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP; + + if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags & + cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); + } else { + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); + } + + if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { + cmd->heavy_tx_thld_packets = + IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS; + cmd->heavy_rx_thld_packets = + IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS; + } else { + cmd->heavy_tx_thld_packets = + IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; + cmd->heavy_rx_thld_packets = + IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; + } + cmd->heavy_tx_thld_percentage = + IWL_MVM_PS_HEAVY_TX_THLD_PERCENT; + cmd->heavy_rx_thld_percentage = + IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; +} + static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct iwl_mac_power_cmd *cmd) @@ -198,8 +284,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, bool radar_detect = false; struct iwl_mvm_vif *mvmvif __maybe_unused = iwl_mvm_vif_from_mac80211(vif); - enum ieee80211_ac_numbers ac; - bool tid_found = false; + bool allow_uapsd = true; cmd->id_and_color = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color)); @@ -217,7 +302,8 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, keep_alive = DIV_ROUND_UP(keep_alive, MSEC_PER_SEC); cmd->keep_alive_seconds = cpu_to_le16(keep_alive); - if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) + if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM || + mvm->ps_prevented) return; cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); @@ -227,7 +313,7 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, mvmvif->dbgfs_pm.disable_power_off) cmd->flags &= cpu_to_le16(~POWER_FLAGS_POWER_SAVE_ENA_MSK); #endif - if (!vif->bss_conf.ps) + if (!vif->bss_conf.ps || mvmvif->pm_prevented) return; cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); @@ -269,81 +355,24 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); } - for (ac = IEEE80211_AC_VO; ac <= IEEE80211_AC_BK; ac++) { - if (!mvmvif->queue_params[ac].uapsd) - continue; - - if (mvm->cur_ucode != IWL_UCODE_WOWLAN) - cmd->flags |= - cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); - - cmd->uapsd_ac_flags |= BIT(ac); + if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, + ETH_ALEN)) + allow_uapsd = false; - /* QNDP TID - the highest TID with no admission control */ - if (!tid_found && !mvmvif->queue_params[ac].acm) { - tid_found = true; - switch (ac) { - case IEEE80211_AC_VO: - cmd->qndp_tid = 6; - break; - case IEEE80211_AC_VI: - cmd->qndp_tid = 5; - break; - case IEEE80211_AC_BE: - cmd->qndp_tid = 0; - break; - case IEEE80211_AC_BK: - cmd->qndp_tid = 1; - break; - } - } - } - - if (cmd->flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { - if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | - BIT(IEEE80211_AC_VI) | - BIT(IEEE80211_AC_BE) | - BIT(IEEE80211_AC_BK))) { - cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); - cmd->snooze_interval = - cpu_to_le16(IWL_MVM_PS_SNOOZE_INTERVAL); - cmd->snooze_window = - (mvm->cur_ucode == IWL_UCODE_WOWLAN) ? - cpu_to_le16(IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW) : - cpu_to_le16(IWL_MVM_PS_SNOOZE_WINDOW); - } - - cmd->uapsd_max_sp = IWL_UAPSD_MAX_SP; - - if (mvm->cur_ucode == IWL_UCODE_WOWLAN || cmd->flags & - cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { - cmd->rx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_WOWLAN_PS_RX_DATA_TIMEOUT); - cmd->tx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_WOWLAN_PS_TX_DATA_TIMEOUT); - } else { - cmd->rx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_UAPSD_RX_DATA_TIMEOUT); - cmd->tx_data_timeout_uapsd = - cpu_to_le32(IWL_MVM_UAPSD_TX_DATA_TIMEOUT); - } + if (vif->p2p && + !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD)) + allow_uapsd = false; + /* + * Avoid using uAPSD if P2P client is associated to GO that uses + * opportunistic power save. This is due to current FW limitation. + */ + if (vif->p2p && + vif->bss_conf.p2p_noa_attr.oppps_ctwindow & + IEEE80211_P2P_OPPPS_ENABLE_BIT) + allow_uapsd = false; - if (cmd->flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { - cmd->heavy_tx_thld_packets = - IWL_MVM_PS_SNOOZE_HEAVY_TX_THLD_PACKETS; - cmd->heavy_rx_thld_packets = - IWL_MVM_PS_SNOOZE_HEAVY_RX_THLD_PACKETS; - } else { - cmd->heavy_tx_thld_packets = - IWL_MVM_PS_HEAVY_TX_THLD_PACKETS; - cmd->heavy_rx_thld_packets = - IWL_MVM_PS_HEAVY_RX_THLD_PACKETS; - } - cmd->heavy_tx_thld_percentage = - IWL_MVM_PS_HEAVY_TX_THLD_PERCENT; - cmd->heavy_rx_thld_percentage = - IWL_MVM_PS_HEAVY_RX_THLD_PERCENT; - } + if (allow_uapsd) + iwl_mvm_power_configure_uapsd(mvm, vif, cmd); #ifdef CONFIG_IWLWIFI_DEBUGFS if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_KEEP_ALIVE) @@ -381,6 +410,13 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, cmd->flags &= cpu_to_le16(~POWER_FLAGS_SNOOZE_ENA_MSK); } + if (mvmvif->dbgfs_pm.mask & MVM_DEBUGFS_PM_UAPSD_MISBEHAVING) { + u16 flag = POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK; + if (mvmvif->dbgfs_pm.uapsd_misbehaving) + cmd->flags |= cpu_to_le16(flag); + else + cmd->flags &= cpu_to_le16(flag); + } #endif /* CONFIG_IWLWIFI_DEBUGFS */ } @@ -391,18 +427,11 @@ static int iwl_mvm_power_mac_update_mode(struct iwl_mvm *mvm, bool ba_enable; struct iwl_mac_power_cmd cmd = {}; - if (vif->type != NL80211_IFTYPE_STATION || vif->p2p) + if (vif->type != NL80211_IFTYPE_STATION) return 0; - /* - * TODO: The following vif_count verification is temporary condition. - * Avoid power mode update if more than one interface is currently - * active. Remove this condition when FW will support power management - * on multiple MACs. - */ - IWL_DEBUG_POWER(mvm, "Currently %d interfaces active\n", - mvm->vif_count); - if (mvm->vif_count > 1) + if (vif->p2p && + !(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PS)) return 0; iwl_mvm_power_build_cmd(mvm, vif, &cmd); @@ -446,7 +475,7 @@ static int iwl_mvm_power_mac_disable(struct iwl_mvm *mvm, sizeof(cmd), &cmd); } -static int iwl_mvm_power_update_device(struct iwl_mvm *mvm) +static int _iwl_mvm_power_update_device(struct iwl_mvm *mvm, bool force_disable) { struct iwl_device_power_cmd cmd = { .flags = cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK), @@ -455,7 +484,8 @@ static int iwl_mvm_power_update_device(struct iwl_mvm *mvm) if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) return 0; - if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) + if (iwlmvm_mod_params.power_scheme == IWL_POWER_SCHEME_CAM || + force_disable) cmd.flags |= cpu_to_le16(DEVICE_POWER_FLAGS_CAM_MSK); #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -472,6 +502,78 @@ static int iwl_mvm_power_update_device(struct iwl_mvm *mvm) &cmd); } +static int iwl_mvm_power_update_device(struct iwl_mvm *mvm) +{ + return _iwl_mvm_power_update_device(mvm, false); +} + +void iwl_mvm_power_vif_assoc(struct iwl_mvm *mvm, struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (memcmp(vif->bss_conf.bssid, mvmvif->uapsd_misbehaving_bssid, + ETH_ALEN)) + memset(mvmvif->uapsd_misbehaving_bssid, 0, ETH_ALEN); +} + +static void iwl_mvm_power_uapsd_misbehav_ap_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + u8 *ap_sta_id = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + /* The ap_sta_id is not expected to change during current association + * so no explicit protection is needed + */ + if (mvmvif->ap_sta_id == *ap_sta_id) + memcpy(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid, + ETH_ALEN); +} + +int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; + u8 ap_sta_id = le32_to_cpu(notif->sta_id); + + ieee80211_iterate_active_interfaces_atomic( + mvm->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_uapsd_misbehav_ap_iterator, &ap_sta_id); + + return 0; +} + +static void iwl_mvm_power_binding_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = _data; + int ret; + + mvmvif->pm_prevented = (mvm->bound_vif_cnt <= 1) ? false : true; + + ret = iwl_mvm_power_mac_update_mode(mvm, vif); + WARN_ONCE(ret, "Failed to update power parameters on a specific vif\n"); +} + +static void _iwl_mvm_power_update_binding(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + bool assign) +{ + if (vif->type == NL80211_IFTYPE_MONITOR) { + int ret = _iwl_mvm_power_update_device(mvm, assign); + mvm->ps_prevented = assign; + WARN_ONCE(ret, "Failed to update power device state\n"); + } + + ieee80211_iterate_active_interfaces(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_power_binding_iterator, + mvm); +} + #ifdef CONFIG_IWLWIFI_DEBUGFS static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, struct ieee80211_vif *vif, char *buf, @@ -494,70 +596,58 @@ static int iwl_mvm_power_mac_dbgfs_read(struct iwl_mvm *mvm, pos += scnprintf(buf+pos, bufsz-pos, "keep_alive = %d\n", le16_to_cpu(cmd.keep_alive_seconds)); - if (cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)) { - pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? - 1 : 0); - pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", - cmd.skip_dtim_periods); - if (!(cmd.flags & - cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { - pos += scnprintf(buf+pos, bufsz-pos, - "rx_data_timeout = %d\n", - le32_to_cpu(cmd.rx_data_timeout)); - pos += scnprintf(buf+pos, bufsz-pos, - "tx_data_timeout = %d\n", - le32_to_cpu(cmd.tx_data_timeout)); - } - if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) - pos += scnprintf(buf+pos, bufsz-pos, - "lprx_rssi_threshold = %d\n", - cmd.lprx_rssi_threshold); - if (cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK)) { - pos += - scnprintf(buf+pos, bufsz-pos, - "rx_data_timeout_uapsd = %d\n", - le32_to_cpu(cmd.rx_data_timeout_uapsd)); - pos += - scnprintf(buf+pos, bufsz-pos, - "tx_data_timeout_uapsd = %d\n", - le32_to_cpu(cmd.tx_data_timeout_uapsd)); - pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", - cmd.qndp_tid); - pos += scnprintf(buf+pos, bufsz-pos, - "uapsd_ac_flags = 0x%x\n", - cmd.uapsd_ac_flags); - pos += scnprintf(buf+pos, bufsz-pos, - "uapsd_max_sp = %d\n", - cmd.uapsd_max_sp); - pos += scnprintf(buf+pos, bufsz-pos, - "heavy_tx_thld_packets = %d\n", - cmd.heavy_tx_thld_packets); - pos += scnprintf(buf+pos, bufsz-pos, - "heavy_rx_thld_packets = %d\n", - cmd.heavy_rx_thld_packets); - pos += scnprintf(buf+pos, bufsz-pos, - "heavy_tx_thld_percentage = %d\n", - cmd.heavy_tx_thld_percentage); - pos += scnprintf(buf+pos, bufsz-pos, - "heavy_rx_thld_percentage = %d\n", - cmd.heavy_rx_thld_percentage); - pos += - scnprintf(buf+pos, bufsz-pos, "snooze_enable = %d\n", - (cmd.flags & - cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) ? - 1 : 0); - } - if (cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK)) { - pos += scnprintf(buf+pos, bufsz-pos, - "snooze_interval = %d\n", - cmd.snooze_interval); - pos += scnprintf(buf+pos, bufsz-pos, - "snooze_window = %d\n", - cmd.snooze_window); - } + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) + return pos; + + pos += scnprintf(buf+pos, bufsz-pos, "skip_over_dtim = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK)) ? 1 : 0); + pos += scnprintf(buf+pos, bufsz-pos, "skip_dtim_periods = %d\n", + cmd.skip_dtim_periods); + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) { + pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout = %d\n", + le32_to_cpu(cmd.rx_data_timeout)); + pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout = %d\n", + le32_to_cpu(cmd.tx_data_timeout)); } + if (cmd.flags & cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK)) + pos += scnprintf(buf+pos, bufsz-pos, + "lprx_rssi_threshold = %d\n", + cmd.lprx_rssi_threshold); + + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK))) + return pos; + + pos += scnprintf(buf+pos, bufsz-pos, "rx_data_timeout_uapsd = %d\n", + le32_to_cpu(cmd.rx_data_timeout_uapsd)); + pos += scnprintf(buf+pos, bufsz-pos, "tx_data_timeout_uapsd = %d\n", + le32_to_cpu(cmd.tx_data_timeout_uapsd)); + pos += scnprintf(buf+pos, bufsz-pos, "qndp_tid = %d\n", cmd.qndp_tid); + pos += scnprintf(buf+pos, bufsz-pos, "uapsd_ac_flags = 0x%x\n", + cmd.uapsd_ac_flags); + pos += scnprintf(buf+pos, bufsz-pos, "uapsd_max_sp = %d\n", + cmd.uapsd_max_sp); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_packets = %d\n", + cmd.heavy_tx_thld_packets); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_packets = %d\n", + cmd.heavy_rx_thld_packets); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_tx_thld_percentage = %d\n", + cmd.heavy_tx_thld_percentage); + pos += scnprintf(buf+pos, bufsz-pos, "heavy_rx_thld_percentage = %d\n", + cmd.heavy_rx_thld_percentage); + pos += scnprintf(buf+pos, bufsz-pos, "uapsd_misbehaving_enable = %d\n", + (cmd.flags & + cpu_to_le16(POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK)) ? + 1 : 0); + + if (!(cmd.flags & cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK))) + return pos; + + pos += scnprintf(buf+pos, bufsz-pos, "snooze_interval = %d\n", + cmd.snooze_interval); + pos += scnprintf(buf+pos, bufsz-pos, "snooze_window = %d\n", + cmd.snooze_window); + return pos; } @@ -654,6 +744,7 @@ const struct iwl_mvm_power_ops pm_mac_ops = { .power_update_mode = iwl_mvm_power_mac_update_mode, .power_update_device_mode = iwl_mvm_power_update_device, .power_disable = iwl_mvm_power_mac_disable, + .power_update_binding = _iwl_mvm_power_update_binding, #ifdef CONFIG_IWLWIFI_DEBUGFS .power_dbgfs_read = iwl_mvm_power_mac_dbgfs_read, #endif diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index bf6e29f5b4d0..d6d28d7b442b 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -42,9 +42,16 @@ #define RS_NAME "iwl-mvm-rs" -#define NUM_TRY_BEFORE_ANT_TOGGLE 1 -#define IWL_NUMBER_TRY 1 -#define IWL_HT_NUMBER_TRY 3 +#define NUM_TRY_BEFORE_ANT_TOGGLE 1 +#define RS_LEGACY_RETRIES_PER_RATE 1 +#define RS_HT_VHT_RETRIES_PER_RATE 2 +#define RS_HT_VHT_RETRIES_PER_RATE_TW 1 +#define RS_INITIAL_MIMO_NUM_RATES 3 +#define RS_INITIAL_SISO_NUM_RATES 3 +#define RS_INITIAL_LEGACY_NUM_RATES LINK_QUAL_MAX_RETRY_NUM +#define RS_SECONDARY_LEGACY_NUM_RATES LINK_QUAL_MAX_RETRY_NUM +#define RS_SECONDARY_SISO_NUM_RATES 3 +#define RS_SECONDARY_SISO_RETRIES 1 #define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ #define IWL_RATE_MIN_FAILURE_TH 3 /* min failures to calc tpt */ @@ -123,6 +130,12 @@ static const struct iwl_rs_rate_info iwl_rates[IWL_RATE_COUNT] = { IWL_DECLARE_MCS_RATE(9), /* MCS 9 */ }; +enum rs_action { + RS_ACTION_STAY = 0, + RS_ACTION_DOWNSCALE = -1, + RS_ACTION_UPSCALE = 1, +}; + enum rs_column_mode { RS_INVALID = 0, RS_LEGACY, @@ -351,20 +364,12 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta); -static void rs_fill_link_cmd(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, u32 rate_n_flags); +static void rs_fill_lq_cmd(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate); static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search); -#ifdef CONFIG_MAC80211_DEBUGFS -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags); -#else -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags) -{} -#endif - /** * The following tables contain the expected throughput metrics for all rates * @@ -504,30 +509,6 @@ static inline u8 rs_is_valid_ant(u8 valid_antenna, u8 ant_type) return (ant_type & valid_antenna) == ant_type; } -#ifdef CONFIG_MAC80211_DEBUGFS -/** - * Program the device to use fixed rate for frame transmit - * This is for debugging/testing only - * once the device start use fixed rate, we need to reload the module - * to being back the normal operation. - */ -static void rs_program_fix_rate(struct iwl_mvm *mvm, - struct iwl_lq_sta *lq_sta) -{ - lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ - lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ - - IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n", - lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); - - if (lq_sta->dbg_fixed_rate) { - rs_fill_link_cmd(NULL, NULL, lq_sta, lq_sta->dbg_fixed_rate); - iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false); - } -} -#endif - static int rs_tl_turn_on_agg_for_tid(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_data, u8 tid, struct ieee80211_sta *sta) @@ -658,7 +639,7 @@ static int rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, /* Convert rs_rate object into ucode rate bitmask */ static u32 ucode_rate_from_rs_rate(struct iwl_mvm *mvm, - struct rs_rate *rate) + struct rs_rate *rate) { u32 ucode_rate = 0; int index = rate->index; @@ -785,8 +766,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate, /* switch to another antenna/antennas and return 1 */ /* if no other valid antenna found, return 0 */ -static int rs_toggle_antenna(u32 valid_ant, u32 *ucode_rate, - struct rs_rate *rate) +static int rs_toggle_antenna(u32 valid_ant, struct rs_rate *rate) { u8 new_ant_type; @@ -807,9 +787,6 @@ static int rs_toggle_antenna(u32 valid_ant, u32 *ucode_rate, rate->ant = new_ant_type; - /* TODO: get rid of ucode_rate here. This should handle only rs_rate */ - *ucode_rate &= ~RATE_MCS_ANT_ABC_MSK; - *ucode_rate |= new_ant_type << RATE_MCS_ANT_POS; return 1; } @@ -883,65 +860,73 @@ static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, return (high << 8) | low; } -static u32 rs_get_lower_rate(struct iwl_lq_sta *lq_sta, - struct rs_rate *rate, - u8 scale_index, u8 ht_possible) +static inline bool rs_rate_supported(struct iwl_lq_sta *lq_sta, + struct rs_rate *rate) { - s32 low; - u16 rate_mask; + return BIT(rate->index) & rs_get_supported_rates(lq_sta, rate); +} + +/* Get the next supported lower rate in the current column. + * Return true if bottom rate in the current column was reached + */ +static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta, + struct rs_rate *rate) +{ + u8 low; u16 high_low; - u8 switch_to_legacy = 0; + u16 rate_mask; struct iwl_mvm *mvm = lq_sta->drv; - /* check if we need to switch from HT to legacy rates. - * assumption is that mandatory rates (1Mbps or 6Mbps) - * are always supported (spec demand) */ - if (!is_legacy(rate) && (!ht_possible || !scale_index)) { - switch_to_legacy = 1; - WARN_ON_ONCE(scale_index < IWL_RATE_MCS_0_INDEX && - scale_index > IWL_RATE_MCS_9_INDEX); - scale_index = rs_ht_to_legacy[scale_index]; + rate_mask = rs_get_supported_rates(lq_sta, rate); + high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask, + rate->type); + low = high_low & 0xff; + + /* Bottom rate of column reached */ + if (low == IWL_RATE_INVALID) + return true; + + rate->index = low; + return false; +} + +/* Get the next rate to use following a column downgrade */ +static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta, + struct rs_rate *rate) +{ + struct iwl_mvm *mvm = lq_sta->drv; + + if (is_legacy(rate)) { + /* No column to downgrade from Legacy */ + return; + } else if (is_siso(rate)) { + /* Downgrade to Legacy if we were in SISO */ if (lq_sta->band == IEEE80211_BAND_5GHZ) rate->type = LQ_LEGACY_A; else rate->type = LQ_LEGACY_G; - if (num_of_ant(rate->ant) > 1) - rate->ant = - first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); - rate->bw = RATE_MCS_CHAN_WIDTH_20; - rate->sgi = false; - } - rate_mask = rs_get_supported_rates(lq_sta, rate); + WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX && + rate->index > IWL_RATE_MCS_9_INDEX); - /* Mask with station rate restriction */ - if (is_legacy(rate)) { - /* supp_rates has no CCK bits in A mode */ - if (lq_sta->band == IEEE80211_BAND_5GHZ) - rate_mask = (u16)(rate_mask & - (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); - else - rate_mask = (u16)(rate_mask & lq_sta->supp_rates); + rate->index = rs_ht_to_legacy[rate->index]; + } else { + /* Downgrade to SISO with same MCS if in MIMO */ + rate->type = is_vht_mimo2(rate) ? + LQ_VHT_SISO : LQ_HT_SISO; } - /* If we switched from HT to legacy, check current rate */ - if (switch_to_legacy && (rate_mask & (1 << scale_index))) { - low = scale_index; - goto out; - } - high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask, - rate->type); - low = high_low & 0xff; + if (num_of_ant(rate->ant) > 1) + rate->ant = first_antenna(iwl_fw_valid_tx_ant(mvm->fw)); - if (low == IWL_RATE_INVALID) - low = scale_index; + /* Relevant in both switching to SISO or Legacy */ + rate->sgi = false; -out: - rate->index = low; - return ucode_rate_from_rs_rate(lq_sta->drv, rate); + if (!rs_rate_supported(lq_sta, rate)) + rs_get_lower_rate_in_column(lq_sta, rate); } /* Simple function to compare two rate scale table types */ @@ -1137,14 +1122,9 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband, tmp_tbl = curr_tbl; else if (rs_rate_match(&rate, &other_tbl->rate)) tmp_tbl = other_tbl; - else { - IWL_DEBUG_RATE(mvm, - "Tx packet rate doesn't match ACTIVE or SEARCH tables\n"); - rs_dump_rate(mvm, &rate, "Tx PACKET:"); - rs_dump_rate(mvm, &curr_tbl->rate, "CURRENT:"); - rs_dump_rate(mvm, &other_tbl->rate, "OTHER:"); + else continue; - } + rs_collect_tx_data(tmp_tbl, rate.index, 1, i < retries ? 0 : legacy_success); } @@ -1471,10 +1451,7 @@ static void rs_update_rate_tbl(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_sta, struct rs_rate *rate) { - u32 ucode_rate; - - ucode_rate = ucode_rate_from_rs_rate(mvm, rate); - rs_fill_link_cmd(mvm, sta, lq_sta, ucode_rate); + rs_fill_lq_cmd(mvm, sta, lq_sta, rate); iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); } @@ -1634,10 +1611,6 @@ static int rs_switch_to_column(struct iwl_mvm *mvm, rate->index = rate_idx; } - /* TODO: remove current_rate and keep using rs_rate all the way until - * we need to fill in the rs_table in the LQ command - */ - search_tbl->current_rate = ucode_rate_from_rs_rate(mvm, rate); IWL_DEBUG_RATE(mvm, "Switched to column %d: Index %d\n", col_id, rate->index); @@ -1649,6 +1622,97 @@ err: return -1; } +static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, + struct iwl_scale_tbl_info *tbl, + s32 sr, int low, int high, + int current_tpt, + int low_tpt, int high_tpt) +{ + enum rs_action action = RS_ACTION_STAY; + + /* Too many failures, decrease rate */ + if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) { + IWL_DEBUG_RATE(mvm, + "decrease rate because of low SR\n"); + action = RS_ACTION_DOWNSCALE; + /* No throughput measured yet for adjacent rates; try increase. */ + } else if ((low_tpt == IWL_INVALID_VALUE) && + (high_tpt == IWL_INVALID_VALUE)) { + if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) { + IWL_DEBUG_RATE(mvm, + "Good SR and no high rate measurement. " + "Increase rate\n"); + action = RS_ACTION_UPSCALE; + } else if (low != IWL_RATE_INVALID) { + IWL_DEBUG_RATE(mvm, + "Remain in current rate\n"); + action = RS_ACTION_STAY; + } + } + + /* Both adjacent throughputs are measured, but neither one has better + * throughput; we're using the best rate, don't change it! + */ + else if ((low_tpt != IWL_INVALID_VALUE) && + (high_tpt != IWL_INVALID_VALUE) && + (low_tpt < current_tpt) && + (high_tpt < current_tpt)) { + IWL_DEBUG_RATE(mvm, + "Both high and low are worse. " + "Maintain rate\n"); + action = RS_ACTION_STAY; + } + + /* At least one adjacent rate's throughput is measured, + * and may have better performance. + */ + else { + /* Higher adjacent rate's throughput is measured */ + if (high_tpt != IWL_INVALID_VALUE) { + /* Higher rate has better throughput */ + if (high_tpt > current_tpt && + sr >= IWL_RATE_INCREASE_TH) { + IWL_DEBUG_RATE(mvm, + "Higher rate is better and good " + "SR. Increate rate\n"); + action = RS_ACTION_UPSCALE; + } else { + IWL_DEBUG_RATE(mvm, + "Higher rate isn't better OR " + "no good SR. Maintain rate\n"); + action = RS_ACTION_STAY; + } + + /* Lower adjacent rate's throughput is measured */ + } else if (low_tpt != IWL_INVALID_VALUE) { + /* Lower rate has better throughput */ + if (low_tpt > current_tpt) { + IWL_DEBUG_RATE(mvm, + "Lower rate is better. " + "Decrease rate\n"); + action = RS_ACTION_DOWNSCALE; + } else if (sr >= IWL_RATE_INCREASE_TH) { + IWL_DEBUG_RATE(mvm, + "Lower rate isn't better and " + "good SR. Increase rate\n"); + action = RS_ACTION_UPSCALE; + } + } + } + + /* Sanity check; asked for decrease, but success rate or throughput + * has been good at old rate. Don't change it. + */ + if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID) && + ((sr > IWL_RATE_HIGH_TH) || + (current_tpt > (100 * tbl->expected_tpt[low])))) { + IWL_DEBUG_RATE(mvm, + "Sanity check failed. Maintain rate\n"); + action = RS_ACTION_STAY; + } + + return action; +} /* * Do rate scaling and search for new modulation mode. @@ -1669,11 +1733,10 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, int low_tpt = IWL_INVALID_VALUE; int high_tpt = IWL_INVALID_VALUE; u32 fail_count; - s8 scale_action = 0; + enum rs_action scale_action = RS_ACTION_STAY; u16 rate_mask; u8 update_lq = 0; struct iwl_scale_tbl_info *tbl, *tbl1; - u16 rate_scale_index_msk = 0; u8 active_tbl = 0; u8 done_search = 0; u16 high_low; @@ -1690,8 +1753,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, info->flags & IEEE80211_TX_CTL_NO_ACK) return; - lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; - tid = rs_get_tid(lq_sta, hdr); if ((tid != IWL_MAX_TID_COUNT) && (lq_sta->tx_agg_tid_en & (1 << tid))) { @@ -1730,33 +1791,13 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, /* rates available for this association, and for modulation mode */ rate_mask = rs_get_supported_rates(lq_sta, rate); - /* mask with station rate restriction */ - if (is_legacy(rate)) { - if (lq_sta->band == IEEE80211_BAND_5GHZ) - /* supp_rates has no CCK bits in A mode */ - rate_scale_index_msk = (u16) (rate_mask & - (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); - else - rate_scale_index_msk = (u16) (rate_mask & - lq_sta->supp_rates); - - } else { - rate_scale_index_msk = rate_mask; - } - - if (!rate_scale_index_msk) - rate_scale_index_msk = rate_mask; - - if (!((BIT(index) & rate_scale_index_msk))) { + if (!(BIT(index) & rate_mask)) { IWL_ERR(mvm, "Current Rate is not valid\n"); if (lq_sta->search_better_tbl) { /* revert to active table if search table is not valid*/ rate->type = LQ_NONE; lq_sta->search_better_tbl = 0; tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); - /* get "active" rate info */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); - tbl->rate.index = index; rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate); } return; @@ -1847,7 +1888,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, tbl = &(lq_sta->lq_info[active_tbl]); /* Revert to "active" rate and throughput info */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + index = tbl->rate.index; current_tpt = lq_sta->last_tpt; /* Need to set up a new rate table in uCode */ @@ -1863,8 +1904,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, /* (Else) not in search of better modulation mode, try for better * starting rate, while staying in this mode. */ - high_low = rs_get_adjacent_rate(mvm, index, rate_scale_index_msk, - rate->type); + high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type); low = high_low & 0xff; high = (high_low >> 8) & 0xff; @@ -1887,85 +1927,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, rs_pretty_lq_type(rate->type), index, current_tpt, sr, low, high, low_tpt, high_tpt); - scale_action = 0; - - /* Too many failures, decrease rate */ - if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) { - IWL_DEBUG_RATE(mvm, - "decrease rate because of low SR\n"); - scale_action = -1; - /* No throughput measured yet for adjacent rates; try increase. */ - } else if ((low_tpt == IWL_INVALID_VALUE) && - (high_tpt == IWL_INVALID_VALUE)) { - if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) { - IWL_DEBUG_RATE(mvm, - "Good SR and no high rate measurement. " - "Increase rate\n"); - scale_action = 1; - } else if (low != IWL_RATE_INVALID) { - IWL_DEBUG_RATE(mvm, - "Remain in current rate\n"); - scale_action = 0; - } - } - - /* Both adjacent throughputs are measured, but neither one has better - * throughput; we're using the best rate, don't change it! */ - else if ((low_tpt != IWL_INVALID_VALUE) && - (high_tpt != IWL_INVALID_VALUE) && - (low_tpt < current_tpt) && - (high_tpt < current_tpt)) { - IWL_DEBUG_RATE(mvm, - "Both high and low are worse. " - "Maintain rate\n"); - scale_action = 0; - } - - /* At least one adjacent rate's throughput is measured, - * and may have better performance. */ - else { - /* Higher adjacent rate's throughput is measured */ - if (high_tpt != IWL_INVALID_VALUE) { - /* Higher rate has better throughput */ - if (high_tpt > current_tpt && - sr >= IWL_RATE_INCREASE_TH) { - IWL_DEBUG_RATE(mvm, - "Higher rate is better and good " - "SR. Increate rate\n"); - scale_action = 1; - } else { - IWL_DEBUG_RATE(mvm, - "Higher rate isn't better OR " - "no good SR. Maintain rate\n"); - scale_action = 0; - } - - /* Lower adjacent rate's throughput is measured */ - } else if (low_tpt != IWL_INVALID_VALUE) { - /* Lower rate has better throughput */ - if (low_tpt > current_tpt) { - IWL_DEBUG_RATE(mvm, - "Lower rate is better. " - "Decrease rate\n"); - scale_action = -1; - } else if (sr >= IWL_RATE_INCREASE_TH) { - IWL_DEBUG_RATE(mvm, - "Lower rate isn't better and " - "good SR. Increase rate\n"); - scale_action = 1; - } - } - } - - /* Sanity check; asked for decrease, but success rate or throughput - * has been good at old rate. Don't change it. */ - if ((scale_action == -1) && (low != IWL_RATE_INVALID) && - ((sr > IWL_RATE_HIGH_TH) || - (current_tpt > (100 * tbl->expected_tpt[low])))) { - IWL_DEBUG_RATE(mvm, - "Sanity check failed. Maintain rate\n"); - scale_action = 0; - } + scale_action = rs_get_rate_action(mvm, tbl, sr, low, high, + current_tpt, low_tpt, high_tpt); /* Force a search in case BT doesn't like us being in MIMO */ if (is_mimo(rate) && @@ -1977,7 +1940,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, } switch (scale_action) { - case -1: + case RS_ACTION_DOWNSCALE: /* Decrease starting rate, update uCode's rate table */ if (low != IWL_RATE_INVALID) { update_lq = 1; @@ -1988,7 +1951,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, } break; - case 1: + case RS_ACTION_UPSCALE: /* Increase starting rate, update uCode's rate table */ if (high != IWL_RATE_INVALID) { update_lq = 1; @@ -1999,7 +1962,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, } break; - case 0: + case RS_ACTION_STAY: /* No change */ default: break; @@ -2053,11 +2016,11 @@ lq_update: rs_rate_scale_clear_window(&(tbl->win[i])); /* Use new "search" start rate */ - index = iwl_hwrate_to_plcp_idx(tbl->current_rate); + index = tbl->rate.index; rs_dump_rate(mvm, &tbl->rate, "Switch to SEARCH TABLE:"); - rs_fill_link_cmd(mvm, sta, lq_sta, tbl->current_rate); + rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate); iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false); } else { done_search = 1; @@ -2095,8 +2058,6 @@ lq_update: } out: - tbl->rate.index = index; - tbl->current_rate = ucode_rate_from_rs_rate(mvm, &tbl->rate); lq_sta->last_txrate_idx = index; } @@ -2123,7 +2084,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, struct iwl_scale_tbl_info *tbl; struct rs_rate *rate; int i; - u32 ucode_rate; u8 active_tbl = 0; u8 valid_tx_ant; @@ -2154,9 +2114,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, else rate->type = LQ_LEGACY_G; - ucode_rate = ucode_rate_from_rs_rate(mvm, rate); - tbl->current_rate = ucode_rate; - WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B); if (rate->ant == ANT_A) tbl->column = RS_COLUMN_LEGACY_ANT_A; @@ -2164,7 +2121,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, tbl->column = RS_COLUMN_LEGACY_ANT_B; rs_set_expected_tpt_table(lq_sta, tbl); - rs_fill_link_cmd(NULL, NULL, lq_sta, ucode_rate); + rs_fill_lq_cmd(NULL, NULL, lq_sta, rate); /* TODO restore station should remember the lq cmd */ iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init); } @@ -2250,6 +2207,10 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, if (i == IWL_RATE_9M_INDEX) continue; + /* Disable MCS9 as a workaround */ + if (i == IWL_RATE_MCS_9_INDEX) + continue; + /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ if (i == IWL_RATE_MCS_9_INDEX && sta->bandwidth == IEEE80211_STA_RX_BW_20) @@ -2268,6 +2229,10 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, if (i == IWL_RATE_9M_INDEX) continue; + /* Disable MCS9 as a workaround */ + if (i == IWL_RATE_MCS_9_INDEX) + continue; + /* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */ if (i == IWL_RATE_MCS_9_INDEX && sta->bandwidth == IEEE80211_STA_RX_BW_20) @@ -2306,7 +2271,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]); lq_sta->flush_timer = 0; - lq_sta->supp_rates = sta->supp_rates[sband->band]; IWL_DEBUG_RATE(mvm, "LQ: *** rate scale station global init for station %d ***\n", @@ -2395,112 +2359,164 @@ static void rs_rate_update(void *mvm_r, iwl_mvm_rs_rate_init(mvm, sta, sband->band, false); } -static void rs_fill_link_cmd(struct iwl_mvm *mvm, - struct ieee80211_sta *sta, - struct iwl_lq_sta *lq_sta, u32 new_rate) +#ifdef CONFIG_MAC80211_DEBUGFS +static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm, + struct iwl_lq_cmd *lq_cmd, + enum ieee80211_band band, + u32 ucode_rate) { struct rs_rate rate; - int index = 0; - int repeat_rate = 0; - u8 ant_toggle_cnt = 0; - u8 use_ht_possible = 1; - u8 valid_tx_ant = 0; - struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; + int i; + int num_rates = ARRAY_SIZE(lq_cmd->rs_table); + __le32 ucode_rate_le32 = cpu_to_le32(ucode_rate); - /* Override starting rate (index 0) if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate); + for (i = 0; i < num_rates; i++) + lq_cmd->rs_table[i] = ucode_rate_le32; - rs_rate_from_ucode_rate(new_rate, lq_sta->band, &rate); + rs_rate_from_ucode_rate(ucode_rate, band, &rate); - /* How many times should we repeat the initial rate? */ - if (is_legacy(&rate)) { - ant_toggle_cnt = 1; - repeat_rate = IWL_NUMBER_TRY; - } else { - repeat_rate = min(IWL_HT_NUMBER_TRY, - LINK_QUAL_AGG_DISABLE_START_DEF - 1); + if (is_mimo(&rate)) + lq_cmd->mimo_delim = num_rates - 1; + else + lq_cmd->mimo_delim = 0; +} +#endif /* CONFIG_MAC80211_DEBUGFS */ + +static void rs_fill_rates_for_column(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + struct rs_rate *rate, + __le32 *rs_table, int *rs_table_index, + int num_rates, int num_retries, + u8 valid_tx_ant, bool toggle_ant) +{ + int i, j; + __le32 ucode_rate; + bool bottom_reached = false; + int prev_rate_idx = rate->index; + int end = LINK_QUAL_MAX_RETRY_NUM; + int index = *rs_table_index; + + for (i = 0; i < num_rates && index < end; i++) { + ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, rate)); + for (j = 0; j < num_retries && index < end; j++, index++) + rs_table[index] = ucode_rate; + + if (toggle_ant) + rs_toggle_antenna(valid_tx_ant, rate); + + prev_rate_idx = rate->index; + bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate); + if (bottom_reached && !is_legacy(rate)) + break; } - lq_cmd->mimo_delim = is_mimo(&rate) ? 1 : 0; + if (!bottom_reached) + rate->index = prev_rate_idx; - /* Fill 1st table entry (index 0) */ - lq_cmd->rs_table[index] = cpu_to_le32(new_rate); + *rs_table_index = index; +} - if (num_of_ant(rate.ant) == 1) - lq_cmd->single_stream_ant_msk = rate.ant; - /* otherwise we don't modify the existing value */ +/* Building the rate table is non trivial. When we're in MIMO2/VHT/80Mhz/SGI + * column the rate table should look like this: + * + * rate[0] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI + * rate[1] 0x400D019 VHT | ANT: AB BW: 80Mhz MCS: 9 NSS: 2 SGI + * rate[2] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI + * rate[3] 0x400D018 VHT | ANT: AB BW: 80Mhz MCS: 8 NSS: 2 SGI + * rate[4] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI + * rate[5] 0x400D017 VHT | ANT: AB BW: 80Mhz MCS: 7 NSS: 2 SGI + * rate[6] 0x4005007 VHT | ANT: A BW: 80Mhz MCS: 7 NSS: 1 NGI + * rate[7] 0x4009006 VHT | ANT: B BW: 80Mhz MCS: 6 NSS: 1 NGI + * rate[8] 0x4005005 VHT | ANT: A BW: 80Mhz MCS: 5 NSS: 1 NGI + * rate[9] 0x800B Legacy | ANT: B Rate: 36 Mbps + * rate[10] 0x4009 Legacy | ANT: A Rate: 24 Mbps + * rate[11] 0x8007 Legacy | ANT: B Rate: 18 Mbps + * rate[12] 0x4005 Legacy | ANT: A Rate: 12 Mbps + * rate[13] 0x800F Legacy | ANT: B Rate: 9 Mbps + * rate[14] 0x400D Legacy | ANT: A Rate: 6 Mbps + * rate[15] 0x800D Legacy | ANT: B Rate: 6 Mbps + */ +static void rs_build_rates_table(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate) +{ + struct rs_rate rate; + int num_rates, num_retries, index = 0; + u8 valid_tx_ant = 0; + struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; + bool toggle_ant = false; + + memcpy(&rate, initial_rate, sizeof(struct rs_rate)); - index++; - repeat_rate--; if (mvm) valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - /* Fill rest of rate table */ - while (index < LINK_QUAL_MAX_RETRY_NUM) { - /* Repeat initial/next rate. - * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. - * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ - while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { - if (is_legacy(&rate)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (mvm && - rs_toggle_antenna(valid_tx_ant, - &new_rate, &rate)) - ant_toggle_cnt = 1; - } + if (is_siso(&rate)) { + num_rates = RS_INITIAL_SISO_NUM_RATES; + num_retries = RS_HT_VHT_RETRIES_PER_RATE; + } else if (is_mimo(&rate)) { + num_rates = RS_INITIAL_MIMO_NUM_RATES; + num_retries = RS_HT_VHT_RETRIES_PER_RATE; + } else { + num_rates = RS_INITIAL_LEGACY_NUM_RATES; + num_retries = RS_LEGACY_RETRIES_PER_RATE; + toggle_ant = true; + } - /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate); + rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, + num_rates, num_retries, valid_tx_ant, + toggle_ant); - /* Fill next table entry */ - lq_cmd->rs_table[index] = - cpu_to_le32(new_rate); - repeat_rate--; - index++; - } + rs_get_lower_rate_down_column(lq_sta, &rate); - rs_rate_from_ucode_rate(new_rate, lq_sta->band, &rate); + if (is_siso(&rate)) { + num_rates = RS_SECONDARY_SISO_NUM_RATES; + num_retries = RS_SECONDARY_SISO_RETRIES; + } else if (is_legacy(&rate)) { + num_rates = RS_SECONDARY_LEGACY_NUM_RATES; + num_retries = RS_LEGACY_RETRIES_PER_RATE; + } else { + WARN_ON_ONCE(1); + } - /* Indicate to uCode which entries might be MIMO. - * If initial rate was MIMO, this will finally end up - * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ - if (is_mimo(&rate)) - lq_cmd->mimo_delim = index; + toggle_ant = true; - /* Get next rate */ - new_rate = rs_get_lower_rate(lq_sta, &rate, rate.index, - use_ht_possible); + rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, + num_rates, num_retries, valid_tx_ant, + toggle_ant); - /* How many times should we repeat the next rate? */ - if (is_legacy(&rate)) { - if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) - ant_toggle_cnt++; - else if (mvm && - rs_toggle_antenna(valid_tx_ant, - &new_rate, &rate)) - ant_toggle_cnt = 1; + rs_get_lower_rate_down_column(lq_sta, &rate); - repeat_rate = IWL_NUMBER_TRY; - } else { - repeat_rate = IWL_HT_NUMBER_TRY; - } + num_rates = RS_SECONDARY_LEGACY_NUM_RATES; + num_retries = RS_LEGACY_RETRIES_PER_RATE; - /* Don't allow HT rates after next pass. - * rs_get_lower_rate() will change type to LQ_LEGACY_A - * or LQ_LEGACY_G. - */ - use_ht_possible = 0; + rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, + num_rates, num_retries, valid_tx_ant, + toggle_ant); - /* Override next rate if needed for debug purposes */ - rs_dbgfs_set_mcs(lq_sta, &new_rate); +} - /* Fill next table entry */ - lq_cmd->rs_table[index] = cpu_to_le32(new_rate); +static void rs_fill_lq_cmd(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate) +{ + struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; + u8 ant = initial_rate->ant; - index++; - repeat_rate--; - } +#ifdef CONFIG_MAC80211_DEBUGFS + if (lq_sta->dbg_fixed_rate) { + rs_build_rates_table_from_fixed(mvm, lq_cmd, + lq_sta->band, + lq_sta->dbg_fixed_rate); + ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >> + RATE_MCS_ANT_POS; + } else +#endif + rs_build_rates_table(mvm, lq_sta, initial_rate); + + if (num_of_ant(ant) == 1) + lq_cmd->single_stream_ant_msk = ant; lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; @@ -2534,31 +2550,6 @@ static void rs_free_sta(void *mvm_r, struct ieee80211_sta *sta, } #ifdef CONFIG_MAC80211_DEBUGFS -static void rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, - u32 *rate_n_flags) -{ - struct iwl_mvm *mvm; - u8 valid_tx_ant; - u8 ant_sel_tx; - - mvm = lq_sta->drv; - valid_tx_ant = iwl_fw_valid_tx_ant(mvm->fw); - if (lq_sta->dbg_fixed_rate) { - ant_sel_tx = - ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) - >> RATE_MCS_ANT_POS); - if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { - *rate_n_flags = lq_sta->dbg_fixed_rate; - } else { - lq_sta->dbg_fixed_rate = 0; - IWL_ERR(mvm, - "Invalid antenna selection 0x%X, Valid is 0x%X\n", - ant_sel_tx, valid_tx_ant); - IWL_DEBUG_RATE(mvm, "Fixed rate OFF\n"); - } - } -} - static int rs_pretty_print_rate(char *buf, const u32 rate) { @@ -2612,6 +2603,31 @@ static int rs_pretty_print_rate(char *buf, const u32 rate) (rate & RATE_MCS_ZLF_MSK) ? "ZLF " : ""); } +/** + * Program the device to use fixed rate for frame transmit + * This is for debugging/testing only + * once the device start use fixed rate, we need to reload the module + * to being back the normal operation. + */ +static void rs_program_fix_rate(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta) +{ + lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ + lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + + IWL_DEBUG_RATE(mvm, "sta_id %d rate 0x%X\n", + lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); + + if (lq_sta->dbg_fixed_rate) { + struct rs_rate rate; + rs_rate_from_ucode_rate(lq_sta->dbg_fixed_rate, + lq_sta->band, &rate); + rs_fill_lq_cmd(NULL, NULL, lq_sta, &rate); + iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false); + } +} + static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { @@ -2702,12 +2718,10 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, lq_sta->lq.initial_rate_index[3]); for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { - u32 rate = le32_to_cpu(lq_sta->lq.rs_table[i]); - desc += sprintf(buff+desc, - " rate[%d] 0x%X ", - i, rate); + u32 r = le32_to_cpu(lq_sta->lq.rs_table[i]); - desc += rs_pretty_print_rate(buff+desc, rate); + desc += sprintf(buff+desc, " rate[%d] 0x%X ", i, r); + desc += rs_pretty_print_rate(buff+desc, r); } ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); @@ -2741,14 +2755,14 @@ static ssize_t rs_sta_dbgfs_stats_table_read(struct file *file, rate = &tbl->rate; desc += sprintf(buff+desc, "%s type=%d SGI=%d BW=%s DUP=0\n" - "rate=0x%X\n", + "index=%d\n", lq_sta->active_tbl == i ? "*" : "x", rate->type, rate->sgi, is_ht20(rate) ? "20Mhz" : is_ht40(rate) ? "40Mhz" : is_ht80(rate) ? "80Mhz" : "ERR", - tbl->current_rate); + rate->index); for (j = 0; j < IWL_RATE_COUNT; j++) { desc += sprintf(buff+desc, "counter=%d success=%d %%=%d\n", diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index b32960796384..c31aa59728ea 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -278,7 +278,6 @@ struct iwl_scale_tbl_info { struct rs_rate rate; enum rs_column column; s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ - u32 current_rate; /* rate_n_flags, uCode API format */ struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ }; @@ -315,7 +314,6 @@ struct iwl_lq_sta { enum ieee80211_band band; /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ - u32 supp_rates; u16 active_legacy_rate; u16 active_siso_rate; u16 active_mimo2_rate; diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 3a1f3982109d..454341cc3b27 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -251,6 +251,12 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, stats->flag |= RX_FLAG_DECRYPTED; return 0; + case RX_MPDU_RES_STATUS_SEC_EXT_ENC: + if (!(rx_pkt_status & RX_MPDU_RES_STATUS_MIC_OK)) + return -1; + stats->flag |= RX_FLAG_DECRYPTED; + return 0; + default: IWL_ERR(mvm, "Unhandled alg: 0x%x\n", rx_pkt_status); } diff --git a/drivers/net/wireless/iwlwifi/mvm/sf.c b/drivers/net/wireless/iwlwifi/mvm/sf.c new file mode 100644 index 000000000000..97bb3c3e75ce --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/sf.c @@ -0,0 +1,291 @@ +/****************************************************************************** + * + * 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) 2013 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 COPYING. + * + * 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) 2013 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 "mvm.h" + +/* For counting bound interfaces */ +struct iwl_mvm_active_iface_iterator_data { + struct ieee80211_vif *ignore_vif; + u8 sta_vif_ap_sta_id; + enum iwl_sf_state sta_vif_state; + int num_active_macs; +}; + +/* + * Count bound interfaces which are not p2p, besides data->ignore_vif. + * data->station_vif will point to one bound vif of type station, if exists. + */ +static void iwl_mvm_bound_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mvm_active_iface_iterator_data *data = _data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (vif == data->ignore_vif || !mvmvif->phy_ctxt || + vif->type == NL80211_IFTYPE_P2P_DEVICE) + return; + + data->num_active_macs++; + + if (vif->type == NL80211_IFTYPE_STATION) { + data->sta_vif_ap_sta_id = mvmvif->ap_sta_id; + if (vif->bss_conf.assoc) + data->sta_vif_state = SF_FULL_ON; + else + data->sta_vif_state = SF_INIT_OFF; + } +} + +/* + * Aging and idle timeouts for the different possible scenarios + * in SF_FULL_ON state. + */ +static const __le32 sf_full_timeout[SF_NUM_SCENARIO][SF_NUM_TIMEOUT_TYPES] = { + { + cpu_to_le32(SF_SINGLE_UNICAST_AGING_TIMER), + cpu_to_le32(SF_SINGLE_UNICAST_IDLE_TIMER) + }, + { + cpu_to_le32(SF_AGG_UNICAST_AGING_TIMER), + cpu_to_le32(SF_AGG_UNICAST_IDLE_TIMER) + }, + { + cpu_to_le32(SF_MCAST_AGING_TIMER), + cpu_to_le32(SF_MCAST_IDLE_TIMER) + }, + { + cpu_to_le32(SF_BA_AGING_TIMER), + cpu_to_le32(SF_BA_IDLE_TIMER) + }, + { + cpu_to_le32(SF_TX_RE_AGING_TIMER), + cpu_to_le32(SF_TX_RE_IDLE_TIMER) + }, +}; + +static void iwl_mvm_fill_sf_command(struct iwl_sf_cfg_cmd *sf_cmd, + struct ieee80211_sta *sta) +{ + int i, j, watermark; + + sf_cmd->watermark[SF_LONG_DELAY_ON] = cpu_to_le32(SF_W_MARK_SCAN); + + /* + * If we are in association flow - check antenna configuration + * capabilities of the AP station, and choose the watermark accordingly. + */ + if (sta) { + if (sta->ht_cap.ht_supported || sta->vht_cap.vht_supported) { + switch (sta->rx_nss) { + case 1: + watermark = SF_W_MARK_SISO; + break; + case 2: + watermark = SF_W_MARK_MIMO2; + break; + default: + watermark = SF_W_MARK_MIMO3; + break; + } + } else { + watermark = SF_W_MARK_LEGACY; + } + /* default watermark value for unassociated mode. */ + } else { + watermark = SF_W_MARK_MIMO2; + } + sf_cmd->watermark[SF_FULL_ON] = cpu_to_le32(watermark); + + for (i = 0; i < SF_NUM_SCENARIO; i++) { + for (j = 0; j < SF_NUM_TIMEOUT_TYPES; j++) { + sf_cmd->long_delay_timeouts[i][j] = + cpu_to_le32(SF_LONG_DELAY_AGING_TIMER); + } + } + BUILD_BUG_ON(sizeof(sf_full_timeout) != + sizeof(__le32) * SF_NUM_SCENARIO * SF_NUM_TIMEOUT_TYPES); + + memcpy(sf_cmd->full_on_timeouts, sf_full_timeout, + sizeof(sf_full_timeout)); +} + +static int iwl_mvm_sf_config(struct iwl_mvm *mvm, u8 sta_id, + enum iwl_sf_state new_state) +{ + struct iwl_sf_cfg_cmd sf_cmd = { + .state = new_state, + }; + struct ieee80211_sta *sta; + int ret = 0; + + /* + * If an associated AP sta changed its antenna configuration, the state + * will remain FULL_ON but SF parameters need to be reconsidered. + */ + if (new_state != SF_FULL_ON && mvm->sf_state == new_state) + return 0; + + switch (new_state) { + case SF_UNINIT: + break; + case SF_FULL_ON: + if (sta_id == IWL_MVM_STATION_COUNT) { + IWL_ERR(mvm, + "No station: Cannot switch SF to FULL_ON\n"); + return -EINVAL; + } + rcu_read_lock(); + sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); + if (IS_ERR_OR_NULL(sta)) { + IWL_ERR(mvm, "Invalid station id\n"); + rcu_read_unlock(); + return -EINVAL; + } + iwl_mvm_fill_sf_command(&sf_cmd, sta); + rcu_read_unlock(); + break; + case SF_INIT_OFF: + iwl_mvm_fill_sf_command(&sf_cmd, NULL); + break; + default: + WARN_ONCE(1, "Invalid state: %d. not sending Smart Fifo cmd\n", + new_state); + return -EINVAL; + } + + ret = iwl_mvm_send_cmd_pdu(mvm, REPLY_SF_CFG_CMD, CMD_ASYNC, + sizeof(sf_cmd), &sf_cmd); + if (!ret) + mvm->sf_state = new_state; + + return ret; +} + +/* + * Update Smart fifo: + * Count bound interfaces that are not to be removed, ignoring p2p devices, + * and set new state accordingly. + */ +int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *changed_vif, + bool remove_vif) +{ + enum iwl_sf_state new_state; + u8 sta_id = IWL_MVM_STATION_COUNT; + struct iwl_mvm_vif *mvmvif = NULL; + struct iwl_mvm_active_iface_iterator_data data = { + .ignore_vif = changed_vif, + .sta_vif_state = SF_UNINIT, + .sta_vif_ap_sta_id = IWL_MVM_STATION_COUNT, + }; + + if (IWL_UCODE_API(mvm->fw->ucode_ver) < 8) + return 0; + + /* + * Ignore the call if we are in HW Restart flow, or if the handled + * vif is a p2p device. + */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) || + (changed_vif && changed_vif->type == NL80211_IFTYPE_P2P_DEVICE)) + return 0; + + ieee80211_iterate_active_interfaces_atomic(mvm->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mvm_bound_iface_iterator, + &data); + + /* If changed_vif exists and is not to be removed, add to the count */ + if (changed_vif && !remove_vif) + data.num_active_macs++; + + switch (data.num_active_macs) { + case 0: + /* If there are no active macs - change state to SF_INIT_OFF */ + new_state = SF_INIT_OFF; + break; + case 1: + if (remove_vif) { + /* The one active mac left is of type station + * and we filled the relevant data during iteration + */ + new_state = data.sta_vif_state; + sta_id = data.sta_vif_ap_sta_id; + } else { + if (WARN_ON(!changed_vif)) + return -EINVAL; + if (changed_vif->type != NL80211_IFTYPE_STATION) { + new_state = SF_UNINIT; + } else if (changed_vif->bss_conf.assoc) { + mvmvif = iwl_mvm_vif_from_mac80211(changed_vif); + sta_id = mvmvif->ap_sta_id; + new_state = SF_FULL_ON; + } else { + new_state = SF_INIT_OFF; + } + } + break; + default: + /* If there are multiple active macs - change to SF_UNINIT */ + new_state = SF_UNINIT; + } + return iwl_mvm_sf_config(mvm, sta_id, new_state); +} diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index 7a5b7473eafa..0a8af2083ddc 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -452,8 +452,15 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk) rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], lockdep_is_held(&mvm->mutex)); - /* This station is in use */ - if (!IS_ERR(sta)) + /* + * This station is in use or RCU-removed; the latter happens in + * managed mode, where mac80211 removes the station before we + * can remove it from firmware (we can only do that after the + * MAC is marked unassociated), and possibly while the deauth + * frame to disconnect from the AP is still queued. Then, the + * station pointer is -ENOENT when the last skb is reclaimed. + */ + if (!IS_ERR(sta) || PTR_ERR(sta) == -ENOENT) continue; if (PTR_ERR(sta) == -EINVAL) { @@ -932,19 +939,6 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, IWL_DEBUG_HT(mvm, "Tx aggregation enabled on ra = %pM tid = %d\n", sta->addr, tid); - if (mvm->cfg->ht_params->use_rts_for_aggregation) { - /* - * switch to RTS/CTS if it is the prefer protection - * method for HT traffic - * this function also sends the LQ command - */ - return iwl_mvm_tx_protection(mvm, mvmsta, true); - /* - * TODO: remove the TLC_RTS flag when we tear down the last - * AGG session (agg_tids_count in DVM) - */ - } - return iwl_mvm_send_lq_cmd(mvm, &mvmsta->lq_sta.lq, false); } @@ -1123,8 +1117,8 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, memcpy(cmd.key, keyconf->key, keyconf->keylen); break; default: - WARN_ON(1); - return -EINVAL; + key_flags |= cpu_to_le16(STA_KEY_FLG_EXT); + memcpy(cmd.key, keyconf->key, keyconf->keylen); } if (!(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) @@ -1288,8 +1282,8 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, 0, NULL, CMD_SYNC); break; default: - IWL_ERR(mvm, "Unknown cipher %x\n", keyconf->cipher); - ret = -EINVAL; + ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, + sta_id, 0, NULL, CMD_SYNC); } if (ret) diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c index 18be04da8e3f..a0ec7b3473bd 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -340,7 +340,7 @@ static void check_exit_ctkill(struct work_struct *work) iwl_trans_start_hw(mvm->trans); temp = check_nic_temperature(mvm); - iwl_trans_stop_hw(mvm->trans, false); + iwl_trans_stop_device(mvm->trans); if (temp < MIN_TEMPERATURE || temp > MAX_TEMPERATURE) { IWL_DEBUG_TEMP(mvm, "Failed to measure NIC temperature\n"); diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index d87649ac88e1..735f86da7985 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -253,8 +253,7 @@ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); break; default: - IWL_ERR(mvm, "Unknown encode cipher %x\n", keyconf->cipher); - break; + tx_cmd->sec_ctl |= TX_CMD_SEC_EXT; } } diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index 56cf819bc0c7..f4aff56a36da 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -518,6 +518,11 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, int i; lockdep_assert_held(&mvm->mutex); + + /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */ + if (num_of_ant(iwl_fw_valid_rx_ant(mvm->fw)) == 1) + return; + mvmvif = iwl_mvm_vif_from_mac80211(vif); mvmvif->smps_requests[req_type] = smps_request; for (i = 0; i < NUM_IWL_MVM_SMPS_REQ; i++) { diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 051268c037b1..674c75b0d002 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -256,7 +256,6 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) * @hw_base: pci hardware address support * @ucode_write_complete: indicates that the ucode has been copied. * @ucode_write_waitq: wait queue for uCode load - * @status - transport specific status flags * @cmd_queue - command queue number * @rx_buf_size_8k: 8 kB RX buffer size * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) @@ -296,7 +295,6 @@ struct iwl_trans_pcie { wait_queue_head_t ucode_write_waitq; wait_queue_head_t wait_command_queue; - unsigned long status; u8 cmd_queue; u8 cmd_fifo; u8 n_no_reclaim_cmds; @@ -315,24 +313,6 @@ struct iwl_trans_pcie { spinlock_t reg_lock; }; -/** - * enum iwl_pcie_status: status of the PCIe transport - * @STATUS_HCMD_ACTIVE: a SYNC command is being processed - * @STATUS_DEVICE_ENABLED: APM is enabled - * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up) - * @STATUS_INT_ENABLED: interrupts are enabled - * @STATUS_RFKILL: the HW RFkill switch is in KILL position - * @STATUS_FW_ERROR: the fw is in error state - */ -enum iwl_pcie_status { - STATUS_HCMD_ACTIVE, - STATUS_DEVICE_ENABLED, - STATUS_TPOWER_PMI, - STATUS_INT_ENABLED, - STATUS_RFKILL, - STATUS_FW_ERROR, -}; - #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) @@ -399,8 +379,7 @@ void iwl_pcie_dump_csr(struct iwl_trans *trans); ******************************************************/ static inline void iwl_disable_interrupts(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); + clear_bit(STATUS_INT_ENABLED, &trans->status); /* disable interrupts from uCode/NIC to host */ iwl_write32(trans, CSR_INT_MASK, 0x00000000); @@ -417,7 +396,7 @@ static inline void iwl_enable_interrupts(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); IWL_DEBUG_ISR(trans, "Enabling interrupts\n"); - set_bit(STATUS_INT_ENABLED, &trans_pcie->status); + set_bit(STATUS_INT_ENABLED, &trans->status); iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); } @@ -477,12 +456,4 @@ static inline bool iwl_is_rfkill_set(struct iwl_trans *trans) CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW); } -static inline void iwl_nic_error(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - set_bit(STATUS_FW_ERROR, &trans_pcie->status); - iwl_op_mode_nic_error(trans->op_mode); -} - #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 1d6bf7b98e2d..7aeec5ccefa5 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -162,11 +162,8 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, rxq->write_actual = (rxq->write & ~0x7); iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); } else { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - /* If power-saving is in use, make sure device is awake */ - if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) { + if (test_bit(STATUS_TPOWER_PMI, &trans->status)) { reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { @@ -222,7 +219,7 @@ static void iwl_pcie_rxq_restock(struct iwl_trans *trans) * stopped, we cannot access the HW (in particular not prph). * So don't try to restock if the APM has been already stopped. */ - if (!test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) + if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) return; spin_lock_irqsave(&rxq->lock, flags); @@ -791,7 +788,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) APMS_CLK_VAL_MRB_FUNC_MODE) || (iwl_read_prph(trans, APMG_PS_CTRL_REG) & APMG_PS_CTRL_VAL_RESET_REQ))) { - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); iwl_op_mode_wimax_active(trans->op_mode); wake_up(&trans_pcie->wait_command_queue); return; @@ -800,14 +797,14 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) iwl_pcie_dump_csr(trans); iwl_dump_fh(trans, NULL); - /* set the ERROR bit before we wake up the caller */ - set_bit(STATUS_FW_ERROR, &trans_pcie->status); - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - wake_up(&trans_pcie->wait_command_queue); - local_bh_disable(); - iwl_nic_error(trans); + /* The STATUS_FW_ERROR bit is set in this function. This must happen + * before we wake up the command caller, to ensure a proper cleanup. */ + iwl_trans_fw_error(trans); local_bh_enable(); + + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + wake_up(&trans_pcie->wait_command_queue); } irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) @@ -894,14 +891,14 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); if (hw_rfkill) { - set_bit(STATUS_RFKILL, &trans_pcie->status); - if (test_and_clear_bit(STATUS_HCMD_ACTIVE, - &trans_pcie->status)) + set_bit(STATUS_RFKILL, &trans->status); + if (test_and_clear_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status)) IWL_DEBUG_RF_KILL(trans, "Rfkill while SYNC HCMD in flight\n"); wake_up(&trans_pcie->wait_command_queue); } else { - clear_bit(STATUS_RFKILL, &trans_pcie->status); + clear_bit(STATUS_RFKILL, &trans->status); } handled |= CSR_INT_BIT_RF_KILL; @@ -1005,7 +1002,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) /* Re-enable all interrupts */ /* only Re-enable if disabled by irq */ - if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status)) + if (test_bit(STATUS_INT_ENABLED, &trans->status)) iwl_enable_interrupts(trans); /* Re-enable RF_KILL if it occurred */ else if (handled & CSR_INT_BIT_RF_KILL) @@ -1160,7 +1157,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data) * the handler can be scheduled because of a previous * interrupt. */ - if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && + if (test_bit(STATUS_INT_ENABLED, &trans->status) && !trans_pcie->inta) iwl_enable_interrupts(trans); return IRQ_NONE; @@ -1290,7 +1287,7 @@ irqreturn_t iwl_pcie_isr_ict(int irq, void *data) /* re-enable interrupts here since we don't have anything to service. * only Re-enable if disabled by irq. */ - if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && + if (test_bit(STATUS_INT_ENABLED, &trans->status) && !trans_pcie->inta) iwl_enable_interrupts(trans); diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index cde9c16f6e4f..eecd38e3f15f 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -150,7 +150,6 @@ static void iwl_pcie_apm_config(struct iwl_trans *trans) */ static int iwl_pcie_apm_init(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret = 0; IWL_DEBUG_INFO(trans, "Init card's basic functions\n"); @@ -223,7 +222,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) /* Clear the interrupt in APMG if the NIC is in RFKILL */ iwl_write_prph(trans, APMG_RTC_INT_STT_REG, APMG_RTC_INT_STT_RFKILL); - set_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); + set_bit(STATUS_DEVICE_ENABLED, &trans->status); out: return ret; @@ -249,10 +248,9 @@ static int iwl_pcie_apm_stop_master(struct iwl_trans *trans) static void iwl_pcie_apm_stop(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); - clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); + clear_bit(STATUS_DEVICE_ENABLED, &trans->status); /* Stop device's DMA activity */ iwl_pcie_apm_stop_master(trans); @@ -582,7 +580,6 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; bool hw_rfkill; @@ -592,16 +589,14 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, return -EIO; } - clear_bit(STATUS_FW_ERROR, &trans_pcie->status); - iwl_enable_rfkill_int(trans); /* If platform's RF_KILL switch is NOT set to KILL */ hw_rfkill = iwl_is_rfkill_set(trans); if (hw_rfkill) - set_bit(STATUS_RFKILL, &trans_pcie->status); + set_bit(STATUS_RFKILL, &trans->status); else - clear_bit(STATUS_RFKILL, &trans_pcie->status); + clear_bit(STATUS_RFKILL, &trans->status); iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); if (hw_rfkill && !run_in_rfkill) return -ERFKILL; @@ -641,6 +636,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; + bool hw_rfkill; /* tell the device to stop sending interrupts */ spin_lock_irqsave(&trans_pcie->irq_lock, flags); @@ -657,7 +653,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) * restart. So don't process again if the device is * already dead. */ - if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) { + if (test_bit(STATUS_DEVICE_ENABLED, &trans->status)) { iwl_pcie_tx_stop(trans); iwl_pcie_rx_stop(trans); @@ -681,17 +677,34 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) iwl_disable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - iwl_enable_rfkill_int(trans); - /* stop and reset the on-board processor */ iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); /* clear all status bits */ - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); - clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); - clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); - clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); - clear_bit(STATUS_RFKILL, &trans_pcie->status); + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); + clear_bit(STATUS_INT_ENABLED, &trans->status); + clear_bit(STATUS_DEVICE_ENABLED, &trans->status); + clear_bit(STATUS_TPOWER_PMI, &trans->status); + clear_bit(STATUS_RFKILL, &trans->status); + + /* + * Even if we stop the HW, we still want the RF kill + * interrupt + */ + iwl_enable_rfkill_int(trans); + + /* + * Check again since the RF kill state may have changed while + * all the interrupts were disabled, in this case we couldn't + * receive the RF kill interrupt and update the state in the + * op_mode. + */ + hw_rfkill = iwl_is_rfkill_set(trans); + if (hw_rfkill) + set_bit(STATUS_RFKILL, &trans->status); + else + clear_bit(STATUS_RFKILL, &trans->status); + iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); } static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test) @@ -776,7 +789,6 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); bool hw_rfkill; int err; @@ -798,21 +810,20 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) hw_rfkill = iwl_is_rfkill_set(trans); if (hw_rfkill) - set_bit(STATUS_RFKILL, &trans_pcie->status); + set_bit(STATUS_RFKILL, &trans->status); else - clear_bit(STATUS_RFKILL, &trans_pcie->status); + clear_bit(STATUS_RFKILL, &trans->status); iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); return 0; } -static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, - bool op_mode_leaving) +static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - bool hw_rfkill; unsigned long flags; + /* disable interrupts - don't enable HW RF kill interrupt */ spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_disable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); @@ -824,27 +835,6 @@ static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); iwl_pcie_disable_ict(trans); - - if (!op_mode_leaving) { - /* - * Even if we stop the HW, we still want the RF kill - * interrupt - */ - iwl_enable_rfkill_int(trans); - - /* - * Check again since the RF kill state may have changed while - * all the interrupts were disabled, in this case we couldn't - * receive the RF kill interrupt and update the state in the - * op_mode. - */ - hw_rfkill = iwl_is_rfkill_set(trans); - if (hw_rfkill) - set_bit(STATUS_RFKILL, &trans_pcie->status); - else - clear_bit(STATUS_RFKILL, &trans_pcie->status); - iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); - } } static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) @@ -928,12 +918,10 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) static void iwl_trans_pcie_set_pmi(struct iwl_trans *trans, bool state) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (state) - set_bit(STATUS_TPOWER_PMI, &trans_pcie->status); + set_bit(STATUS_TPOWER_PMI, &trans->status); else - clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); + clear_bit(STATUS_TPOWER_PMI, &trans->status); } static bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent, @@ -1457,7 +1445,7 @@ static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans, static const struct iwl_trans_ops trans_ops_pcie = { .start_hw = iwl_trans_pcie_start_hw, - .stop_hw = iwl_trans_pcie_stop_hw, + .op_mode_leave = iwl_trans_pcie_op_mode_leave, .fw_alive = iwl_trans_pcie_fw_alive, .start_fw = iwl_trans_pcie_start_fw, .stop_device = iwl_trans_pcie_stop_device, diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index a4ef5cc11100..8df24787c141 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -207,7 +207,7 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) IWL_ERR(trans, "scratch %d = 0x%08x\n", i, le32_to_cpu(txq->scratchbufs[i].scratch)); - iwl_nic_error(trans); + iwl_trans_fw_error(trans); } /* @@ -300,10 +300,8 @@ void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq) iwl_write32(trans, HBUS_TARG_WRPTR, txq->q.write_ptr | (txq_id << 8)); } else { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); /* if we're trying to save power */ - if (test_bit(STATUS_TPOWER_PMI, &trans_pcie->status)) { + if (test_bit(STATUS_TPOWER_PMI, &trans->status)) { /* wake up nic if it's powered down ... * uCode will wake up, and interrupt us again, so next * time we'll skip this part. */ @@ -1023,7 +1021,7 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) if (nfreed++ > 0) { IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", idx, q->write_ptr, q->read_ptr); - iwl_nic_error(trans); + iwl_trans_fw_error(trans); } } @@ -1449,12 +1447,12 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, iwl_pcie_cmdq_reclaim(trans, txq_id, index); if (!(meta->flags & CMD_ASYNC)) { - if (!test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { + if (!test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status)) { IWL_WARN(trans, "HCMD_ACTIVE already clear for command %s\n", get_cmd_string(trans_pcie, cmd->hdr.cmd)); } - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", get_cmd_string(trans_pcie, cmd->hdr.cmd)); wake_up(&trans_pcie->wait_command_queue); @@ -1499,8 +1497,8 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", get_cmd_string(trans_pcie, cmd->id)); - if (WARN(test_and_set_bit(STATUS_HCMD_ACTIVE, - &trans_pcie->status), + if (WARN(test_and_set_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status), "Command %s: a command is already active!\n", get_cmd_string(trans_pcie, cmd->id))) return -EIO; @@ -1511,7 +1509,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd); if (cmd_idx < 0) { ret = cmd_idx; - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", get_cmd_string(trans_pcie, cmd->id), ret); @@ -1523,8 +1521,8 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, timeout -= COMMAND_POKE_TIMEOUT; ret = wait_event_timeout(trans_pcie->wait_command_queue, - !test_bit(STATUS_HCMD_ACTIVE, - &trans_pcie->status), + !test_bit(STATUS_SYNC_HCMD_ACTIVE, + &trans->status), COMMAND_POKE_TIMEOUT); if (ret) break; @@ -1552,17 +1550,17 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, IWL_ERR(trans, "Current CMD queue read_ptr %d write_ptr %d\n", q->read_ptr, q->write_ptr); - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", get_cmd_string(trans_pcie, cmd->id)); ret = -ETIMEDOUT; - iwl_nic_error(trans); + iwl_trans_fw_error(trans); goto cancel; } - if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) { + if (test_bit(STATUS_FW_ERROR, &trans->status)) { IWL_ERR(trans, "FW error in SYNC CMD %s\n", get_cmd_string(trans_pcie, cmd->id)); dump_stack(); @@ -1571,7 +1569,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, } if (!(cmd->flags & CMD_SEND_IN_RFKILL) && - test_bit(STATUS_RFKILL, &trans_pcie->status)) { + test_bit(STATUS_RFKILL, &trans->status)) { IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); ret = -ERFKILL; goto cancel; @@ -1608,13 +1606,8 @@ cancel: int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) - return -EIO; - if (!(cmd->flags & CMD_SEND_IN_RFKILL) && - test_bit(STATUS_RFKILL, &trans_pcie->status)) { + test_bit(STATUS_RFKILL, &trans->status)) { IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n", cmd->id); return -ERFKILL; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 4d23647faef0..b994679abce0 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -538,23 +538,40 @@ static void mwifiex_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv = mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY); wiphy_dbg(wiphy, "info: cfg80211 regulatory domain callback for %c%c\n", request->alpha2[0], request->alpha2[1]); - memcpy(adapter->country_code, request->alpha2, sizeof(request->alpha2)); - switch (request->initiator) { case NL80211_REGDOM_SET_BY_DRIVER: case NL80211_REGDOM_SET_BY_CORE: case NL80211_REGDOM_SET_BY_USER: - break; - /* Todo: apply driver specific changes in channel flags based - on the request initiator if necessary. */ case NL80211_REGDOM_SET_BY_COUNTRY_IE: break; + default: + wiphy_err(wiphy, "unknown regdom initiator: %d\n", + request->initiator); + return; + } + + /* Don't send world or same regdom info to firmware */ + if (strncmp(request->alpha2, "00", 2) && + strncmp(request->alpha2, adapter->country_code, + sizeof(request->alpha2))) { + memcpy(adapter->country_code, request->alpha2, + sizeof(request->alpha2)); + mwifiex_send_domain_info_cmd_fw(wiphy); + + if (adapter->dt_node) { + char txpwr[] = {"marvell,00_txpwrlimit"}; + + memcpy(&txpwr[8], adapter->country_code, 2); + mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node, + txpwr); + } } - mwifiex_send_domain_info_cmd_fw(wiphy); } /* @@ -1171,10 +1188,10 @@ static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, else bitmap_rates[1] = mask->control[band].legacy; - /* Fill MCS rates */ - bitmap_rates[2] = mask->control[band].mcs[0]; + /* Fill HT MCS rates */ + bitmap_rates[2] = mask->control[band].ht_mcs[0]; if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) - bitmap_rates[2] |= mask->control[band].mcs[1] << 8; + bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; return mwifiex_send_cmd_sync(priv, HostCmd_CMD_TX_RATE_CFG, HostCmd_ACT_GEN_SET, 0, bitmap_rates); diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 8fcb500fa09b..4cee6ceb7e9e 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -468,8 +468,6 @@ enum P2P_MODES { #define MWIFIEX_CRITERIA_UNICAST BIT(1) #define MWIFIEX_CRITERIA_MULTICAST BIT(3) -#define CFG_DATA_TYPE_CAL 2 - struct mwifiex_ie_types_header { __le16 type; __le16 len; @@ -1592,12 +1590,6 @@ struct mwifiex_ie_list { struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX]; } __packed; -struct host_cmd_ds_802_11_cfg_data { - __le16 action; - __le16 type; - __le16 data_len; -} __packed; - struct coalesce_filt_field_param { u8 operation; u8 operand_len; @@ -1678,7 +1670,6 @@ struct host_cmd_ds_command { struct host_cmd_ds_sys_config uap_sys_config; struct host_cmd_ds_sta_deauth sta_deauth; struct host_cmd_11ac_vht_cfg vht_cfg; - struct host_cmd_ds_802_11_cfg_data cfg_data; struct host_cmd_ds_coalesce_cfg coalesce_cfg; } params; } __packed; diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index dc34457557f3..ab3416449bfd 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -32,6 +32,7 @@ #include <net/lib80211.h> #include <linux/firmware.h> #include <linux/ctype.h> +#include <linux/of.h> #include "decl.h" #include "ioctl.h" @@ -739,6 +740,7 @@ struct mwifiex_adapter { u8 scan_delay_cnt; u8 empty_tx_q_cnt; const struct firmware *cal_data; + struct device_node *dt_node; /* 11AC */ u32 is_hw_11ac_capable; @@ -1151,6 +1153,8 @@ void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, struct mwifiex_bssdescriptor *bss_desc); int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv); +int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, + struct device_node *node, const char *prefix); extern const struct ethtool_ops mwifiex_ethtool_ops; diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 1efa43ec1c6e..9c2404cd755f 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1156,30 +1156,61 @@ static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst) return d - dst; } +int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, + struct device_node *node, const char *prefix) +{ +#ifdef CONFIG_OF + struct property *prop; + size_t len = strlen(prefix); + int ret; + + /* look for all matching property names */ + for_each_property_of_node(node, prop) { + if (len > strlen(prop->name) || + strncmp(prop->name, prefix, len)) + continue; + + /* property header is 6 bytes */ + if (prop && prop->value && prop->length > 6) { + ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, 0, + prop); + if (ret) + return ret; + } + } +#endif + return 0; +} + /* This function prepares command of set_cfg_data. */ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action) + struct host_cmd_ds_command *cmd, void *data_buf) { - struct host_cmd_ds_802_11_cfg_data *cfg_data = &cmd->params.cfg_data; struct mwifiex_adapter *adapter = priv->adapter; - u32 len, cal_data_offset; - u8 *tmp_cmd = (u8 *)cmd; + struct property *prop = data_buf; + u32 len; + u8 *data = (u8 *)cmd + S_DS_GEN; + int ret; - cal_data_offset = S_DS_GEN + sizeof(*cfg_data); - if ((adapter->cal_data->data) && (adapter->cal_data->size > 0)) + if (prop) { + len = prop->length; + ret = of_property_read_u8_array(adapter->dt_node, prop->name, + data, len); + if (ret) + return ret; + dev_dbg(adapter->dev, + "download cfg_data from device tree: %s\n", prop->name); + } else if (adapter->cal_data->data && adapter->cal_data->size > 0) { len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data, - adapter->cal_data->size, - (u8 *)(tmp_cmd + cal_data_offset)); - else + adapter->cal_data->size, data); + dev_dbg(adapter->dev, "download cfg_data from config file\n"); + } else { return -1; - - cfg_data->action = cpu_to_le16(cmd_action); - cfg_data->type = cpu_to_le16(CFG_DATA_TYPE_CAL); - cfg_data->data_len = cpu_to_le16(len); + } cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); - cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*cfg_data) + len); + cmd->size = cpu_to_le16(S_DS_GEN + len); return 0; } @@ -1267,7 +1298,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); break; case HostCmd_CMD_CFG_DATA: - ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, cmd_action); + ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, data_buf); break; case HostCmd_CMD_MAC_CONTROL: ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, @@ -1527,7 +1558,19 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) if (ret) return -1; - /* Download calibration data to firmware */ + /* Download calibration data to firmware. + * The cal-data can be read from device tree and/or + * a configuration file and downloaded to firmware. + */ + adapter->dt_node = + of_find_node_by_name(NULL, "marvell_cfgdata"); + if (adapter->dt_node) { + ret = mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node, + "marvell,caldata"); + if (ret) + return -1; + } + if (adapter->cal_data) { ret = mwifiex_send_cmd_sync(priv, HostCmd_CMD_CFG_DATA, HostCmd_ACT_GEN_SET, 0, NULL); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index a09398fe9e2a..3edc92fad319 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -205,6 +205,14 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv, return 0; } + if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) { + rcu_read_unlock(); + wiphy_dbg(priv->wdev->wiphy, + "11D: skip setting domain info in FW\n"); + return 0; + } + memcpy(priv->adapter->country_code, &country_ie[2], 2); + domain_info->country_code[0] = country_ie[2]; domain_info->country_code[1] = country_ie[3]; domain_info->country_code[2] = ' '; @@ -226,6 +234,13 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv, return -1; } + if (priv->adapter->dt_node) { + char txpwr[] = {"marvell,00_txpwrlimit"}; + + memcpy(&txpwr[8], priv->adapter->country_code, 2); + mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr); + } + return 0; } diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e9abc7b536cd..80a10212b1b9 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -747,6 +747,8 @@ enum station_parameters_apply_mask { * @supported_channels_len: number of supported channels * @supported_oper_classes: supported oper classes in IEEE 802.11 format * @supported_oper_classes_len: number of supported operating classes + * @opmode_notif: operating mode field from Operating Mode Notification + * @opmode_notif_used: information if operating mode field is used */ struct station_parameters { const u8 *supported_rates; @@ -770,6 +772,8 @@ struct station_parameters { u8 supported_channels_len; const u8 *supported_oper_classes; u8 supported_oper_classes_len; + u8 opmode_notif; + bool opmode_notif_used; }; /** @@ -1762,7 +1766,8 @@ enum wiphy_params_flags { struct cfg80211_bitrate_mask { struct { u32 legacy; - u8 mcs[IEEE80211_HT_MCS_MASK_LEN]; + u8 ht_mcs[IEEE80211_HT_MCS_MASK_LEN]; + u16 vht_mcs[NL80211_VHT_NSS_MAX]; } control[IEEE80211_NUM_BANDS]; }; /** @@ -2676,6 +2681,34 @@ struct wiphy_coalesce_support { }; /** + * enum wiphy_vendor_command_flags - validation flags for vendor commands + * @WIPHY_VENDOR_CMD_NEED_WDEV: vendor command requires wdev + * @WIPHY_VENDOR_CMD_NEED_NETDEV: vendor command requires netdev + * @WIPHY_VENDOR_CMD_NEED_RUNNING: interface/wdev must be up & running + * (must be combined with %_WDEV or %_NETDEV) + */ +enum wiphy_vendor_command_flags { + WIPHY_VENDOR_CMD_NEED_WDEV = BIT(0), + WIPHY_VENDOR_CMD_NEED_NETDEV = BIT(1), + WIPHY_VENDOR_CMD_NEED_RUNNING = BIT(2), +}; + +/** + * struct wiphy_vendor_command - vendor command definition + * @info: vendor command identifying information, as used in nl80211 + * @flags: flags, see &enum wiphy_vendor_command_flags + * @doit: callback for the operation, note that wdev is %NULL if the + * flags didn't ask for a wdev and non-%NULL otherwise; the data + * pointer may be %NULL if userspace provided no data at all + */ +struct wiphy_vendor_command { + struct nl80211_vendor_cmd_info info; + u32 flags; + int (*doit)(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len); +}; + +/** * struct wiphy - wireless hardware description * @reg_notifier: the driver's regulatory notification callback, * note that if your driver uses wiphy_apply_custom_regulatory() @@ -2788,6 +2821,9 @@ struct wiphy_coalesce_support { * @extended_capabilities_mask: mask of the valid values * @extended_capabilities_len: length of the extended capabilities * @coalesce: packet coalescing support information + * + * @vendor_commands: array of vendor commands supported by the hardware + * @n_vendor_commands: number of vendor commands */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -2899,6 +2935,9 @@ struct wiphy { const struct wiphy_coalesce_support *coalesce; + const struct wiphy_vendor_command *vendor_commands; + int n_vendor_commands; + char priv[0] __aligned(NETDEV_ALIGN); }; @@ -3843,6 +3882,75 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy); */ void wiphy_rfkill_stop_polling(struct wiphy *wiphy); +/** + * DOC: Vendor commands + * + * Occasionally, there are special protocol or firmware features that + * can't be implemented very openly. For this and similar cases, the + * vendor command functionality allows implementing the features with + * (typically closed-source) userspace and firmware, using nl80211 as + * the configuration mechanism. + * + * A driver supporting vendor commands must register them as an array + * in struct wiphy, with handlers for each one, each command has an + * OUI and sub command ID to identify it. + * + * Note that this feature should not be (ab)used to implement protocol + * features that could openly be shared across drivers. In particular, + * it must never be required to use vendor commands to implement any + * "normal" functionality that higher-level userspace like connection + * managers etc. need. + */ + +struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, + enum nl80211_commands cmd, + enum nl80211_attrs attr, + int approxlen); + +/** + * cfg80211_vendor_cmd_alloc_reply_skb - allocate vendor command reply + * @wiphy: the wiphy + * @approxlen: an upper bound of the length of the data that will + * be put into the skb + * + * This function allocates and pre-fills an skb for a reply to + * a vendor command. Since it is intended for a reply, calling + * it outside of a vendor command's doit() operation is invalid. + * + * The returned skb is pre-filled with some identifying data in + * a way that any data that is put into the skb (with skb_put(), + * nla_put() or similar) will end up being within the + * %NL80211_ATTR_VENDOR_DATA attribute, so all that needs to be done + * with the skb is adding data for the corresponding userspace tool + * which can then read that data out of the vendor data attribute. + * You must not modify the skb in any other way. + * + * When done, call cfg80211_vendor_cmd_reply() with the skb and return + * its error code as the result of the doit() operation. + * + * Return: An allocated and pre-filled skb. %NULL if any errors happen. + */ +static inline struct sk_buff * +cfg80211_vendor_cmd_alloc_reply_skb(struct wiphy *wiphy, int approxlen) +{ + return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_VENDOR, + NL80211_ATTR_VENDOR_DATA, approxlen); +} + +/** + * cfg80211_vendor_cmd_reply - send the reply skb + * @skb: The skb, must have been allocated with + * cfg80211_vendor_cmd_alloc_reply_skb() + * + * Since calling this function will usually be the last thing + * before returning from the vendor command doit() you should + * return the error code. Note that this function consumes the + * skb regardless of the return value. + * + * Return: An error code or 0 on success. + */ +int cfg80211_vendor_cmd_reply(struct sk_buff *skb); + #ifdef CONFIG_NL80211_TESTMODE /** * DOC: Test mode @@ -3878,8 +3986,12 @@ void wiphy_rfkill_stop_polling(struct wiphy *wiphy); * * Return: An allocated and pre-filled skb. %NULL if any errors happen. */ -struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, - int approxlen); +static inline struct sk_buff * +cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, int approxlen) +{ + return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_TESTMODE, + NL80211_ATTR_TESTDATA, approxlen); +} /** * cfg80211_testmode_reply - send the reply skb @@ -3893,7 +4005,10 @@ struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, * * Return: An error code or 0 on success. */ -int cfg80211_testmode_reply(struct sk_buff *skb); +static inline int cfg80211_testmode_reply(struct sk_buff *skb) +{ + return cfg80211_vendor_cmd_reply(skb); +} /** * cfg80211_testmode_alloc_event_skb - allocate testmode event diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 3cd408b326de..531785f5819e 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -1163,6 +1163,19 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif) } /** + * wdev_to_ieee80211_vif - return a vif struct from a wdev + * @wdev: the wdev to get the vif for + * + * This can be used by mac80211 drivers with direct cfg80211 APIs + * (like the vendor commands) that get a wdev. + * + * Note that this function may return %NULL if the given wdev isn't + * associated with a vif that the driver knows about (e.g. monitor + * or AP_VLAN interfaces.) + */ +struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev); + +/** * enum ieee80211_key_flags - key flags * * These flags are used for communication about keys between the driver @@ -1600,6 +1613,9 @@ enum ieee80211_hw_flags { * @extra_tx_headroom: headroom to reserve in each transmit skb * for use by the driver (e.g. for transmit headers.) * + * @extra_beacon_tailroom: tailroom to reserve in each beacon tx skb. + * Can be used by drivers to add extra IEs. + * * @channel_change_time: time (in microseconds) it takes to change channels. * * @max_signal: Maximum value for signal (rssi) in RX information, used @@ -1682,6 +1698,7 @@ struct ieee80211_hw { void *priv; u32 flags; unsigned int extra_tx_headroom; + unsigned int extra_beacon_tailroom; int channel_change_time; int vif_data_size; int sta_data_size; @@ -2398,9 +2415,6 @@ enum ieee80211_roc_type { * See the section "Frame filtering" for more information. * This callback must be implemented and can sleep. * - * @set_multicast_list: Configure the device's interface specific RX multicast - * filter. This callback is optional. This callback must be atomic. - * * @set_tim: Set TIM bit. mac80211 calls this function when a TIM bit * must be set or cleared for a given STA. Must be atomic. * @@ -2485,7 +2499,11 @@ enum ieee80211_roc_type { * AP, IBSS/WDS/mesh peer etc. This callback can sleep. * * @sta_remove: Notifies low level driver about removal of an associated - * station, AP, IBSS/WDS/mesh peer etc. This callback can sleep. + * station, AP, IBSS/WDS/mesh peer etc. Note that after the callback + * returns it isn't safe to use the pointer, not even RCU protected; + * no RCU grace period is guaranteed between returning here and freeing + * the station. See @sta_pre_rcu_remove if needed. + * This callback can sleep. * * @sta_add_debugfs: Drivers can use this callback to add debugfs files * when a station is added to mac80211's station list. This callback @@ -2504,7 +2522,17 @@ enum ieee80211_roc_type { * station (which can be the AP, a client, IBSS/WDS/mesh peer etc.) * This callback is mutually exclusive with @sta_add/@sta_remove. * It must not fail for down transitions but may fail for transitions - * up the list of states. + * up the list of states. Also note that after the callback returns it + * isn't safe to use the pointer, not even RCU protected - no RCU grace + * period is guaranteed between returning here and freeing the station. + * See @sta_pre_rcu_remove if needed. + * The callback can sleep. + * + * @sta_pre_rcu_remove: Notify driver about station removal before RCU + * synchronisation. This is useful if a driver needs to have station + * pointers protected using RCU, it can then use this call to clear + * the pointers instead of waiting for an RCU grace period to elapse + * in @sta_state. * The callback can sleep. * * @sta_rc_update: Notifies the driver of changes to the bitrates that can be @@ -2764,10 +2792,6 @@ struct ieee80211_ops { unsigned int changed_flags, unsigned int *total_flags, u64 multicast); - void (*set_multicast_list)(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, bool allmulti, - struct netdev_hw_addr_list *mc_list); - int (*set_tim)(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set); int (*set_key)(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -2821,6 +2845,9 @@ struct ieee80211_ops { struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, enum ieee80211_sta_state new_state); + void (*sta_pre_rcu_remove)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); void (*sta_rc_update)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 129b7b087148..e1307909ecf1 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -693,6 +693,15 @@ * other station that transmission must be blocked until the channel * switch is complete. * + * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified + * by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in + * %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in + * %NL80211_ATTR_VENDOR_DATA. + * For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is + * used in the wiphy data as a nested attribute containing descriptions + * (&struct nl80211_vendor_cmd_info) of the supported vendor commands. + * This may also be sent as an event with the same attributes. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -860,6 +869,8 @@ enum nl80211_commands { NL80211_CMD_CHANNEL_SWITCH, + NL80211_CMD_VENDOR, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -1520,6 +1531,16 @@ enum nl80211_commands { * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports * 10 MHz channel bandwidth. * + * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode + * Notification Element based on association request when used with + * %NL80211_CMD_NEW_STATION; u8 attribute. + * + * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if + * %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet) + * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command + * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this + * attribute is also used for vendor command feature advertisement + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -1839,6 +1860,12 @@ enum nl80211_attrs { NL80211_ATTR_SUPPORT_5_MHZ, NL80211_ATTR_SUPPORT_10_MHZ, + NL80211_ATTR_OPMODE_NOTIF, + + NL80211_ATTR_VENDOR_ID, + NL80211_ATTR_VENDOR_SUBCMD, + NL80211_ATTR_VENDOR_DATA, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -3083,21 +3110,35 @@ enum nl80211_key_attributes { * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with * 1 = 500 kbps) but without the IE length restriction (at most * %NL80211_MAX_SUPP_RATES in a single array). - * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection + * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection * in an array of MCS numbers. + * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection, + * see &struct nl80211_txrate_vht * @__NL80211_TXRATE_AFTER_LAST: internal * @NL80211_TXRATE_MAX: highest TX rate attribute */ enum nl80211_tx_rate_attributes { __NL80211_TXRATE_INVALID, NL80211_TXRATE_LEGACY, - NL80211_TXRATE_MCS, + NL80211_TXRATE_HT, + NL80211_TXRATE_VHT, /* keep last */ __NL80211_TXRATE_AFTER_LAST, NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 }; +#define NL80211_TXRATE_MCS NL80211_TXRATE_HT +#define NL80211_VHT_NSS_MAX 8 + +/** + * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap + * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.) + */ +struct nl80211_txrate_vht { + __u16 mcs[NL80211_VHT_NSS_MAX]; +}; + /** * enum nl80211_band - Frequency band * @NL80211_BAND_2GHZ: 2.4 GHz ISM band @@ -3959,4 +4000,24 @@ enum nl80211_rxmgmt_flags { NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0, }; +/* + * If this flag is unset, the lower 24 bits are an OUI, if set + * a Linux nl80211 vendor ID is used (no such IDs are allocated + * yet, so that's not valid so far) + */ +#define NL80211_VENDOR_ID_IS_LINUX 0x80000000 + +/** + * struct nl80211_vendor_cmd_info - vendor command data + * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the + * value is a 24-bit OUI; if it is set then a separately allocated ID + * may be used, but no such IDs are allocated yet. New IDs should be + * added to this file when needed. + * @subcmd: sub-command ID for the command + */ +struct nl80211_vendor_cmd_info { + __u32 vendor_id; + __u32 subcmd; +}; + #endif /* __LINUX_NL80211_H */ diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index f80e8c4c6bcd..ac185286842d 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -301,9 +301,10 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, if (!sta) goto out; - if (pairwise) + if (pairwise && key_idx < NUM_DEFAULT_KEYS) key = rcu_dereference(sta->ptk[key_idx]); - else if (key_idx < NUM_DEFAULT_KEYS) + else if (!pairwise && + key_idx < NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS) key = rcu_dereference(sta->gtk[key_idx]); } else key = rcu_dereference(sdata->keys[key_idx]); @@ -873,8 +874,8 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata, return 0; } -int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, - struct cfg80211_beacon_data *params) +static int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, + struct cfg80211_beacon_data *params) { struct beacon_data *new, *old; int new_head_len, new_tail_len; @@ -1097,17 +1098,8 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) if (old_probe_resp) kfree_rcu(old_probe_resp, rcu_head); - list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) - sta_info_flush_defer(vlan); - sta_info_flush_defer(sdata); - synchronize_net(); - rcu_barrier(); - list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) { - sta_info_flush_cleanup(vlan); - ieee80211_free_keys(vlan); - } - sta_info_flush_cleanup(sdata); - ieee80211_free_keys(sdata); + __sta_info_flush(sdata, true); + ieee80211_free_keys(sdata, true); sdata->vif.bss_conf.enable_beacon = false; sdata->vif.bss_conf.ssid_len = 0; @@ -2587,8 +2579,8 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, int j; sdata->rc_rateidx_mask[i] = mask->control[i].legacy; - memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].mcs, - sizeof(mask->control[i].mcs)); + memcpy(sdata->rc_rateidx_mcs_mask[i], mask->control[i].ht_mcs, + sizeof(mask->control[i].ht_mcs)); sdata->rc_has_mcs_mask[i] = false; if (!sband) @@ -3047,8 +3039,8 @@ unlock: sdata_unlock(sdata); } -static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_csa_settings *params) +int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = sdata->local; diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 5d03c47c0a4c..ef8b385eff04 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -242,22 +242,6 @@ static inline u64 drv_prepare_multicast(struct ieee80211_local *local, return ret; } -static inline void drv_set_multicast_list(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - struct netdev_hw_addr_list *mc_list) -{ - bool allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI; - - trace_drv_set_multicast_list(local, sdata, mc_list->count); - - check_sdata_in_driver(sdata); - - if (local->ops->set_multicast_list) - local->ops->set_multicast_list(&local->hw, &sdata->vif, - allmulti, mc_list); - trace_drv_return_void(local); -} - static inline void drv_configure_filter(struct ieee80211_local *local, unsigned int changed_flags, unsigned int *total_flags, @@ -550,6 +534,22 @@ static inline void drv_sta_remove_debugfs(struct ieee80211_local *local, } #endif +static inline void drv_sta_pre_rcu_remove(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct sta_info *sta) +{ + might_sleep(); + + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + + trace_drv_sta_pre_rcu_remove(local, sdata, &sta->sta); + if (local->ops->sta_pre_rcu_remove) + local->ops->sta_pre_rcu_remove(&local->hw, &sdata->vif, + &sta->sta); + trace_drv_return_void(local); +} + static inline __must_check int drv_sta_state(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 2eda7b13124a..d6ba841437b6 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -522,7 +522,7 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, if (csa_settings) ieee80211_send_action_csa(sdata, csa_settings); - ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON); + return BSS_CHANGED_BEACON; out: return ret; } @@ -534,7 +534,8 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) int err; u16 capability; - sdata_lock(sdata); + sdata_assert_lock(sdata); + /* update cfg80211 bss information with the new channel */ if (!is_zero_ether_addr(ifibss->bssid)) { capability = WLAN_CAPABILITY_IBSS; @@ -559,10 +560,12 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) /* generate the beacon */ err = ieee80211_ibss_csa_beacon(sdata, NULL); - sdata_unlock(sdata); if (err < 0) return err; + if (err) + ieee80211_bss_info_change_notify(sdata, err); + return 0; } @@ -753,12 +756,16 @@ static void ieee80211_csa_connection_drop_work(struct work_struct *work) container_of(work, struct ieee80211_sub_if_data, u.ibss.csa_connection_drop_work); + sdata_lock(sdata); + ieee80211_ibss_disconnect(sdata); synchronize_rcu(); skb_queue_purge(&sdata->skb_queue); /* trigger a scan to find another IBSS network to join */ ieee80211_queue_work(&sdata->local->hw, &sdata->work); + + sdata_unlock(sdata); } static void ieee80211_ibss_csa_mark_radar(struct ieee80211_sub_if_data *sdata) @@ -784,18 +791,10 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, struct cfg80211_csa_settings params; struct ieee80211_csa_ie csa_ie; struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; - struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_chanctx *chanctx; enum nl80211_channel_type ch_type; - int err, num_chanctx; + int err; u32 sta_flags; - if (sdata->vif.csa_active) - return true; - - if (!sdata->vif.bss_conf.ibss_joined) - return false; - sta_flags = IEEE80211_STA_DISABLE_VHT; switch (ifibss->chandef.width) { case NL80211_CHAN_WIDTH_5: @@ -830,9 +829,6 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, params.count = csa_ie.count; params.chandef = csa_ie.chandef; - if (ifibss->chandef.chan->band != params.chandef.chan->band) - goto disconnect; - switch (ifibss->chandef.width) { case NL80211_CHAN_WIDTH_20_NOHT: case NL80211_CHAN_WIDTH_20: @@ -888,28 +884,12 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, params.radar_required = true; } - rcu_read_lock(); - chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (!chanctx_conf) { - rcu_read_unlock(); - goto disconnect; - } - - /* don't handle for multi-VIF cases */ - chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); - if (chanctx->refcount > 1) { - rcu_read_unlock(); - goto disconnect; - } - num_chanctx = 0; - list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list) - num_chanctx++; - - if (num_chanctx > 1) { - rcu_read_unlock(); - goto disconnect; + if (cfg80211_chandef_identical(¶ms.chandef, + &sdata->vif.bss_conf.chandef)) { + ibss_dbg(sdata, + "received csa with an identical chandef, ignoring\n"); + return true; } - rcu_read_unlock(); /* all checks done, now perform the channel switch. */ ibss_dbg(sdata, @@ -918,19 +898,9 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata, params.block_tx = !!csa_ie.mode; - ieee80211_ibss_csa_beacon(sdata, ¶ms); - sdata->csa_radar_required = params.radar_required; - - if (params.block_tx) - ieee80211_stop_queues_by_reason(&sdata->local->hw, - IEEE80211_MAX_QUEUE_MAP, - IEEE80211_QUEUE_STOP_REASON_CSA); - - sdata->csa_chandef = params.chandef; - sdata->vif.csa_active = true; - - ieee80211_bss_info_change_notify(sdata, err); - drv_channel_switch_beacon(sdata, ¶ms.chandef); + if (ieee80211_channel_switch(sdata->local->hw.wiphy, sdata->dev, + ¶ms)) + goto disconnect; ieee80211_ibss_csa_mark_radar(sdata); @@ -966,7 +936,8 @@ ieee80211_rx_mgmt_spectrum_mgmt(struct ieee80211_sub_if_data *sdata, if (len < required_len) return; - ieee80211_ibss_process_chanswitch(sdata, elems, false); + if (!sdata->vif.csa_active) + ieee80211_ibss_process_chanswitch(sdata, elems, false); } static void ieee80211_rx_mgmt_deauth_ibss(struct ieee80211_sub_if_data *sdata, @@ -1147,7 +1118,8 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, goto put_bss; /* process channel switch */ - if (ieee80211_ibss_process_chanswitch(sdata, elems, true)) + if (sdata->vif.csa_active || + ieee80211_ibss_process_chanswitch(sdata, elems, true)) goto put_bss; /* same BSSID */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ed5bf8b4b5c2..fb5dbcb79a12 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -232,6 +232,7 @@ struct ieee80211_rx_data { struct beacon_data { u8 *head, *tail; int head_len, tail_len; + struct ieee80211_meshconf_ie *meshconf; struct rcu_head rcu_head; }; @@ -540,7 +541,10 @@ struct ieee80211_mesh_sync_ops { struct ieee80211_mgmt *mgmt, struct ieee802_11_elems *elems, struct ieee80211_rx_status *rx_status); - void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata); + + /* should be called with beacon_data under RCU read lock */ + void (*adjust_tbtt)(struct ieee80211_sub_if_data *sdata, + struct beacon_data *beacon); /* add other framework functions here */ }; @@ -614,6 +618,9 @@ struct ieee80211_if_mesh { bool chsw_init; u8 chsw_ttl; u16 pre_value; + + /* offset from skb->data while building IE */ + int meshconf_offset; }; #ifdef CONFIG_MAC80211_MESH @@ -776,10 +783,6 @@ struct ieee80211_sub_if_data { u32 mntr_flags; } u; - spinlock_t cleanup_stations_lock; - struct list_head cleanup_stations; - struct work_struct cleanup_stations_wk; - #ifdef CONFIG_MAC80211_DEBUGFS struct { struct dentry *subdir_stations; @@ -1117,6 +1120,7 @@ struct ieee80211_local { struct work_struct sched_scan_stopped_work; struct ieee80211_sub_if_data __rcu *sched_scan_sdata; + struct cfg80211_sched_scan_request *sched_scan_req; unsigned long leave_oper_channel_time; enum mac80211_scan_state next_scan_state; @@ -1425,6 +1429,9 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local, struct ieee80211_bss *bss); /* scheduled scan handling */ +int +__ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, + struct cfg80211_sched_scan_request *req); int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, struct cfg80211_sched_scan_request *req); int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata); @@ -1443,6 +1450,8 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc); /* channel switch handling */ void ieee80211_csa_finalize_work(struct work_struct *work); +int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params); /* interface handling */ int ieee80211_iface_init(void); @@ -1465,8 +1474,6 @@ void ieee80211_del_virtual_monitor(struct ieee80211_local *local); bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); -int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata, - struct cfg80211_beacon_data *params); static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) { diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index a4f98123d0bf..d624ed49a7d9 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -786,10 +786,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, * This is relevant only in WDS mode, in all other modes we've * already removed all stations when disconnecting or similar, * so warn otherwise. - * - * We call sta_info_flush_cleanup() later, to combine RCU waits. */ - flushed = sta_info_flush_defer(sdata); + flushed = sta_info_flush(sdata); WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) || (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)); @@ -891,23 +889,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->work); /* * When we get here, the interface is marked down. + * Free the remaining keys, if there are any + * (shouldn't be, except maybe in WDS mode?) * - * sta_info_flush_cleanup() requires rcu_barrier() - * first to wait for the station call_rcu() calls - * to complete, and we also need synchronize_rcu() - * to wait for the RX path in case it is using the - * interface and enqueuing frames at this very time on + * Force the key freeing to always synchronize_net() + * to wait for the RX path in case it is using this + * interface enqueuing frames * at this very time on * another CPU. */ - synchronize_rcu(); - rcu_barrier(); - sta_info_flush_cleanup(sdata); - - /* - * Free all remaining keys, there shouldn't be any, - * except maybe in WDS mode? - */ - ieee80211_free_keys(sdata); + ieee80211_free_keys(sdata, true); /* fall through */ case NL80211_IFTYPE_AP: @@ -1018,17 +1008,6 @@ static void ieee80211_set_multicast_list(struct net_device *dev) atomic_dec(&local->iff_promiscs); sdata->flags ^= IEEE80211_SDATA_PROMISC; } - - /* - * TODO: If somebody needs this on AP interfaces, - * it can be enabled easily but multicast - * addresses from VLANs need to be synced. - */ - if (sdata->vif.type != NL80211_IFTYPE_MONITOR && - sdata->vif.type != NL80211_IFTYPE_AP_VLAN && - sdata->vif.type != NL80211_IFTYPE_AP) - drv_set_multicast_list(local, sdata, &dev->mc); - spin_lock_bh(&local->filter_lock); __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len); spin_unlock_bh(&local->filter_lock); @@ -1044,7 +1023,7 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata) int i; /* free extra data */ - ieee80211_free_keys(sdata); + ieee80211_free_keys(sdata, false); ieee80211_debugfs_remove_netdev(sdata); @@ -1578,15 +1557,6 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, mutex_unlock(&local->iflist_mtx); } -static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk) -{ - struct ieee80211_sub_if_data *sdata; - - sdata = container_of(wk, struct ieee80211_sub_if_data, cleanup_stations_wk); - - ieee80211_cleanup_sdata_stas(sdata); -} - int ieee80211_if_add(struct ieee80211_local *local, const char *name, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params) @@ -1659,9 +1629,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, INIT_LIST_HEAD(&sdata->key_list); - spin_lock_init(&sdata->cleanup_stations_lock); - INIT_LIST_HEAD(&sdata->cleanup_stations); - INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk); INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work, ieee80211_dfs_cac_timer_work); INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk, diff --git a/net/mac80211/key.c b/net/mac80211/key.c index e568d98167d0..6ff65a1ebaa9 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -589,14 +589,10 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_iter_keys); -void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) +static void ieee80211_free_keys_iface(struct ieee80211_sub_if_data *sdata, + struct list_head *keys) { struct ieee80211_key *key, *tmp; - LIST_HEAD(keys); - - cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk); - - mutex_lock(&sdata->local->key_mtx); sdata->crypto_tx_tailroom_needed_cnt -= sdata->crypto_tx_tailroom_pending_dec; @@ -608,28 +604,51 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) ieee80211_key_replace(key->sdata, key->sta, key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, key, NULL); - list_add_tail(&key->list, &keys); + list_add_tail(&key->list, keys); } ieee80211_debugfs_key_update_default(sdata); +} - if (!list_empty(&keys)) { - synchronize_net(); - list_for_each_entry_safe(key, tmp, &keys, list) - __ieee80211_key_destroy(key, false); +void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata, + bool force_synchronize) +{ + struct ieee80211_local *local = sdata->local; + struct ieee80211_sub_if_data *vlan; + struct ieee80211_key *key, *tmp; + LIST_HEAD(keys); + + cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk); + + mutex_lock(&local->key_mtx); + + ieee80211_free_keys_iface(sdata, &keys); + + if (sdata->vif.type == NL80211_IFTYPE_AP) { + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + ieee80211_free_keys_iface(vlan, &keys); } + if (!list_empty(&keys) || force_synchronize) + synchronize_net(); + list_for_each_entry_safe(key, tmp, &keys, list) + __ieee80211_key_destroy(key, false); + WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt || sdata->crypto_tx_tailroom_pending_dec); + if (sdata->vif.type == NL80211_IFTYPE_AP) { + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + WARN_ON_ONCE(vlan->crypto_tx_tailroom_needed_cnt || + vlan->crypto_tx_tailroom_pending_dec); + } - mutex_unlock(&sdata->local->key_mtx); + mutex_unlock(&local->key_mtx); } void ieee80211_free_sta_keys(struct ieee80211_local *local, struct sta_info *sta) { - struct ieee80211_key *key, *tmp; - LIST_HEAD(keys); + struct ieee80211_key *key; int i; mutex_lock(&local->key_mtx); @@ -640,7 +659,7 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local, ieee80211_key_replace(key->sdata, key->sta, key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, key, NULL); - list_add(&key->list, &keys); + __ieee80211_key_destroy(key, true); } for (i = 0; i < NUM_DEFAULT_KEYS; i++) { @@ -650,17 +669,8 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local, ieee80211_key_replace(key->sdata, key->sta, key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE, key, NULL); - list_add(&key->list, &keys); - } - - /* - * NB: the station code relies on this being - * done even if there aren't any keys - */ - synchronize_net(); - - list_for_each_entry_safe(key, tmp, &keys, list) __ieee80211_key_destroy(key, true); + } mutex_unlock(&local->key_mtx); } diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 0aebb889caba..19db68663d75 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -136,7 +136,8 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx, bool uni, bool multi); void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata, int idx); -void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata); +void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata, + bool force_synchronize); void ieee80211_free_sta_keys(struct ieee80211_local *local, struct sta_info *sta); void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index fa34cd2344b9..2bd5b552b2f6 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -250,12 +250,8 @@ static void ieee80211_restart_work(struct work_struct *work) /* wait for scan work complete */ flush_workqueue(local->workqueue); - mutex_lock(&local->mtx); - WARN(test_bit(SCAN_HW_SCANNING, &local->scanning) || - rcu_dereference_protected(local->sched_scan_sdata, - lockdep_is_held(&local->mtx)), - "%s called with hardware scan in progress\n", __func__); - mutex_unlock(&local->mtx); + WARN(test_bit(SCAN_HW_SCANNING, &local->scanning), + "%s called with hardware scan in progress\n", __func__); rtnl_lock(); ieee80211_scan_cancel(local); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 89df62b2b689..5a74b249ba35 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -259,6 +259,9 @@ int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata, *pos++ = WLAN_EID_MESH_CONFIG; *pos++ = meshconf_len; + /* save a pointer for quick updates in pre-tbtt */ + ifmsh->meshconf_offset = pos - skb->data; + /* Active path selection protocol ID */ *pos++ = ifmsh->mesh_pp_id; /* Active path selection metric ID */ @@ -723,6 +726,8 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh) bcn->tail_len = skb->len; memcpy(bcn->tail, skb->data, bcn->tail_len); + bcn->meshconf = (struct ieee80211_meshconf_ie *) + (bcn->tail + ifmsh->meshconf_offset); dev_kfree_skb(skb); rcu_assign_pointer(ifmsh->beacon, bcn); diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index d1cf2d553499..2bc5dc25d5ad 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -164,12 +164,15 @@ no_sync: rcu_read_unlock(); } -static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) +static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata, + struct beacon_data *beacon) { struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; + u8 cap; WARN_ON(ifmsh->mesh_sp_id != IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET); BUG_ON(!rcu_read_lock_held()); + cap = beacon->meshconf->meshconf_cap; spin_lock_bh(&ifmsh->sync_offset_lock); @@ -194,6 +197,10 @@ static void mesh_sync_offset_adjust_tbtt(struct ieee80211_sub_if_data *sdata) ifmsh->adjusting_tbtt = false; } spin_unlock_bh(&ifmsh->sync_offset_lock); + + beacon->meshconf->meshconf_cap = ifmsh->adjusting_tbtt ? + IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING | cap : + ~IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING & cap; } static const struct sync_method sync_methods[] = { diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 900ead344f5b..9c2c7ee2cc30 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1698,7 +1698,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, memset(ifmgd->bssid, 0, ETH_ALEN); /* remove AP and TDLS peers */ - sta_info_flush_defer(sdata); + sta_info_flush(sdata); /* finally reset all BSS / config parameters */ changed |= ieee80211_reset_erp_info(sdata); diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 340126204343..af64fb8e8add 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -37,9 +37,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) IEEE80211_MAX_QUEUE_MAP, IEEE80211_QUEUE_STOP_REASON_SUSPEND); - /* flush out all packets and station cleanup call_rcu()s */ + /* flush out all packets */ synchronize_net(); - rcu_barrier(); ieee80211_flush_queues(local, NULL); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 2dfa75522733..5a2afe9583a8 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1963,20 +1963,17 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) } } - if (skb) { - int align __maybe_unused; - #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS - /* - * 'align' will only take the values 0 or 2 here - * since all frames are required to be aligned - * to 2-byte boundaries when being passed to - * mac80211; the code here works just as well if - * that isn't true, but mac80211 assumes it can - * access fields as 2-byte aligned (e.g. for - * compare_ether_addr) + if (skb) { + /* 'align' will only take the values 0 or 2 here since all + * frames are required to be aligned to 2-byte boundaries + * when being passed to mac80211; the code here works just + * as well if that isn't true, but mac80211 assumes it can + * access fields as 2-byte aligned (e.g. for ether_addr_equal) */ - align = ((unsigned long)(skb->data + sizeof(struct ethhdr))) & 3; + int align; + + align = (unsigned long)(skb->data + sizeof(struct ethhdr)) & 3; if (align) { if (WARN_ON(skb_headroom(skb) < 3)) { dev_kfree_skb(skb); @@ -1989,14 +1986,14 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) skb_set_tail_pointer(skb, len); } } + } #endif - if (skb) { - /* deliver to local stack */ - skb->protocol = eth_type_trans(skb, dev); - memset(skb->cb, 0, sizeof(skb->cb)); - netif_receive_skb(skb); - } + if (skb) { + /* deliver to local stack */ + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_receive_skb(skb); } if (xmit_skb) { diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 4d73c46df862..88c81616f8f7 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -271,10 +271,11 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) return true; } -static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, - bool was_hw_scan) +static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) { struct ieee80211_local *local = hw_to_local(hw); + bool hw_scan = local->ops->hw_scan; + bool was_scanning = local->scanning; lockdep_assert_held(&local->mtx); @@ -290,7 +291,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, if (WARN_ON(!local->scan_req)) return; - if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { + if (hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { int rc; rc = drv_hw_scan(local, @@ -316,7 +317,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, /* Set power back to normal operating levels. */ ieee80211_hw_config(local, 0); - if (!was_hw_scan) { + if (!hw_scan) { ieee80211_configure_filter(local); drv_sw_scan_complete(local); ieee80211_offchannel_return(local); @@ -327,7 +328,8 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, ieee80211_mlme_notify_scan_completed(local); ieee80211_ibss_notify_scan_completed(local); ieee80211_mesh_notify_scan_completed(local); - ieee80211_start_next_roc(local); + if (was_scanning) + ieee80211_start_next_roc(local); } void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) @@ -747,7 +749,7 @@ void ieee80211_scan_work(struct work_struct *work) container_of(work, struct ieee80211_local, scan_work.work); struct ieee80211_sub_if_data *sdata; unsigned long next_delay = 0; - bool aborted, hw_scan; + bool aborted; mutex_lock(&local->mtx); @@ -786,14 +788,6 @@ void ieee80211_scan_work(struct work_struct *work) } /* - * Avoid re-scheduling when the sdata is going away. - */ - if (!ieee80211_sdata_running(sdata)) { - aborted = true; - goto out_complete; - } - - /* * as long as no delay is required advance immediately * without scheduling a new work */ @@ -834,8 +828,7 @@ void ieee80211_scan_work(struct work_struct *work) goto out; out_complete: - hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); - __ieee80211_scan_completed(&local->hw, aborted, hw_scan); + __ieee80211_scan_completed(&local->hw, aborted); out: mutex_unlock(&local->mtx); } @@ -973,13 +966,13 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) */ cancel_delayed_work(&local->scan_work); /* and clean up */ - __ieee80211_scan_completed(&local->hw, true, false); + __ieee80211_scan_completed(&local->hw, true); out: mutex_unlock(&local->mtx); } -int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, - struct cfg80211_sched_scan_request *req) +int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, + struct cfg80211_sched_scan_request *req) { struct ieee80211_local *local = sdata->local; struct ieee80211_sched_scan_ies sched_scan_ies = {}; @@ -989,17 +982,10 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, iebufsz = 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len + req->ie_len; - mutex_lock(&local->mtx); - - if (rcu_access_pointer(local->sched_scan_sdata)) { - ret = -EBUSY; - goto out; - } + lockdep_assert_held(&local->mtx); - if (!local->ops->sched_scan_start) { - ret = -ENOTSUPP; - goto out; - } + if (!local->ops->sched_scan_start) + return -ENOTSUPP; for (i = 0; i < IEEE80211_NUM_BANDS; i++) { if (!local->hw.wiphy->bands[i]) @@ -1020,13 +1006,39 @@ int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, } ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies); - if (ret == 0) + if (ret == 0) { rcu_assign_pointer(local->sched_scan_sdata, sdata); + local->sched_scan_req = req; + } out_free: while (i > 0) kfree(sched_scan_ies.ie[--i]); -out: + + if (ret) { + /* Clean in case of failure after HW restart or upon resume. */ + rcu_assign_pointer(local->sched_scan_sdata, NULL); + local->sched_scan_req = NULL; + } + + return ret; +} + +int ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata, + struct cfg80211_sched_scan_request *req) +{ + struct ieee80211_local *local = sdata->local; + int ret; + + mutex_lock(&local->mtx); + + if (rcu_access_pointer(local->sched_scan_sdata)) { + mutex_unlock(&local->mtx); + return -EBUSY; + } + + ret = __ieee80211_request_sched_scan_start(sdata, req); + mutex_unlock(&local->mtx); return ret; } @@ -1043,6 +1055,9 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata) goto out; } + /* We don't want to restart sched scan anymore. */ + local->sched_scan_req = NULL; + if (rcu_access_pointer(local->sched_scan_sdata)) drv_sched_scan_stop(local, sdata); @@ -1077,6 +1092,9 @@ void ieee80211_sched_scan_stopped_work(struct work_struct *work) rcu_assign_pointer(local->sched_scan_sdata, NULL); + /* If sched scan was aborted by the driver. */ + local->sched_scan_req = NULL; + mutex_unlock(&local->mtx); cfg80211_sched_scan_stopped(local->hw.wiphy); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 8ed97f76c3cf..4576ba0ff221 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -99,23 +99,6 @@ static void cleanup_single_sta(struct sta_info *sta) struct ieee80211_local *local = sdata->local; struct ps_data *ps; - /* - * At this point, when being called as call_rcu callback, - * neither mac80211 nor the driver can reference this - * sta struct any more except by still existing timers - * associated with this station that we clean up below. - * - * Note though that this still uses the sdata and even - * calls the driver in AP and mesh mode, so interfaces - * of those types mush use call sta_info_flush_cleanup() - * (typically via sta_info_flush()) before deconfiguring - * the driver. - * - * In station mode, nothing happens here so it doesn't - * have to (and doesn't) do that, this is intentional to - * speed up roaming. - */ - if (test_sta_flag(sta, WLAN_STA_PS_STA)) { if (sta->sdata->vif.type == NL80211_IFTYPE_AP || sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -160,37 +143,6 @@ static void cleanup_single_sta(struct sta_info *sta) sta_info_free(local, sta); } -void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata) -{ - struct sta_info *sta; - - spin_lock_bh(&sdata->cleanup_stations_lock); - while (!list_empty(&sdata->cleanup_stations)) { - sta = list_first_entry(&sdata->cleanup_stations, - struct sta_info, list); - list_del(&sta->list); - spin_unlock_bh(&sdata->cleanup_stations_lock); - - cleanup_single_sta(sta); - - spin_lock_bh(&sdata->cleanup_stations_lock); - } - - spin_unlock_bh(&sdata->cleanup_stations_lock); -} - -static void free_sta_rcu(struct rcu_head *h) -{ - struct sta_info *sta = container_of(h, struct sta_info, rcu_head); - struct ieee80211_sub_if_data *sdata = sta->sdata; - - spin_lock(&sdata->cleanup_stations_lock); - list_add_tail(&sta->list, &sdata->cleanup_stations); - spin_unlock(&sdata->cleanup_stations_lock); - - ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk); -} - /* protected by RCU */ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, const u8 *addr) @@ -842,7 +794,7 @@ static bool sta_info_cleanup_expire_buffered(struct ieee80211_local *local, return have_buffered; } -int __must_check __sta_info_destroy(struct sta_info *sta) +static int __must_check __sta_info_destroy_part1(struct sta_info *sta) { struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; @@ -868,12 +820,35 @@ int __must_check __sta_info_destroy(struct sta_info *sta) ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA); ret = sta_info_hash_del(local, sta); - if (ret) + if (WARN_ON(ret)) return ret; list_del_rcu(&sta->list); - /* this always calls synchronize_net() */ + drv_sta_pre_rcu_remove(local, sta->sdata, sta); + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + rcu_access_pointer(sdata->u.vlan.sta) == sta) + RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); + + return 0; +} + +static void __sta_info_destroy_part2(struct sta_info *sta) +{ + struct ieee80211_local *local = sta->local; + struct ieee80211_sub_if_data *sdata = sta->sdata; + int ret; + + /* + * NOTE: This assumes at least synchronize_net() was done + * after _part1 and before _part2! + */ + + might_sleep(); + lockdep_assert_held(&local->sta_mtx); + + /* now keys can no longer be reached */ ieee80211_free_sta_keys(local, sta); sta->dead = true; @@ -881,9 +856,6 @@ int __must_check __sta_info_destroy(struct sta_info *sta) local->num_sta--; local->sta_generation++; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); - while (sta->sta_state > IEEE80211_STA_NONE) { ret = sta_info_move_state(sta, sta->sta_state - 1); if (ret) { @@ -906,7 +878,19 @@ int __must_check __sta_info_destroy(struct sta_info *sta) ieee80211_sta_debugfs_remove(sta); ieee80211_recalc_min_chandef(sdata); - call_rcu(&sta->rcu_head, free_sta_rcu); + cleanup_single_sta(sta); +} + +int __must_check __sta_info_destroy(struct sta_info *sta) +{ + int err = __sta_info_destroy_part1(sta); + + if (err) + return err; + + synchronize_net(); + + __sta_info_destroy_part2(sta); return 0; } @@ -976,32 +960,38 @@ void sta_info_stop(struct ieee80211_local *local) } -int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) +int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans) { struct ieee80211_local *local = sdata->local; struct sta_info *sta, *tmp; + LIST_HEAD(free_list); int ret = 0; might_sleep(); + WARN_ON(vlans && sdata->vif.type != NL80211_IFTYPE_AP); + WARN_ON(vlans && !sdata->bss); + mutex_lock(&local->sta_mtx); list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { - if (sdata == sta->sdata) { - WARN_ON(__sta_info_destroy(sta)); + if (sdata == sta->sdata || + (vlans && sdata->bss == sta->sdata->bss)) { + if (!WARN_ON(__sta_info_destroy_part1(sta))) + list_add(&sta->free_list, &free_list); ret++; } } + + if (!list_empty(&free_list)) { + synchronize_net(); + list_for_each_entry_safe(sta, tmp, &free_list, free_list) + __sta_info_destroy_part2(sta); + } mutex_unlock(&local->sta_mtx); return ret; } -void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata) -{ - ieee80211_cleanup_sdata_stas(sdata); - cancel_work_sync(&sdata->cleanup_stations_wk); -} - void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, unsigned long exp_time) { diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 0218caf5c14a..d77ff7090630 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -247,6 +247,7 @@ struct ieee80211_tx_latency_stat { * mac80211 is communicating with. * * @list: global linked list entry + * @free_list: list entry for keeping track of stations to free * @hnext: hash table linked list pointer * @local: pointer to the global information * @sdata: virtual interface this station belongs to @@ -329,7 +330,7 @@ struct ieee80211_tx_latency_stat { */ struct sta_info { /* General information, mostly static */ - struct list_head list; + struct list_head list, free_list; struct rcu_head rcu_head; struct sta_info __rcu *hnext; struct ieee80211_local *local; @@ -605,21 +606,6 @@ void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); -int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata); - -/** - * sta_info_flush_cleanup - flush the sta_info cleanup queue - * @sdata: the interface - * - * Flushes the sta_info cleanup queue for a given interface; - * this is necessary before the interface is removed or, for - * AP/mesh interfaces, before it is deconfigured. - * - * Note an rcu_barrier() must precede the function, after all - * stations have been flushed/removed to ensure the call_rcu() - * calls that add stations to the cleanup queue have completed. - */ -void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata); /** * sta_info_flush - flush matching STA entries from the STA table @@ -627,15 +613,13 @@ void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata); * Returns the number of removed STA entries. * * @sdata: sdata to remove all stations from + * @vlans: if the given interface is an AP interface, also flush VLANs */ +int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans); + static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata) { - int ret = sta_info_flush_defer(sdata); - - rcu_barrier(); - sta_info_flush_cleanup(sdata); - - return ret; + return __sta_info_flush(sdata, false); } void sta_set_rate_info_tx(struct sta_info *sta, @@ -651,6 +635,4 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); -void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata); - #endif /* STA_INFO_H */ diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index e9ccf22f6dd9..3a669d7ec7ad 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -443,30 +443,6 @@ TRACE_EVENT(drv_prepare_multicast, ) ); -TRACE_EVENT(drv_set_multicast_list, - TP_PROTO(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, int mc_count), - - TP_ARGS(local, sdata, mc_count), - - TP_STRUCT__entry( - LOCAL_ENTRY - __field(bool, allmulti) - __field(int, mc_count) - ), - - TP_fast_assign( - LOCAL_ASSIGN; - __entry->allmulti = sdata->flags & IEEE80211_SDATA_ALLMULTI; - __entry->mc_count = mc_count; - ), - - TP_printk( - LOCAL_PR_FMT " configure mc filter, count=%d, allmulti=%d", - LOCAL_PR_ARG, __entry->mc_count, __entry->allmulti - ) -); - TRACE_EVENT(drv_configure_filter, TP_PROTO(struct ieee80211_local *local, unsigned int changed_flags, @@ -790,7 +766,7 @@ TRACE_EVENT(drv_sta_rc_update, ) ); -TRACE_EVENT(drv_sta_add, +DECLARE_EVENT_CLASS(sta_event, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta), @@ -815,29 +791,25 @@ TRACE_EVENT(drv_sta_add, ) ); -TRACE_EVENT(drv_sta_remove, +DEFINE_EVENT(sta_event, drv_sta_add, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *sta), + TP_ARGS(local, sdata, sta) +); - TP_ARGS(local, sdata, sta), - - TP_STRUCT__entry( - LOCAL_ENTRY - VIF_ENTRY - STA_ENTRY - ), - - TP_fast_assign( - LOCAL_ASSIGN; - VIF_ASSIGN; - STA_ASSIGN; - ), +DEFINE_EVENT(sta_event, drv_sta_remove, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta), + TP_ARGS(local, sdata, sta) +); - TP_printk( - LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT, - LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG - ) +DEFINE_EVENT(sta_event, drv_sta_pre_rcu_remove, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta), + TP_ARGS(local, sdata, sta) ); TRACE_EVENT(drv_conf_tx, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 6d59e21cdb9f..2f0e176e7989 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2549,7 +2549,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, */ skb = dev_alloc_skb(local->tx_headroom + beacon->head_len + - beacon->tail_len + 256); + beacon->tail_len + 256 + + local->hw.extra_beacon_tailroom); if (!skb) goto out; @@ -2581,7 +2582,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, ieee80211_update_csa(sdata, presp); - skb = dev_alloc_skb(local->tx_headroom + presp->head_len); + skb = dev_alloc_skb(local->tx_headroom + presp->head_len + + local->hw.extra_beacon_tailroom); if (!skb) goto out; skb_reserve(skb, local->tx_headroom); @@ -2602,13 +2604,13 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, ieee80211_update_csa(sdata, bcn); if (ifmsh->sync_ops) - ifmsh->sync_ops->adjust_tbtt( - sdata); + ifmsh->sync_ops->adjust_tbtt(sdata, bcn); skb = dev_alloc_skb(local->tx_headroom + bcn->head_len + 256 + /* TIM IE */ - bcn->tail_len); + bcn->tail_len + + local->hw.extra_beacon_tailroom); if (!skb) goto out; skb_reserve(skb, local->tx_headroom); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 875e172c001c..591b46b72462 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -642,6 +642,17 @@ void ieee80211_iterate_active_interfaces_rtnl( } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); +struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + + if (!ieee80211_sdata_running(sdata) || + !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + return NULL; + return &sdata->vif; +} +EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); + /* * Nothing should have been stuffed into the workqueue during * the suspend->resume cycle. If this WARN is seen then there @@ -1451,6 +1462,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) struct sta_info *sta; int res, i; bool reconfig_due_to_wowlan = false; + struct ieee80211_sub_if_data *sched_scan_sdata; + bool sched_scan_stopped = false; #ifdef CONFIG_PM if (local->suspended) @@ -1754,6 +1767,27 @@ int ieee80211_reconfig(struct ieee80211_local *local) #else WARN_ON(1); #endif + + /* + * Reconfigure sched scan if it was interrupted by FW restart or + * suspend. + */ + mutex_lock(&local->mtx); + sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata, + lockdep_is_held(&local->mtx)); + if (sched_scan_sdata && local->sched_scan_req) + /* + * Sched scan stopped, but we don't want to report it. Instead, + * we're trying to reschedule. + */ + if (__ieee80211_request_sched_scan_start(sched_scan_sdata, + local->sched_scan_req)) + sched_scan_stopped = true; + mutex_unlock(&local->mtx); + + if (sched_scan_stopped) + cfg80211_sched_scan_stopped(local->hw.wiphy); + return 0; } diff --git a/net/wireless/core.c b/net/wireless/core.c index 06db6eb5258a..d89dee2259b5 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -203,17 +203,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev, rdev->opencount--; - if (rdev->scan_req && rdev->scan_req->wdev == wdev) { - /* - * If the scan request wasn't notified as done, set it - * to aborted and leak it after a warning. The driver - * should have notified us that it ended at the latest - * during rdev_stop_p2p_device(). - */ - if (WARN_ON(!rdev->scan_req->notified)) - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev, !rdev->scan_req->notified); - } + WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev && + !rdev->scan_req->notified); } static int cfg80211_rfkill_set_block(void *data, bool blocked) @@ -765,13 +756,16 @@ void cfg80211_leave(struct cfg80211_registered_device *rdev, { struct net_device *dev = wdev->netdev; + ASSERT_RTNL(); + switch (wdev->iftype) { case NL80211_IFTYPE_ADHOC: cfg80211_leave_ibss(rdev, dev, true); break; case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_STATION: - __cfg80211_stop_sched_scan(rdev, false); + if (rdev->sched_scan_req && dev == rdev->sched_scan_req->dev) + __cfg80211_stop_sched_scan(rdev, false); wdev_lock(wdev); #ifdef CONFIG_CFG80211_WEXT @@ -865,11 +859,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, break; case NETDEV_DOWN: cfg80211_update_iface_num(rdev, wdev->iftype, -1); - if (rdev->scan_req && rdev->scan_req->wdev == wdev) { - if (WARN_ON(!rdev->scan_req->notified)) - rdev->scan_req->aborted = true; - ___cfg80211_scan_done(rdev, true); - } + WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev && + !rdev->scan_req->notified); if (WARN_ON(rdev->sched_scan_req && rdev->sched_scan_req->dev == wdev->netdev)) { diff --git a/net/wireless/core.h b/net/wireless/core.h index 0a277c33bb02..37ec16d7bb1a 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -67,9 +67,7 @@ struct cfg80211_registered_device { struct work_struct scan_done_wk; struct work_struct sched_scan_results_wk; -#ifdef CONFIG_NL80211_TESTMODE - struct genl_info *testmode_info; -#endif + struct genl_info *cur_cmd_info; struct work_struct conn_work; struct work_struct event_work; @@ -363,7 +361,7 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev, struct key_params *params, int key_idx, bool pairwise, const u8 *mac_addr); void __cfg80211_scan_done(struct work_struct *wk); -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev); void __cfg80211_sched_scan_results(struct work_struct *wk); int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, bool driver_initiated); diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index b0e1869de7de..9c7a11ae7936 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -99,6 +99,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, const struct mesh_config *conf) { struct wireless_dev *wdev = dev->ieee80211_ptr; + u8 radar_detect_width = 0; int err; BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); @@ -177,8 +178,16 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) return -EINVAL; - err = cfg80211_can_use_chan(rdev, wdev, setup->chandef.chan, - CHAN_MODE_SHARED); + err = cfg80211_chandef_dfs_required(wdev->wiphy, &setup->chandef); + if (err < 0) + return err; + if (err) + radar_detect_width = BIT(setup->chandef.width); + + err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype, + setup->chandef.chan, + CHAN_MODE_SHARED, + radar_detect_width); if (err) return err; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index a693f86e5970..04681a46eda8 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -376,6 +376,10 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY }, [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY }, [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, + [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 }, + [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 }, + [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 }, + [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY }, }; /* policy for the key attributes */ @@ -1184,6 +1188,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, struct nlattr *nl_bands, *nl_band; struct nlattr *nl_freqs, *nl_freq; struct nlattr *nl_cmds; + struct nlattr *nl_vendor_cmds; enum ieee80211_band band; struct ieee80211_channel *chan; int i; @@ -1579,6 +1584,19 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) || nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ))) goto nla_put_failure; + state->split_start++; + break; + case 11: + nl_vendor_cmds = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!nl_vendor_cmds) + goto nla_put_failure; + + for (i = 0; i < dev->wiphy.n_vendor_commands; i++) + if (nla_put(msg, i + 1, + sizeof(struct nl80211_vendor_cmd_info), + &dev->wiphy.vendor_commands[i].info)) + goto nla_put_failure; + nla_nest_end(msg, nl_vendor_cmds); /* done */ state->split_start = 0; @@ -4151,6 +4169,12 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) params.vht_capa = nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]); + if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) { + params.opmode_notif_used = true; + params.opmode_notif = + nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]); + } + if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) { params.plink_action = nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); @@ -5667,8 +5691,13 @@ static int nl80211_start_radar_detection(struct sk_buff *skb, struct net_device *dev = info->user_ptr[1]; struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_chan_def chandef; + enum nl80211_dfs_regions dfs_region; int err; + dfs_region = reg_get_dfs_region(wdev->wiphy); + if (dfs_region == NL80211_DFS_UNSET) + return -EINVAL; + err = nl80211_parse_chandef(rdev, info, &chandef); if (err) return err; @@ -6693,6 +6722,40 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info) return err; } +static struct sk_buff * +__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, + int approxlen, u32 portid, u32 seq, + enum nl80211_commands cmd, + enum nl80211_attrs attr, gfp_t gfp) +{ + struct sk_buff *skb; + void *hdr; + struct nlattr *data; + + skb = nlmsg_new(approxlen + 100, gfp); + if (!skb) + return NULL; + + hdr = nl80211hdr_put(skb, portid, seq, 0, cmd); + if (!hdr) { + kfree_skb(skb); + return NULL; + } + + if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) + goto nla_put_failure; + data = nla_nest_start(skb, attr); + + ((void **)skb->cb)[0] = rdev; + ((void **)skb->cb)[1] = hdr; + ((void **)skb->cb)[2] = data; + + return skb; + + nla_put_failure: + kfree_skb(skb); + return NULL; +} #ifdef CONFIG_NL80211_TESTMODE static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) @@ -6717,11 +6780,11 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) if (!info->attrs[NL80211_ATTR_TESTDATA]) return -EINVAL; - rdev->testmode_info = info; + rdev->cur_cmd_info = info; err = rdev_testmode_cmd(rdev, wdev, nla_data(info->attrs[NL80211_ATTR_TESTDATA]), nla_len(info->attrs[NL80211_ATTR_TESTDATA])); - rdev->testmode_info = NULL; + rdev->cur_cmd_info = NULL; return err; } @@ -6821,77 +6884,14 @@ static int nl80211_testmode_dump(struct sk_buff *skb, return err; } -static struct sk_buff * -__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev, - int approxlen, u32 portid, u32 seq, gfp_t gfp) -{ - struct sk_buff *skb; - void *hdr; - struct nlattr *data; - - skb = nlmsg_new(approxlen + 100, gfp); - if (!skb) - return NULL; - - hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE); - if (!hdr) { - kfree_skb(skb); - return NULL; - } - - if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) - goto nla_put_failure; - data = nla_nest_start(skb, NL80211_ATTR_TESTDATA); - - ((void **)skb->cb)[0] = rdev; - ((void **)skb->cb)[1] = hdr; - ((void **)skb->cb)[2] = data; - - return skb; - - nla_put_failure: - kfree_skb(skb); - return NULL; -} - -struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, - int approxlen) -{ - struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - - if (WARN_ON(!rdev->testmode_info)) - return NULL; - - return __cfg80211_testmode_alloc_skb(rdev, approxlen, - rdev->testmode_info->snd_portid, - rdev->testmode_info->snd_seq, - GFP_KERNEL); -} -EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb); - -int cfg80211_testmode_reply(struct sk_buff *skb) -{ - struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; - void *hdr = ((void **)skb->cb)[1]; - struct nlattr *data = ((void **)skb->cb)[2]; - - if (WARN_ON(!rdev->testmode_info)) { - kfree_skb(skb); - return -EINVAL; - } - - nla_nest_end(skb, data); - genlmsg_end(skb, hdr); - return genlmsg_reply(skb, rdev->testmode_info); -} -EXPORT_SYMBOL(cfg80211_testmode_reply); - struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp); + return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, + NL80211_CMD_TESTMODE, + NL80211_ATTR_TESTDATA, gfp); } EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); @@ -7328,11 +7328,72 @@ static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband, return true; } +static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map) +{ + u16 mcs_mask = 0; + + switch (vht_mcs_map) { + case IEEE80211_VHT_MCS_NOT_SUPPORTED: + break; + case IEEE80211_VHT_MCS_SUPPORT_0_7: + mcs_mask = 0x00FF; + break; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + mcs_mask = 0x01FF; + break; + case IEEE80211_VHT_MCS_SUPPORT_0_9: + mcs_mask = 0x03FF; + break; + default: + break; + } + + return mcs_mask; +} + +static void vht_build_mcs_mask(u16 vht_mcs_map, + u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) +{ + u8 nss; + + for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) { + vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map & 0x03); + vht_mcs_map >>= 2; + } +} + +static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband, + struct nl80211_txrate_vht *txrate, + u16 mcs[NL80211_VHT_NSS_MAX]) +{ + u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); + u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {}; + u8 i; + + if (!sband->vht_cap.vht_supported) + return false; + + memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX); + + /* Build vht_mcs_mask from VHT capabilities */ + vht_build_mcs_mask(tx_mcs_map, tx_mcs_mask); + + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { + if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) + mcs[i] = txrate->mcs[i]; + else + return false; + } + + return true; +} + static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_RATES }, - [NL80211_TXRATE_MCS] = { .type = NLA_BINARY, - .len = NL80211_MAX_SUPP_HT_RATES }, + [NL80211_TXRATE_HT] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_HT_RATES }, + [NL80211_TXRATE_VHT] = { .len = sizeof(struct nl80211_txrate_vht)}, }; static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, @@ -7345,9 +7406,7 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, struct net_device *dev = info->user_ptr[1]; struct nlattr *tx_rates; struct ieee80211_supported_band *sband; - - if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) - return -EINVAL; + u16 vht_tx_mcs_map; if (!rdev->ops->set_bitrate_mask) return -EOPNOTSUPP; @@ -7356,17 +7415,26 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, /* Default to all rates enabled */ for (i = 0; i < IEEE80211_NUM_BANDS; i++) { sband = rdev->wiphy.bands[i]; - mask.control[i].legacy = - sband ? (1 << sband->n_bitrates) - 1 : 0; - if (sband) - memcpy(mask.control[i].mcs, - sband->ht_cap.mcs.rx_mask, - sizeof(mask.control[i].mcs)); - else - memset(mask.control[i].mcs, 0, - sizeof(mask.control[i].mcs)); + + if (!sband) + continue; + + mask.control[i].legacy = (1 << sband->n_bitrates) - 1; + memcpy(mask.control[i].ht_mcs, + sband->ht_cap.mcs.rx_mask, + sizeof(mask.control[i].ht_mcs)); + + if (!sband->vht_cap.vht_supported) + continue; + + vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); + vht_build_mcs_mask(vht_tx_mcs_map, mask.control[i].vht_mcs); } + /* if no rates are given set it back to the defaults */ + if (!info->attrs[NL80211_ATTR_TX_RATES]) + goto out; + /* * The nested attribute uses enum nl80211_band as the index. This maps * directly to the enum ieee80211_band values used in cfg80211. @@ -7391,31 +7459,44 @@ static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, nla_len(tb[NL80211_TXRATE_LEGACY])) return -EINVAL; } - if (tb[NL80211_TXRATE_MCS]) { + if (tb[NL80211_TXRATE_HT]) { if (!ht_rateset_to_mask( sband, - nla_data(tb[NL80211_TXRATE_MCS]), - nla_len(tb[NL80211_TXRATE_MCS]), - mask.control[band].mcs)) + nla_data(tb[NL80211_TXRATE_HT]), + nla_len(tb[NL80211_TXRATE_HT]), + mask.control[band].ht_mcs)) + return -EINVAL; + } + if (tb[NL80211_TXRATE_VHT]) { + if (!vht_set_mcs_mask( + sband, + nla_data(tb[NL80211_TXRATE_VHT]), + mask.control[band].vht_mcs)) return -EINVAL; } if (mask.control[band].legacy == 0) { - /* don't allow empty legacy rates if HT - * is not even supported. */ - if (!rdev->wiphy.bands[band]->ht_cap.ht_supported) + /* don't allow empty legacy rates if HT or VHT + * are not even supported. + */ + if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported || + rdev->wiphy.bands[band]->vht_cap.vht_supported)) return -EINVAL; for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) - if (mask.control[band].mcs[i]) - break; + if (mask.control[band].ht_mcs[i]) + goto out; + + for (i = 0; i < NL80211_VHT_NSS_MAX; i++) + if (mask.control[band].vht_mcs[i]) + goto out; /* legacy and mcs rates may not be both empty */ - if (i == IEEE80211_HT_MCS_MASK_LEN) - return -EINVAL; + return -EINVAL; } } +out: return rdev_set_bitrate_mask(rdev, dev, NULL, &mask); } @@ -8875,6 +8956,111 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb, return 0; } +static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct wireless_dev *wdev = + __cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs); + int i, err; + u32 vid, subcmd; + + if (!rdev->wiphy.vendor_commands) + return -EOPNOTSUPP; + + if (IS_ERR(wdev)) { + err = PTR_ERR(wdev); + if (err != -EINVAL) + return err; + wdev = NULL; + } else if (wdev->wiphy != &rdev->wiphy) { + return -EINVAL; + } + + if (!info->attrs[NL80211_ATTR_VENDOR_ID] || + !info->attrs[NL80211_ATTR_VENDOR_SUBCMD]) + return -EINVAL; + + vid = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_ID]); + subcmd = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_SUBCMD]); + for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { + const struct wiphy_vendor_command *vcmd; + void *data = NULL; + int len = 0; + + vcmd = &rdev->wiphy.vendor_commands[i]; + + if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) + continue; + + if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV)) { + if (!wdev) + return -EINVAL; + if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && + !wdev->netdev) + return -EINVAL; + + if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { + if (wdev->netdev && + !netif_running(wdev->netdev)) + return -ENETDOWN; + if (!wdev->netdev && !wdev->p2p_started) + return -ENETDOWN; + } + } else { + wdev = NULL; + } + + if (info->attrs[NL80211_ATTR_VENDOR_DATA]) { + data = nla_data(info->attrs[NL80211_ATTR_VENDOR_DATA]); + len = nla_len(info->attrs[NL80211_ATTR_VENDOR_DATA]); + } + + rdev->cur_cmd_info = info; + err = rdev->wiphy.vendor_commands[i].doit(&rdev->wiphy, wdev, + data, len); + rdev->cur_cmd_info = NULL; + return err; + } + + return -EOPNOTSUPP; +} + +struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, + enum nl80211_commands cmd, + enum nl80211_attrs attr, + int approxlen) +{ + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + if (WARN_ON(!rdev->cur_cmd_info)) + return NULL; + + return __cfg80211_alloc_vendor_skb(rdev, approxlen, + rdev->cur_cmd_info->snd_portid, + rdev->cur_cmd_info->snd_seq, + cmd, attr, GFP_KERNEL); +} +EXPORT_SYMBOL(__cfg80211_alloc_reply_skb); + +int cfg80211_vendor_cmd_reply(struct sk_buff *skb) +{ + struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; + void *hdr = ((void **)skb->cb)[1]; + struct nlattr *data = ((void **)skb->cb)[2]; + + if (WARN_ON(!rdev->cur_cmd_info)) { + kfree_skb(skb); + return -EINVAL; + } + + nla_nest_end(skb, data); + genlmsg_end(skb, hdr); + return genlmsg_reply(skb, rdev->cur_cmd_info); +} +EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); + + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -9599,6 +9785,14 @@ static const struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_VENDOR, + .doit = nl80211_vendor_cmd, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_WIPHY | + NL80211_FLAG_NEED_RTNL, + }, }; /* notification functions */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index ec54e1aac8e2..7d20d844ca60 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -135,6 +135,33 @@ static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region) return "Unknown"; } +enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy) +{ + const struct ieee80211_regdomain *regd = NULL; + const struct ieee80211_regdomain *wiphy_regd = NULL; + + regd = get_cfg80211_regdom(); + if (!wiphy) + goto out; + + wiphy_regd = get_wiphy_regdom(wiphy); + if (!wiphy_regd) + goto out; + + if (wiphy_regd->dfs_region == regd->dfs_region) + goto out; + + REG_DBG_PRINT("%s: device specific dfs_region " + "(%s) disagrees with cfg80211's " + "central dfs_region (%s)\n", + dev_name(&wiphy->dev), + reg_dfs_region_str(wiphy_regd->dfs_region), + reg_dfs_region_str(regd->dfs_region)); + +out: + return regd->dfs_region; +} + static void rcu_free_regdom(const struct ieee80211_regdomain *r) { if (!r) diff --git a/net/wireless/reg.h b/net/wireless/reg.h index cc4c2c0a6723..02bd8f4b0921 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -21,6 +21,7 @@ extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain; bool reg_is_valid_request(const char *alpha2); bool is_world_regdom(const char *alpha2); bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region); +enum nl80211_dfs_regions reg_get_dfs_region(struct wiphy *wiphy); int regulatory_hint_user(const char *alpha2, enum nl80211_user_reg_hint_type user_reg_hint_type); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index d4397eba5408..a32d52a04c27 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -161,7 +161,7 @@ static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev, dev->bss_generation++; } -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev) { struct cfg80211_scan_request *request; struct wireless_dev *wdev; @@ -210,17 +210,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) dev_put(wdev->netdev); rdev->scan_req = NULL; - - /* - * OK. If this is invoked with "leak" then we can't - * free this ... but we've cleaned it up anyway. The - * driver failed to call the scan_done callback, so - * all bets are off, it might still be trying to use - * the scan request or not ... if it accesses the dev - * in there (it shouldn't anyway) then it may crash. - */ - if (!leak) - kfree(request); + kfree(request); } void __cfg80211_scan_done(struct work_struct *wk) @@ -231,7 +221,7 @@ void __cfg80211_scan_done(struct work_struct *wk) scan_done_wk); rtnl_lock(); - ___cfg80211_scan_done(rdev, false); + ___cfg80211_scan_done(rdev); rtnl_unlock(); } |