Merge branch 'for-davem' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next
John W. Linville says: ==================== pull request: wireless-next 2014-01-01 These patches were tucked-in with me for my long winter's nap! Please pull them for the 3.14 stream... For the mac80211 bits, Johannes says: "Here I just have a collection of fixes/improvements/cleanups, very little really stands out apart from CSA fixes, vendor command support and the RCU speedups." For the iwlwifi bits, Emmanuel says: "I have hear quite a few things. Alex continues his work on power management. Arik is reworking the transport API by unifying redudant APIs and making error handling more generic. Eyal keeps on digging in the rate scaling code. We also have two new features - Max is using the brand new generic cipher infrastructure in mac80211, and Lilach implements the smart fifo which allows to save power by making interrupt coalescing smarter." Along with those, Arend and company bring a batch of brcmfmac. Sujith and Felix bring the usual high level of ath9k activity as well. Bing gives mwifiex some love as well, and a handful of other bits get updates here and there. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
63d515c319
123 changed files with 5236 additions and 4012 deletions
|
@ -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 @@ skip_hw_wakeup:
|
|||
* Generic tsf based hw timer which configures weight
|
||||
* registers to time slice between wlan and bt traffic
|
||||
*/
|
||||
static void ath_btcoex_no_stomp_timer(void *arg)
|
||||
static void ath_btcoex_no_stomp_timer(unsigned long arg)
|
||||
{
|
||||
struct ath_softc *sc = (struct ath_softc *)arg;
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
|
@ -311,24 +271,18 @@ static int ath_init_btcoex_timer(struct ath_softc *sc)
|
|||
struct ath_btcoex *btcoex = &sc->btcoex;
|
||||
|
||||
btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD;
|
||||
btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * 1000 *
|
||||
btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) *
|
||||
btcoex->btcoex_period / 100;
|
||||
btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) * 1000 *
|
||||
btcoex->btscan_no_stomp = (100 - ATH_BTCOEX_BTSCAN_DUTY_CYCLE) *
|
||||
btcoex->btcoex_period / 100;
|
||||
|
||||
setup_timer(&btcoex->period_timer, ath_btcoex_period_timer,
|
||||
(unsigned long) sc);
|
||||
setup_timer(&btcoex->no_stomp_timer, ath_btcoex_no_stomp_timer,
|
||||
(unsigned long) sc);
|
||||
|
||||
spin_lock_init(&btcoex->btcoex_lock);
|
||||
|
||||
btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah,
|
||||
ath_btcoex_no_stomp_timer,
|
||||
ath_btcoex_no_stomp_timer,
|
||||
(void *) sc, AR_FIRST_NDP_TIMER);
|
||||
|
||||
if (!btcoex->no_stomp_timer)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -343,10 +297,7 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc)
|
|||
ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n");
|
||||
|
||||
/* make sure duty cycle timer is also stopped when resuming */
|
||||
if (btcoex->hw_timer_enabled) {
|
||||
ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
|
||||
btcoex->hw_timer_enabled = false;
|
||||
}
|
||||
del_timer_sync(&btcoex->no_stomp_timer);
|
||||
|
||||
btcoex->bt_priority_cnt = 0;
|
||||
btcoex->bt_priority_time = jiffies;
|
||||
|
@ -363,24 +314,16 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc)
|
|||
void ath9k_btcoex_timer_pause(struct ath_softc *sc)
|
||||
{
|
||||
struct ath_btcoex *btcoex = &sc->btcoex;
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
|
||||
del_timer_sync(&btcoex->period_timer);
|
||||
|
||||
if (btcoex->hw_timer_enabled) {
|
||||
ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer);
|
||||
btcoex->hw_timer_enabled = false;
|
||||
}
|
||||
del_timer_sync(&btcoex->no_stomp_timer);
|
||||
}
|
||||
|
||||
void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc)
|
||||
{
|
||||
struct ath_btcoex *btcoex = &sc->btcoex;
|
||||
|
||||
if (btcoex->hw_timer_enabled) {
|
||||
ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer);
|
||||
btcoex->hw_timer_enabled = false;
|
||||
}
|
||||
del_timer_sync(&btcoex->no_stomp_timer);
|
||||
}
|
||||
|
||||
u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
|
||||
|
@ -400,12 +343,6 @@ u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen)
|
|||
|
||||
void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status)
|
||||
{
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
|
||||
if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE)
|
||||
if (status & ATH9K_INT_GENTIMER)
|
||||
ath_gen_timer_isr(sc->sc_ah);
|
||||
|
||||
if (status & ATH9K_INT_MCI)
|
||||
ath_mci_intr(sc);
|
||||
}
|
||||
|
@ -447,10 +384,6 @@ void ath9k_deinit_btcoex(struct ath_softc *sc)
|
|||
{
|
||||
struct ath_hw *ah = sc->sc_ah;
|
||||
|
||||
if ((sc->btcoex.no_stomp_timer) &&
|
||||
ath9k_hw_get_btcoex_scheme(sc->sc_ah) == ATH_BTCOEX_CFG_3WIRE)
|
||||
ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer);
|
||||
|
||||
if (ath9k_hw_mci_is_enabled(ah))
|
||||
ath_mci_cleanup(sc);
|
||||
}
|
||||
|
|
|
@ -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 @@ skip_tuning:
|
|||
if (btcoex->duty_cycle > ATH_MCI_MAX_DUTY_CYCLE)
|
||||
btcoex->duty_cycle = ATH_MCI_MAX_DUTY_CYCLE;
|
||||
|
||||
btcoex->btcoex_no_stomp = btcoex->btcoex_period * 1000 *
|
||||
btcoex->btcoex_no_stomp = btcoex->btcoex_period *
|
||||
(100 - btcoex->duty_cycle) / 100;
|
||||
|
||||
ath9k_hw_btcoex_enable(sc->sc_ah);
|
||||
|
|
|
@ -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 @@ done:
|
|||
return ret;
|
||||
}
|
||||
|
||||
u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
|
||||
u8 brcmf_sdiod_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
|
||||
{
|
||||
u8 data;
|
||||
int retval;
|
||||
|
||||
brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
|
||||
retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false);
|
||||
retval = brcmf_sdiod_regrw_helper(sdiodev, addr, &data, false);
|
||||
brcmf_dbg(SDIO, "data:0x%02x\n", data);
|
||||
|
||||
if (ret)
|
||||
|
@ -277,13 +422,13 @@ u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
|
|||
return data;
|
||||
}
|
||||
|
||||
u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
|
||||
u32 brcmf_sdiod_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
|
||||
{
|
||||
u32 data;
|
||||
int retval;
|
||||
|
||||
brcmf_dbg(SDIO, "addr:0x%08x\n", addr);
|
||||
retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false);
|
||||
retval = brcmf_sdiod_regrw_helper(sdiodev, addr, &data, false);
|
||||
brcmf_dbg(SDIO, "data:0x%08x\n", data);
|
||||
|
||||
if (ret)
|
||||
|
@ -292,37 +437,37 @@ u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
|
|||
return data;
|
||||
}
|
||||
|
||||
void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
|
||||
void brcmf_sdiod_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr,
|
||||
u8 data, int *ret)
|
||||
{
|
||||
int retval;
|
||||
|
||||
brcmf_dbg(SDIO, "addr:0x%08x, data:0x%02x\n", addr, data);
|
||||
retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true);
|
||||
retval = brcmf_sdiod_regrw_helper(sdiodev, addr, &data, true);
|
||||
|
||||
if (ret)
|
||||
*ret = retval;
|
||||
}
|
||||
|
||||
void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
|
||||
void brcmf_sdiod_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr,
|
||||
u32 data, int *ret)
|
||||
{
|
||||
int retval;
|
||||
|
||||
brcmf_dbg(SDIO, "addr:0x%08x, data:0x%08x\n", addr, data);
|
||||
retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true);
|
||||
retval = brcmf_sdiod_regrw_helper(sdiodev, addr, &data, true);
|
||||
|
||||
if (ret)
|
||||
*ret = retval;
|
||||
}
|
||||
|
||||
static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
|
||||
static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
|
||||
bool write, u32 addr, struct sk_buff *pkt)
|
||||
{
|
||||
unsigned int req_sz;
|
||||
|
||||
brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
|
||||
if (brcmf_pm_resume_error(sdiodev))
|
||||
brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
|
||||
if (brcmf_sdiod_pm_resume_error(sdiodev))
|
||||
return -EIO;
|
||||
|
||||
/* Single skb use the standard mmc interface */
|
||||
|
@ -345,7 +490,7 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
|
|||
}
|
||||
|
||||
/**
|
||||
* brcmf_sdio_sglist_rw - SDIO interface function for block data access
|
||||
* brcmf_sdiod_sglist_rw - SDIO interface function for block data access
|
||||
* @sdiodev: brcmfmac sdio device
|
||||
* @fn: SDIO function number
|
||||
* @write: direction flag
|
||||
|
@ -356,9 +501,9 @@ static int brcmf_sdio_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn,
|
|||
* stack for block data access. It assumes that the skb passed down by the
|
||||
* caller has already been padded and aligned.
|
||||
*/
|
||||
static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
|
||||
bool write, u32 addr,
|
||||
struct sk_buff_head *pktlist)
|
||||
static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
|
||||
bool write, u32 addr,
|
||||
struct sk_buff_head *pktlist)
|
||||
{
|
||||
unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
|
||||
unsigned int max_req_sz, orig_offset, dst_offset;
|
||||
|
@ -376,8 +521,8 @@ static int brcmf_sdio_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn,
|
|||
if (!pktlist->qlen)
|
||||
return -EINVAL;
|
||||
|
||||
brcmf_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
|
||||
if (brcmf_pm_resume_error(sdiodev))
|
||||
brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait);
|
||||
if (brcmf_sdiod_pm_resume_error(sdiodev))
|
||||
return -EIO;
|
||||
|
||||
target_list = pktlist;
|
||||
|
@ -524,9 +669,7 @@ exit:
|
|||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
|
||||
uint flags, u8 *buf, uint nbytes)
|
||||
int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
|
||||
{
|
||||
struct sk_buff *mypkt;
|
||||
int err;
|
||||
|
@ -538,7 +681,7 @@ brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
err = brcmf_sdcard_recv_pkt(sdiodev, addr, fn, flags, mypkt);
|
||||
err = brcmf_sdiod_recv_pkt(sdiodev, mypkt);
|
||||
if (!err)
|
||||
memcpy(buf, mypkt->data, nbytes);
|
||||
|
||||
|
@ -546,50 +689,47 @@ brcmf_sdcard_recv_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
|
|||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
|
||||
uint flags, struct sk_buff *pkt)
|
||||
int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt)
|
||||
{
|
||||
uint width;
|
||||
u32 addr = sdiodev->sbwad;
|
||||
int err = 0;
|
||||
|
||||
brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
|
||||
fn, addr, pkt->len);
|
||||
brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len);
|
||||
|
||||
width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
|
||||
err = brcmf_sdio_addrprep(sdiodev, width, &addr);
|
||||
err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pkt);
|
||||
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr, pkt);
|
||||
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
|
||||
uint flags, struct sk_buff_head *pktq, uint totlen)
|
||||
int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
|
||||
struct sk_buff_head *pktq, uint totlen)
|
||||
{
|
||||
struct sk_buff *glom_skb;
|
||||
struct sk_buff *skb;
|
||||
uint width;
|
||||
u32 addr = sdiodev->sbwad;
|
||||
int err = 0;
|
||||
|
||||
brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
|
||||
fn, addr, pktq->qlen);
|
||||
brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n",
|
||||
addr, pktq->qlen);
|
||||
|
||||
width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
|
||||
err = brcmf_sdio_addrprep(sdiodev, width, &addr);
|
||||
err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
if (pktq->qlen == 1)
|
||||
err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, pktq->next);
|
||||
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
|
||||
pktq->next);
|
||||
else if (!sdiodev->sg_support) {
|
||||
glom_skb = brcmu_pkt_buf_get_skb(totlen);
|
||||
if (!glom_skb)
|
||||
return -ENOMEM;
|
||||
err = brcmf_sdio_buffrw(sdiodev, fn, false, addr, glom_skb);
|
||||
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, false, addr,
|
||||
glom_skb);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
|
@ -598,18 +738,17 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
|
|||
skb_pull(glom_skb, skb->len);
|
||||
}
|
||||
} else
|
||||
err = brcmf_sdio_sglist_rw(sdiodev, fn, false, addr, pktq);
|
||||
err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, false, addr,
|
||||
pktq);
|
||||
|
||||
done:
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
|
||||
uint flags, u8 *buf, uint nbytes)
|
||||
int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
|
||||
{
|
||||
struct sk_buff *mypkt;
|
||||
uint width;
|
||||
u32 addr = sdiodev->sbwad;
|
||||
int err;
|
||||
|
||||
mypkt = brcmu_pkt_buf_get_skb(nbytes);
|
||||
|
@ -621,48 +760,47 @@ brcmf_sdcard_send_buf(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
|
|||
|
||||
memcpy(mypkt->data, buf, nbytes);
|
||||
|
||||
width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
|
||||
err = brcmf_sdio_addrprep(sdiodev, width, &addr);
|
||||
err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
|
||||
|
||||
if (!err)
|
||||
err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, mypkt);
|
||||
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true, addr,
|
||||
mypkt);
|
||||
|
||||
brcmu_pkt_buf_free_skb(mypkt);
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn,
|
||||
uint flags, struct sk_buff_head *pktq)
|
||||
int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
|
||||
struct sk_buff_head *pktq)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
uint width;
|
||||
u32 addr = sdiodev->sbwad;
|
||||
int err;
|
||||
|
||||
brcmf_dbg(SDIO, "fun = %d, addr = 0x%x, size = %d\n",
|
||||
fn, addr, pktq->qlen);
|
||||
brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen);
|
||||
|
||||
width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
|
||||
err = brcmf_sdio_addrprep(sdiodev, width, &addr);
|
||||
err = brcmf_sdiod_addrprep(sdiodev, 4, &addr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (pktq->qlen == 1 || !sdiodev->sg_support)
|
||||
skb_queue_walk(pktq, skb) {
|
||||
err = brcmf_sdio_buffrw(sdiodev, fn, true, addr, skb);
|
||||
err = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_2, true,
|
||||
addr, skb);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
else
|
||||
err = brcmf_sdio_sglist_rw(sdiodev, fn, true, addr, pktq);
|
||||
err = brcmf_sdiod_sglist_rw(sdiodev, SDIO_FUNC_2, true, addr,
|
||||
pktq);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int
|
||||
brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
|
||||
u8 *data, uint size)
|
||||
brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
|
||||
u8 *data, uint size)
|
||||
{
|
||||
int bcmerror = 0;
|
||||
struct sk_buff *pkt;
|
||||
|
@ -689,7 +827,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
|
|||
/* Do the transfer(s) */
|
||||
while (size) {
|
||||
/* Set the backplane window to include the start address */
|
||||
bcmerror = brcmf_sdcard_set_sbaddr_window(sdiodev, address);
|
||||
bcmerror = brcmf_sdiod_set_sbaddr_window(sdiodev, address);
|
||||
if (bcmerror)
|
||||
break;
|
||||
|
||||
|
@ -703,8 +841,8 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
|
|||
skb_put(pkt, dsize);
|
||||
if (write)
|
||||
memcpy(pkt->data, data, dsize);
|
||||
bcmerror = brcmf_sdio_buffrw(sdiodev, SDIO_FUNC_1, write,
|
||||
sdaddr, pkt);
|
||||
bcmerror = brcmf_sdiod_buffrw(sdiodev, SDIO_FUNC_1, write,
|
||||
sdaddr, pkt);
|
||||
if (bcmerror) {
|
||||
brcmf_err("membytes transfer failed\n");
|
||||
break;
|
||||
|
@ -726,7 +864,7 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
|
|||
dev_kfree_skb(pkt);
|
||||
|
||||
/* Return the window to backplane enumeration space for core access */
|
||||
if (brcmf_sdcard_set_sbaddr_window(sdiodev, sdiodev->sbwad))
|
||||
if (brcmf_sdiod_set_sbaddr_window(sdiodev, sdiodev->sbwad))
|
||||
brcmf_err("FAILED to set window back to 0x%x\n",
|
||||
sdiodev->sbwad);
|
||||
|
||||
|
@ -735,65 +873,337 @@ brcmf_sdio_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
|
|||
return bcmerror;
|
||||
}
|
||||
|
||||
int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
|
||||
int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, uint fn)
|
||||
{
|
||||
char t_func = (char)fn;
|
||||
brcmf_dbg(SDIO, "Enter\n");
|
||||
|
||||
/* issue abort cmd52 command through F0 */
|
||||
brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0,
|
||||
brcmf_sdiod_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0,
|
||||
SDIO_CCCR_ABORT, &t_func);
|
||||
|
||||
brcmf_dbg(SDIO, "Exit\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)
|
||||
{
|
||||
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 @@ out_release:
|
|||
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 @@ err:
|
|||
return -1;
|
||||
}
|
||||
|
||||
static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm,
|
||||
struct iwl_scale_tbl_info *tbl,
|
||||
s32 sr, int low, int high,
|
||||
int current_tpt,
|
||||
int low_tpt, int high_tpt)
|
||||
{
|
||||
enum rs_action action = RS_ACTION_STAY;
|
||||
|
||||
/* Too many failures, decrease rate */
|
||||
if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"decrease rate because of low SR\n");
|
||||
action = RS_ACTION_DOWNSCALE;
|
||||
/* No throughput measured yet for adjacent rates; try increase. */
|
||||
} else if ((low_tpt == IWL_INVALID_VALUE) &&
|
||||
(high_tpt == IWL_INVALID_VALUE)) {
|
||||
if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Good SR and no high rate measurement. "
|
||||
"Increase rate\n");
|
||||
action = RS_ACTION_UPSCALE;
|
||||
} else if (low != IWL_RATE_INVALID) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Remain in current rate\n");
|
||||
action = RS_ACTION_STAY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Both adjacent throughputs are measured, but neither one has better
|
||||
* throughput; we're using the best rate, don't change it!
|
||||
*/
|
||||
else if ((low_tpt != IWL_INVALID_VALUE) &&
|
||||
(high_tpt != IWL_INVALID_VALUE) &&
|
||||
(low_tpt < current_tpt) &&
|
||||
(high_tpt < current_tpt)) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Both high and low are worse. "
|
||||
"Maintain rate\n");
|
||||
action = RS_ACTION_STAY;
|
||||
}
|
||||
|
||||
/* At least one adjacent rate's throughput is measured,
|
||||
* and may have better performance.
|
||||
*/
|
||||
else {
|
||||
/* Higher adjacent rate's throughput is measured */
|
||||
if (high_tpt != IWL_INVALID_VALUE) {
|
||||
/* Higher rate has better throughput */
|
||||
if (high_tpt > current_tpt &&
|
||||
sr >= IWL_RATE_INCREASE_TH) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Higher rate is better and good "
|
||||
"SR. Increate rate\n");
|
||||
action = RS_ACTION_UPSCALE;
|
||||
} else {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Higher rate isn't better OR "
|
||||
"no good SR. Maintain rate\n");
|
||||
action = RS_ACTION_STAY;
|
||||
}
|
||||
|
||||
/* Lower adjacent rate's throughput is measured */
|
||||
} else if (low_tpt != IWL_INVALID_VALUE) {
|
||||
/* Lower rate has better throughput */
|
||||
if (low_tpt > current_tpt) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Lower rate is better. "
|
||||
"Decrease rate\n");
|
||||
action = RS_ACTION_DOWNSCALE;
|
||||
} else if (sr >= IWL_RATE_INCREASE_TH) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Lower rate isn't better and "
|
||||
"good SR. Increase rate\n");
|
||||
action = RS_ACTION_UPSCALE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanity check; asked for decrease, but success rate or throughput
|
||||
* has been good at old rate. Don't change it.
|
||||
*/
|
||||
if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID) &&
|
||||
((sr > IWL_RATE_HIGH_TH) ||
|
||||
(current_tpt > (100 * tbl->expected_tpt[low])))) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Sanity check failed. Maintain rate\n");
|
||||
action = RS_ACTION_STAY;
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do rate scaling and search for new modulation mode.
|
||||
|
@ -1669,11 +1733,10 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
int low_tpt = IWL_INVALID_VALUE;
|
||||
int high_tpt = IWL_INVALID_VALUE;
|
||||
u32 fail_count;
|
||||
s8 scale_action = 0;
|
||||
enum rs_action scale_action = RS_ACTION_STAY;
|
||||
u16 rate_mask;
|
||||
u8 update_lq = 0;
|
||||
struct iwl_scale_tbl_info *tbl, *tbl1;
|
||||
u16 rate_scale_index_msk = 0;
|
||||
u8 active_tbl = 0;
|
||||
u8 done_search = 0;
|
||||
u16 high_low;
|
||||
|
@ -1690,8 +1753,6 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
info->flags & IEEE80211_TX_CTL_NO_ACK)
|
||||
return;
|
||||
|
||||
lq_sta->supp_rates = sta->supp_rates[lq_sta->band];
|
||||
|
||||
tid = rs_get_tid(lq_sta, hdr);
|
||||
if ((tid != IWL_MAX_TID_COUNT) &&
|
||||
(lq_sta->tx_agg_tid_en & (1 << tid))) {
|
||||
|
@ -1730,33 +1791,13 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
/* rates available for this association, and for modulation mode */
|
||||
rate_mask = rs_get_supported_rates(lq_sta, rate);
|
||||
|
||||
/* mask with station rate restriction */
|
||||
if (is_legacy(rate)) {
|
||||
if (lq_sta->band == IEEE80211_BAND_5GHZ)
|
||||
/* supp_rates has no CCK bits in A mode */
|
||||
rate_scale_index_msk = (u16) (rate_mask &
|
||||
(lq_sta->supp_rates << IWL_FIRST_OFDM_RATE));
|
||||
else
|
||||
rate_scale_index_msk = (u16) (rate_mask &
|
||||
lq_sta->supp_rates);
|
||||
|
||||
} else {
|
||||
rate_scale_index_msk = rate_mask;
|
||||
}
|
||||
|
||||
if (!rate_scale_index_msk)
|
||||
rate_scale_index_msk = rate_mask;
|
||||
|
||||
if (!((BIT(index) & rate_scale_index_msk))) {
|
||||
if (!(BIT(index) & rate_mask)) {
|
||||
IWL_ERR(mvm, "Current Rate is not valid\n");
|
||||
if (lq_sta->search_better_tbl) {
|
||||
/* revert to active table if search table is not valid*/
|
||||
rate->type = LQ_NONE;
|
||||
lq_sta->search_better_tbl = 0;
|
||||
tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
|
||||
/* get "active" rate info */
|
||||
index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
|
||||
tbl->rate.index = index;
|
||||
rs_update_rate_tbl(mvm, sta, lq_sta, &tbl->rate);
|
||||
}
|
||||
return;
|
||||
|
@ -1847,7 +1888,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
tbl = &(lq_sta->lq_info[active_tbl]);
|
||||
|
||||
/* Revert to "active" rate and throughput info */
|
||||
index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
|
||||
index = tbl->rate.index;
|
||||
current_tpt = lq_sta->last_tpt;
|
||||
|
||||
/* Need to set up a new rate table in uCode */
|
||||
|
@ -1863,8 +1904,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
|
||||
/* (Else) not in search of better modulation mode, try for better
|
||||
* starting rate, while staying in this mode. */
|
||||
high_low = rs_get_adjacent_rate(mvm, index, rate_scale_index_msk,
|
||||
rate->type);
|
||||
high_low = rs_get_adjacent_rate(mvm, index, rate_mask, rate->type);
|
||||
low = high_low & 0xff;
|
||||
high = (high_low >> 8) & 0xff;
|
||||
|
||||
|
@ -1887,85 +1927,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
rs_pretty_lq_type(rate->type), index, current_tpt, sr,
|
||||
low, high, low_tpt, high_tpt);
|
||||
|
||||
scale_action = 0;
|
||||
|
||||
/* Too many failures, decrease rate */
|
||||
if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"decrease rate because of low SR\n");
|
||||
scale_action = -1;
|
||||
/* No throughput measured yet for adjacent rates; try increase. */
|
||||
} else if ((low_tpt == IWL_INVALID_VALUE) &&
|
||||
(high_tpt == IWL_INVALID_VALUE)) {
|
||||
if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Good SR and no high rate measurement. "
|
||||
"Increase rate\n");
|
||||
scale_action = 1;
|
||||
} else if (low != IWL_RATE_INVALID) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Remain in current rate\n");
|
||||
scale_action = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Both adjacent throughputs are measured, but neither one has better
|
||||
* throughput; we're using the best rate, don't change it! */
|
||||
else if ((low_tpt != IWL_INVALID_VALUE) &&
|
||||
(high_tpt != IWL_INVALID_VALUE) &&
|
||||
(low_tpt < current_tpt) &&
|
||||
(high_tpt < current_tpt)) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Both high and low are worse. "
|
||||
"Maintain rate\n");
|
||||
scale_action = 0;
|
||||
}
|
||||
|
||||
/* At least one adjacent rate's throughput is measured,
|
||||
* and may have better performance. */
|
||||
else {
|
||||
/* Higher adjacent rate's throughput is measured */
|
||||
if (high_tpt != IWL_INVALID_VALUE) {
|
||||
/* Higher rate has better throughput */
|
||||
if (high_tpt > current_tpt &&
|
||||
sr >= IWL_RATE_INCREASE_TH) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Higher rate is better and good "
|
||||
"SR. Increate rate\n");
|
||||
scale_action = 1;
|
||||
} else {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Higher rate isn't better OR "
|
||||
"no good SR. Maintain rate\n");
|
||||
scale_action = 0;
|
||||
}
|
||||
|
||||
/* Lower adjacent rate's throughput is measured */
|
||||
} else if (low_tpt != IWL_INVALID_VALUE) {
|
||||
/* Lower rate has better throughput */
|
||||
if (low_tpt > current_tpt) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Lower rate is better. "
|
||||
"Decrease rate\n");
|
||||
scale_action = -1;
|
||||
} else if (sr >= IWL_RATE_INCREASE_TH) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Lower rate isn't better and "
|
||||
"good SR. Increase rate\n");
|
||||
scale_action = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanity check; asked for decrease, but success rate or throughput
|
||||
* has been good at old rate. Don't change it. */
|
||||
if ((scale_action == -1) && (low != IWL_RATE_INVALID) &&
|
||||
((sr > IWL_RATE_HIGH_TH) ||
|
||||
(current_tpt > (100 * tbl->expected_tpt[low])))) {
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"Sanity check failed. Maintain rate\n");
|
||||
scale_action = 0;
|
||||
}
|
||||
scale_action = rs_get_rate_action(mvm, tbl, sr, low, high,
|
||||
current_tpt, low_tpt, high_tpt);
|
||||
|
||||
/* Force a search in case BT doesn't like us being in MIMO */
|
||||
if (is_mimo(rate) &&
|
||||
|
@ -1977,7 +1940,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
}
|
||||
|
||||
switch (scale_action) {
|
||||
case -1:
|
||||
case RS_ACTION_DOWNSCALE:
|
||||
/* Decrease starting rate, update uCode's rate table */
|
||||
if (low != IWL_RATE_INVALID) {
|
||||
update_lq = 1;
|
||||
|
@ -1988,7 +1951,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
}
|
||||
|
||||
break;
|
||||
case 1:
|
||||
case RS_ACTION_UPSCALE:
|
||||
/* Increase starting rate, update uCode's rate table */
|
||||
if (high != IWL_RATE_INVALID) {
|
||||
update_lq = 1;
|
||||
|
@ -1999,7 +1962,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm,
|
|||
}
|
||||
|
||||
break;
|
||||
case 0:
|
||||
case RS_ACTION_STAY:
|
||||
/* No change */
|
||||
default:
|
||||
break;
|
||||
|
@ -2053,11 +2016,11 @@ lq_update:
|
|||
rs_rate_scale_clear_window(&(tbl->win[i]));
|
||||
|
||||
/* Use new "search" start rate */
|
||||
index = iwl_hwrate_to_plcp_idx(tbl->current_rate);
|
||||
index = tbl->rate.index;
|
||||
|
||||
rs_dump_rate(mvm, &tbl->rate,
|
||||
"Switch to SEARCH TABLE:");
|
||||
rs_fill_link_cmd(mvm, sta, lq_sta, tbl->current_rate);
|
||||
rs_fill_lq_cmd(mvm, sta, lq_sta, &tbl->rate);
|
||||
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, false);
|
||||
} else {
|
||||
done_search = 1;
|
||||
|
@ -2095,8 +2058,6 @@ lq_update:
|
|||
}
|
||||
|
||||
out:
|
||||
tbl->rate.index = index;
|
||||
tbl->current_rate = ucode_rate_from_rs_rate(mvm, &tbl->rate);
|
||||
lq_sta->last_txrate_idx = index;
|
||||
}
|
||||
|
||||
|
@ -2123,7 +2084,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
|
|||
struct iwl_scale_tbl_info *tbl;
|
||||
struct rs_rate *rate;
|
||||
int i;
|
||||
u32 ucode_rate;
|
||||
u8 active_tbl = 0;
|
||||
u8 valid_tx_ant;
|
||||
|
||||
|
@ -2154,9 +2114,6 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
|
|||
else
|
||||
rate->type = LQ_LEGACY_G;
|
||||
|
||||
ucode_rate = ucode_rate_from_rs_rate(mvm, rate);
|
||||
tbl->current_rate = ucode_rate;
|
||||
|
||||
WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B);
|
||||
if (rate->ant == ANT_A)
|
||||
tbl->column = RS_COLUMN_LEGACY_ANT_A;
|
||||
|
@ -2164,7 +2121,7 @@ static void rs_initialize_lq(struct iwl_mvm *mvm,
|
|||
tbl->column = RS_COLUMN_LEGACY_ANT_B;
|
||||
|
||||
rs_set_expected_tpt_table(lq_sta, tbl);
|
||||
rs_fill_link_cmd(NULL, NULL, lq_sta, ucode_rate);
|
||||
rs_fill_lq_cmd(NULL, NULL, lq_sta, rate);
|
||||
/* TODO restore station should remember the lq cmd */
|
||||
iwl_mvm_send_lq_cmd(mvm, &lq_sta->lq, init);
|
||||
}
|
||||
|
@ -2250,6 +2207,10 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
|
|||
if (i == IWL_RATE_9M_INDEX)
|
||||
continue;
|
||||
|
||||
/* Disable MCS9 as a workaround */
|
||||
if (i == IWL_RATE_MCS_9_INDEX)
|
||||
continue;
|
||||
|
||||
/* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
|
||||
if (i == IWL_RATE_MCS_9_INDEX &&
|
||||
sta->bandwidth == IEEE80211_STA_RX_BW_20)
|
||||
|
@ -2268,6 +2229,10 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta,
|
|||
if (i == IWL_RATE_9M_INDEX)
|
||||
continue;
|
||||
|
||||
/* Disable MCS9 as a workaround */
|
||||
if (i == IWL_RATE_MCS_9_INDEX)
|
||||
continue;
|
||||
|
||||
/* VHT MCS9 isn't valid for 20Mhz for NSS=1,2 */
|
||||
if (i == IWL_RATE_MCS_9_INDEX &&
|
||||
sta->bandwidth == IEEE80211_STA_RX_BW_20)
|
||||
|
@ -2306,7 +2271,6 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
|
|||
rs_rate_scale_clear_window(&lq_sta->lq_info[j].win[i]);
|
||||
|
||||
lq_sta->flush_timer = 0;
|
||||
lq_sta->supp_rates = sta->supp_rates[sband->band];
|
||||
|
||||
IWL_DEBUG_RATE(mvm,
|
||||
"LQ: *** rate scale station global init for station %d ***\n",
|
||||
|
@ -2395,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 @@ cancel:
|
|||
|
||||
int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
||||
if (test_bit(STATUS_FW_ERROR, &trans_pcie->status))
|
||||
return -EIO;
|
||||
|
||||
if (!(cmd->flags & CMD_SEND_IN_RFKILL) &&
|
||||
test_bit(STATUS_RFKILL, &trans_pcie->status)) {
|
||||
test_bit(STATUS_RFKILL, &trans->status)) {
|
||||
IWL_DEBUG_RF_KILL(trans, "Dropping CMD 0x%x: RF KILL\n",
|
||||
cmd->id);
|
||||
return -ERFKILL;
|
||||
|
|
|
@ -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 @@ unlock:
|
|||
sdata_unlock(sdata);
|
||||
}
|
||||
|
||||
static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct cfg80211_csa_settings *params)
|
||||
int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
||||
struct cfg80211_csa_settings *params)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
|
|
|
@ -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…
Reference in a new issue