Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
This commit is contained in:
commit
ad86c55bac
123 changed files with 5236 additions and 4012 deletions
MAINTAINERS
drivers
bcma
net/wireless
ath
ath6kl
ath9k
Makefileantenna.car9002_hw.car9002_mac.car9002_phy.car9003_eeprom.car9003_eeprom.har9003_mac.car9003_phy.hath9k.hbeacon.cbtcoex.ccommon.cdebug.cdebug.hdfs.ceeprom_4k.ceeprom_9287.ceeprom_def.cgpio.chtc.hhtc_drv_beacon.chtc_drv_gpio.chtc_drv_init.chw-ops.hhw.chw.hinit.cmac.cmac.hmain.cmci.crecv.cspectral.cspectral.hxmit.c
wcn36xx
brcm80211/brcmfmac
Makefilebcdc.cbcmsdh.cbcmsdh_sdmmc.cdhd_bus.hdhd_sdio.cfwil.cfwsignal.cproto.cproto.hsdio_chip.csdio_chip.hsdio_host.husb.c
iwlwifi
dvm
iwl-7000.ciwl-config.hiwl-drv.ciwl-eeprom-parse.ciwl-fw-file.hiwl-fw.hiwl-nvm-parse.ciwl-op-mode.hiwl-trans.hmvm
Makefilebinding.cdebugfs-vif.cfw-api-power.hfw-api-sta.hfw-api-tx.hfw-api.hfw.cmac80211.cmvm.hops.cpower.crs.crs.hrx.csf.csta.ctt.ctx.cutils.c
pcie
mwifiex
include
net/mac80211
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:= \
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
nexttbtt = ath9k_get_next_tbtt(sc, tsf, intval);
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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 @@ static void ath_btcoex_period_timer(unsigned long data)
|
|||
* 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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
u32 mask = 0;
|
||||
|
||||
BUG_ON(!timer_period);
|
||||
|
||||
set_bit(timer->index, &timer_table->timer_mask.timer_bits);
|
||||
|
||||
tsf = ath9k_hw_gettsf32(ah);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 *),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -200,7 +200,7 @@ static void ath_mci_update_scheme(struct ath_softc *sc)
|
|||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
543
drivers/net/wireless/ath/ath9k/spectral.c
Normal file
543
drivers/net/wireless/ath/ath9k/spectral.c
Normal file
|
@ -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);
|
||||
}
|
212
drivers/net/wireless/ath/ath9k/spectral.h
Normal file
212
drivers/net/wireless/ath/ath9k/spectral.h
Normal file
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
mutex_unlock(&wcn->hal_ind_mutex);
|
||||
if (msg_ind)
|
||||
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;
|
||||
/* 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);
|
||||
}
|
||||
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);
|
||||
wcn36xx_dbg(WCN36XX_DBG_HAL, "indication arrived\n");
|
||||
break;
|
||||
default:
|
||||
wcn36xx_err("SMD_EVENT (%d) not supported\n",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev)
|
||||
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;
|
||||
}
|
||||
|
||||
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 @@ brcmf_sdio_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr,
|
|||
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 @@ static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
|
|||
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)
|
||||
{
|
||||
u32 regs = 0;
|
||||
int ret = 0;
|
||||
|
||||
ret = brcmf_sdioh_attach(sdiodev);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
regs = SI_ENUM_BASE;
|
||||
|
||||
/* try to attach to the target device */
|
||||
sdiodev->bus = brcmf_sdbrcm_probe(regs, sdiodev);
|
||||
if (!sdiodev->bus) {
|
||||
brcmf_err("device attach failed\n");
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
brcmf_sdio_remove(sdiodev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int brcmf_sdio_remove(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_sdbrcm_disconnect(sdiodev->bus);
|
||||
brcmf_sdio_remove(sdiodev->bus);
|
||||
sdiodev->bus = NULL;
|
||||
}
|
||||
|
||||
brcmf_sdioh_detach(sdiodev);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
void brcmf_sdio_wdtmr_enable(struct brcmf_sdio_dev *sdiodev, bool enable)
|
||||
static int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
|
||||
{
|
||||
if (enable)
|
||||
brcmf_sdbrcm_wd_timer(sdiodev->bus, BRCMF_WD_POLL_MS);
|
||||
else
|
||||
brcmf_sdbrcm_wd_timer(sdiodev->bus, 0);
|
||||
struct sdio_func *func;
|
||||
struct mmc_host *host;
|
||||
uint max_blocks;
|
||||
int ret = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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_sdio_probe(sdiodev);
|
||||
if (!sdiodev->bus) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret)
|
||||
brcmf_sdiod_remove(sdiodev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
SPINWAIT((brcmf_sdiod_regrl(sdiodev,
|
||||
CORE_SB(base, sbtmstatehigh),
|
||||
NULL) &
|
||||
SSB_TMSHIGH_BUSY), 100000);
|
||||
|
||||
regdata = brcmf_sdio_regrl(sdiodev,
|
||||
CORE_SB(base, sbtmstatehigh),
|
||||
NULL);
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_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_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_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);
|
||||
int brcmf_sdiod_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);
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick);
|
||||
|
||||
/* 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);
|
||||
|
||||
void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev);
|
||||
void brcmf_sdbrcm_disconnect(void *ptr);
|
||||
void brcmf_sdbrcm_isr(void *arg);
|
||||
|
||||
void brcmf_sdbrcm_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_ */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 *));
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
@ -317,6 +317,24 @@ enum iwl_d3_status {
|
|||
IWL_D3_STATUS_RESET,
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
@ -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
|
||||
******************************************************/
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,47 +748,116 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw,
|
|||
mutex_unlock(&mvm->mutex);
|
||||
}
|
||||
|
||||
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_config(struct ieee80211_hw *hw, u32 changed)
|
||||
{
|
||||
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,
|
||||
};
|
||||
|
||||
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 u64 iwl_mvm_prepare_multicast(struct ieee80211_hw *hw,
|
||||
struct netdev_hw_addr_list *mc_list)
|
||||
{
|
||||
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,
|
||||
unsigned int changed_flags,
|
||||
unsigned int *total_flags,
|
||||
u64 multicast)
|
||||
{
|
||||
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
|
||||
struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast;
|
||||
|
||||
mutex_lock(&mvm->mutex);
|
||||
|
||||
/* replace previous configuration */
|
||||
kfree(mvm->mcast_filter_cmd);
|
||||
mvm->mcast_filter_cmd = cmd;
|
||||
|
||||
if (!cmd)
|
||||
goto out;
|
||||
|
||||
iwl_mvm_recalc_multicast(mvm);
|
||||
out:
|
||||
mutex_unlock(&mvm->mutex);
|
||||
*total_flags = 0;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
memcpy(mcast_filter_cmd.bssid, vif->bss_conf.bssid, ETH_ALEN);
|
||||
|
||||
return iwl_mvm_send_cmd_pdu(mvm, MCAST_FILTER_CMD, CMD_SYNC,
|
||||
sizeof(mcast_filter_cmd),
|
||||
&mcast_filter_cmd);
|
||||
}
|
||||
|
||||
static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *bss_conf,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
|
||||
ETH_ALEN))
|
||||
allow_uapsd = false;
|
||||
|
||||
if (mvm->cur_ucode != IWL_UCODE_WOWLAN)
|
||||
cmd->flags |=
|
||||
cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK);
|
||||
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;
|
||||
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
||||
WARN_ON_ONCE(rate->index < IWL_RATE_MCS_0_INDEX &&
|
||||
rate->index > IWL_RATE_MCS_9_INDEX);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
rate_mask = rs_get_supported_rates(lq_sta, rate);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
if (num_of_ant(rate->ant) > 1)
|
||||
rate->ant = first_antenna(iwl_fw_valid_tx_ant(mvm->fw));
|
||||
|
||||
/* If we switched from HT to legacy, check current rate */
|
||||
if (switch_to_legacy && (rate_mask & (1 << scale_index))) {
|
||||
low = scale_index;
|
||||
goto out;
|
||||
}
|
||||
/* Relevant in both switching to SISO or Legacy */
|
||||
rate->sgi = false;
|
||||
|
||||
high_low = rs_get_adjacent_rate(lq_sta->drv, scale_index, rate_mask,
|
||||
rate->type);
|
||||
low = high_low & 0xff;
|
||||
|
||||
if (low == IWL_RATE_INVALID)
|
||||
low = scale_index;
|
||||
|
||||
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 @@ static int rs_switch_to_column(struct iwl_mvm *mvm,
|
|||
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 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
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 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
}
|
||||
|
||||
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,113 +2359,165 @@ 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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
repeat_rate--;
|
||||
index++;
|
||||
}
|
||||
|
||||
rs_rate_from_ucode_rate(new_rate, lq_sta->band, &rate);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Get next rate */
|
||||
new_rate = rs_get_lower_rate(lq_sta, &rate, rate.index,
|
||||
use_ht_possible);
|
||||
|
||||
/* 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;
|
||||
|
||||
repeat_rate = IWL_NUMBER_TRY;
|
||||
} else {
|
||||
repeat_rate = IWL_HT_NUMBER_TRY;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/* 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);
|
||||
|
||||
index++;
|
||||
repeat_rate--;
|
||||
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;
|
||||
}
|
||||
|
||||
rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
|
||||
num_rates, num_retries, valid_tx_ant,
|
||||
toggle_ant);
|
||||
|
||||
rs_get_lower_rate_down_column(lq_sta, &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);
|
||||
}
|
||||
|
||||
toggle_ant = true;
|
||||
|
||||
rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
|
||||
num_rates, num_retries, valid_tx_ant,
|
||||
toggle_ant);
|
||||
|
||||
rs_get_lower_rate_down_column(lq_sta, &rate);
|
||||
|
||||
num_rates = RS_SECONDARY_LEGACY_NUM_RATES;
|
||||
num_retries = RS_LEGACY_RETRIES_PER_RATE;
|
||||
|
||||
rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index,
|
||||
num_rates, num_retries, valid_tx_ant,
|
||||
toggle_ant);
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
#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",
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
291
drivers/net/wireless/iwlwifi/mvm/sf.c
Normal file
291
drivers/net/wireless/iwlwifi/mvm/sf.c
Normal file
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans,
|
|||
|
||||
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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
/**
|
||||
|
@ -2675,6 +2680,34 @@ struct wiphy_coalesce_support {
|
|||
int max_pkt_offset;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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,
|
||||
|
@ -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
|
||||
|
|
|
@ -1162,6 +1162,19 @@ static inline bool ieee80211_vif_is_mesh(struct ieee80211_vif *vif)
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
@ -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,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
|
|||
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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,29 +884,13 @@ 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;
|
||||
if (cfg80211_chandef_identical(¶ms.chandef,
|
||||
&sdata->vif.bss_conf.chandef)) {
|
||||
ibss_dbg(sdata,
|
||||
"received csa with an identical chandef, ignoring\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
/* don't handle for multi-VIF cases */
|
||||
chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
|
||||
if (chanctx->refcount > 1) {
|
||||
rcu_read_unlock();
|
||||
goto disconnect;
|
||||
}
|
||||
num_chanctx = 0;
|
||||
list_for_each_entry_rcu(chanctx, &sdata->local->chanctx_list, list)
|
||||
num_chanctx++;
|
||||
|
||||
if (num_chanctx > 1) {
|
||||
rcu_read_unlock();
|
||||
goto disconnect;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
/* all checks done, now perform the channel switch. */
|
||||
ibss_dbg(sdata,
|
||||
"received channel switch announcement to go to channel %d MHz\n",
|
||||
|
@ -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 */
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue