summaryrefslogtreecommitdiff
path: root/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/at76c50x-usb.c3
-rw-r--r--drivers/net/wireless/ath/ath.h1
-rw-r--r--drivers/net/wireless/ath/ath10k/mac.c3
-rw-r--r--drivers/net/wireless/ath/ath5k/ath5k.h1
-rw-r--r--drivers/net/wireless/ath/ath5k/base.c31
-rw-r--r--drivers/net/wireless/ath/ath5k/mac80211-ops.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile3
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_hw.c15
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_phy.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/ar953x_initvals.h201
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h159
-rw-r--r--drivers/net/wireless/ath/ath9k/beacon.c97
-rw-r--r--drivers/net/wireless/ath/ath9k/channel.c685
-rw-r--r--drivers/net/wireless/ath/ath9k/common-beacon.c5
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c28
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c26
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/init.c57
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c6
-rw-r--r--drivers/net/wireless/ath/ath9k/main.c892
-rw-r--r--drivers/net/wireless/ath/ath9k/mci.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c16
-rw-r--r--drivers/net/wireless/ath/ath9k/reg.h4
-rw-r--r--drivers/net/wireless/ath/ath9k/tx99.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/wow.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c114
-rw-r--r--drivers/net/wireless/ath/wil6210/cfg80211.c68
-rw-r--r--drivers/net/wireless/ath/wil6210/debugfs.c305
-rw-r--r--drivers/net/wireless/ath/wil6210/interrupt.c3
-rw-r--r--drivers/net/wireless/ath/wil6210/main.c22
-rw-r--r--drivers/net/wireless/ath/wil6210/pcie_bus.c31
-rw-r--r--drivers/net/wireless/ath/wil6210/rx_reorder.c1
-rw-r--r--drivers/net/wireless/ath/wil6210/txrx.c33
-rw-r--r--drivers/net/wireless/ath/wil6210/wil6210.h10
-rw-r--r--drivers/net/wireless/ath/wil6210/wmi.c40
-rw-r--r--drivers/net/wireless/b43/Kconfig9
-rw-r--r--drivers/net/wireless/b43/Makefile6
-rw-r--r--drivers/net/wireless/b43/main.c311
-rw-r--r--drivers/net/wireless/b43/phy_a.c2
-rw-r--r--drivers/net/wireless/b43/phy_a.h4
-rw-r--r--drivers/net/wireless/b43/phy_common.c26
-rw-r--r--drivers/net/wireless/b43/phy_common.h12
-rw-r--r--drivers/net/wireless/b43/phy_ht.c2
-rw-r--r--drivers/net/wireless/b43/phy_n.c360
-rw-r--r--drivers/net/wireless/b43/radio_2057.c122
-rw-r--r--drivers/net/wireless/b43/radio_2057.h66
-rw-r--r--drivers/net/wireless/b43/tables_nphy.c470
-rw-r--r--drivers/net/wireless/b43/tables_nphy.h4
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/Makefile3
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/btcoex.c38
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd.h10
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c7
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/fwil.c2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/p2p.c2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/usb.c214
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/vendor.c115
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/vendor.h64
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c81
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h2
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c6
-rw-r--r--drivers/net/wireless/cw1200/scan.c3
-rw-r--r--drivers/net/wireless/cw1200/scan.h2
-rw-r--r--drivers/net/wireless/cw1200/sta.c3
-rw-r--r--drivers/net/wireless/ipw2x00/libipw_module.c3
-rw-r--r--drivers/net/wireless/iwlegacy/common.c3
-rw-r--r--drivers/net/wireless/iwlegacy/common.h2
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/mac80211.c3
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/power.c9
-rw-r--r--drivers/net/wireless/iwlwifi/dvm/rxon.c12
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-8000.c5
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-config.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.c15
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h50
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-file.h6
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw.h13
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-modparams.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-nvm-parse.c99
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-prph.h6
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/coex.c350
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/coex_legacy.c1332
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/constants.h2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs.c232
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h220
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-power.h2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h256
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h7
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h30
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api.h11
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c116
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c515
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h98
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/nvm.c31
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c114
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c26
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/power.c39
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/quota.c38
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.c96
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rs.h19
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/rx.c17
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/scan.c599
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.c74
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/sta.h12
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/time-event.c73
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/time-event.h29
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/tx.c25
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/utils.c65
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/drv.c3
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h7
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c145
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c1
-rw-r--r--drivers/net/wireless/libertas/Kconfig2
-rw-r--r--drivers/net/wireless/libertas/cmd.c1
-rw-r--r--drivers/net/wireless/mac80211_hwsim.c69
-rw-r--r--drivers/net/wireless/mwifiex/11ac.c2
-rw-r--r--drivers/net/wireless/mwifiex/11ac.h2
-rw-r--r--drivers/net/wireless/mwifiex/11h.c2
-rw-r--r--drivers/net/wireless/mwifiex/11n.c2
-rw-r--r--drivers/net/wireless/mwifiex/11n.h2
-rw-r--r--drivers/net/wireless/mwifiex/11n_aggr.c2
-rw-r--r--drivers/net/wireless/mwifiex/11n_aggr.h2
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.c2
-rw-r--r--drivers/net/wireless/mwifiex/11n_rxreorder.h2
-rw-r--r--drivers/net/wireless/mwifiex/Makefile2
-rw-r--r--drivers/net/wireless/mwifiex/README32
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.c55
-rw-r--r--drivers/net/wireless/mwifiex/cfg80211.h2
-rw-r--r--drivers/net/wireless/mwifiex/cfp.c2
-rw-r--r--drivers/net/wireless/mwifiex/cmdevt.c30
-rw-r--r--drivers/net/wireless/mwifiex/debugfs.c95
-rw-r--r--drivers/net/wireless/mwifiex/decl.h2
-rw-r--r--drivers/net/wireless/mwifiex/ethtool.c85
-rw-r--r--drivers/net/wireless/mwifiex/fw.h2
-rw-r--r--drivers/net/wireless/mwifiex/ie.c2
-rw-r--r--drivers/net/wireless/mwifiex/init.c15
-rw-r--r--drivers/net/wireless/mwifiex/ioctl.h2
-rw-r--r--drivers/net/wireless/mwifiex/join.c2
-rw-r--r--drivers/net/wireless/mwifiex/main.c8
-rw-r--r--drivers/net/wireless/mwifiex/main.h34
-rw-r--r--drivers/net/wireless/mwifiex/pcie.c195
-rw-r--r--drivers/net/wireless/mwifiex/pcie.h12
-rw-r--r--drivers/net/wireless/mwifiex/scan.c2
-rw-r--r--drivers/net/wireless/mwifiex/sdio.c240
-rw-r--r--drivers/net/wireless/mwifiex/sdio.h14
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmd.c2
-rw-r--r--drivers/net/wireless/mwifiex/sta_cmdresp.c2
-rw-r--r--drivers/net/wireless/mwifiex/sta_event.c2
-rw-r--r--drivers/net/wireless/mwifiex/sta_ioctl.c8
-rw-r--r--drivers/net/wireless/mwifiex/sta_rx.c2
-rw-r--r--drivers/net/wireless/mwifiex/sta_tx.c2
-rw-r--r--drivers/net/wireless/mwifiex/tdls.c8
-rw-r--r--drivers/net/wireless/mwifiex/txrx.c2
-rw-r--r--drivers/net/wireless/mwifiex/uap_cmd.c2
-rw-r--r--drivers/net/wireless/mwifiex/uap_event.c2
-rw-r--r--drivers/net/wireless/mwifiex/uap_txrx.c6
-rw-r--r--drivers/net/wireless/mwifiex/usb.c2
-rw-r--r--drivers/net/wireless/mwifiex/usb.h2
-rw-r--r--drivers/net/wireless/mwifiex/util.c2
-rw-r--r--drivers/net/wireless/mwifiex/util.h2
-rw-r--r--drivers/net/wireless/mwifiex/wmm.c11
-rw-r--r--drivers/net/wireless/mwifiex/wmm.h2
-rw-r--r--drivers/net/wireless/orinoco/Kconfig4
-rw-r--r--drivers/net/wireless/p54/p54spi.c2
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_core.c85
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_debugfs.c10
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mac80211.c143
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_mgmt.c239
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_pkt.c17
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio.c6
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_sdio_ops.c8
-rw-r--r--drivers/net/wireless/rsi/rsi_91x_usb.c88
-rw-r--r--drivers/net/wireless/rsi/rsi_main.h12
-rw-r--r--drivers/net/wireless/rsi/rsi_mgmt.h25
-rw-r--r--drivers/net/wireless/rsi/rsi_sdio.h8
-rw-r--r--drivers/net/wireless/rt2x00/rt2800lib.c69
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c10
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00mac.c20
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00mmio.c8
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c21
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.h1
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180/dev.c78
-rw-r--r--drivers/net/wireless/rtl818x/rtl8180/rtl8180.h1
-rw-r--r--drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/hw.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8188ee/reg.h1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/hw.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/reg.h1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192cu/hw.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/hw.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/phy.c3
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/reg.h1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/hw.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723ae/reg.h1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/hw.c2
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8723be/reg.h1
-rw-r--r--drivers/net/wireless/ti/wl1251/main.c3
-rw-r--r--drivers/net/wireless/ti/wl12xx/scan.c20
-rw-r--r--drivers/net/wireless/ti/wl12xx/scan.h2
-rw-r--r--drivers/net/wireless/ti/wl18xx/scan.c16
-rw-r--r--drivers/net/wireless/ti/wl18xx/scan.h2
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.c11
-rw-r--r--drivers/net/wireless/ti/wlcore/cmd.h3
-rw-r--r--drivers/net/wireless/ti/wlcore/main.c5
-rw-r--r--drivers/net/wireless/ti/wlcore/scan.h2
-rw-r--r--drivers/net/wireless/ti/wlcore/wlcore.h2
208 files changed, 9291 insertions, 2277 deletions
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index d48776e4f343..334c2ece855a 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -1955,8 +1955,9 @@ static void at76_dwork_hw_scan(struct work_struct *work)
static int at76_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req)
+ struct ieee80211_scan_request *hw_req)
{
+ struct cfg80211_scan_request *req = &hw_req->req;
struct at76_priv *priv = hw->priv;
struct at76_req_scan scan;
u8 *ssid = NULL;
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index a889fd66fc63..fd9e5305e77f 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -63,6 +63,7 @@ enum ath_op_flags {
ATH_OP_PRIM_STA_VIF,
ATH_OP_HW_RESET,
ATH_OP_SCANNING,
+ ATH_OP_MULTI_CHANNEL,
};
enum ath_bus_type {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index a21080028c54..b8314a534972 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3137,10 +3137,11 @@ exit:
static int ath10k_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req)
+ struct ieee80211_scan_request *hw_req)
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct cfg80211_scan_request *req = &hw_req->req;
struct wmi_start_scan_arg arg;
int ret = 0;
int i;
diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h
index 74bd54d6aceb..85316bb3f8c6 100644
--- a/drivers/net/wireless/ath/ath5k/ath5k.h
+++ b/drivers/net/wireless/ath/ath5k/ath5k.h
@@ -1285,6 +1285,7 @@ struct ath5k_hw {
#define ATH_STAT_STARTED 3 /* opened & irqs enabled */
unsigned int filter_flags; /* HW flags, AR5K_RX_FILTER_* */
+ unsigned int fif_filter_flags; /* Current FIF_* filter flags */
struct ieee80211_channel *curchan; /* current h/w channel */
u16 nvifs;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 4b18434ba697..8ad2550bce7f 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -1382,6 +1382,9 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb,
rxs->flag = 0;
if (unlikely(rs->rs_status & AR5K_RXERR_MIC))
rxs->flag |= RX_FLAG_MMIC_ERROR;
+ if (unlikely(rs->rs_status & AR5K_RXERR_CRC))
+ rxs->flag |= RX_FLAG_FAILED_FCS_CRC;
+
/*
* always extend the mac timestamp, since this information is
@@ -1449,6 +1452,8 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
ah->stats.rx_bytes_count += rs->rs_datalen;
if (unlikely(rs->rs_status)) {
+ unsigned int filters;
+
if (rs->rs_status & AR5K_RXERR_CRC)
ah->stats.rxerr_crc++;
if (rs->rs_status & AR5K_RXERR_FIFO)
@@ -1457,7 +1462,20 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
ah->stats.rxerr_phy++;
if (rs->rs_phyerr > 0 && rs->rs_phyerr < 32)
ah->stats.rxerr_phy_code[rs->rs_phyerr]++;
- return false;
+
+ /*
+ * Treat packets that underwent a CCK or OFDM reset as having a bad CRC.
+ * These restarts happen when the radio resynchronizes to a stronger frame
+ * while receiving a weaker frame. Here we receive the prefix of the weak
+ * frame. Since these are incomplete packets, mark their CRC as invalid.
+ */
+ if (rs->rs_phyerr == AR5K_RX_PHY_ERROR_OFDM_RESTART ||
+ rs->rs_phyerr == AR5K_RX_PHY_ERROR_CCK_RESTART) {
+ rs->rs_status |= AR5K_RXERR_CRC;
+ rs->rs_status &= ~AR5K_RXERR_PHY;
+ } else {
+ return false;
+ }
}
if (rs->rs_status & AR5K_RXERR_DECRYPT) {
/*
@@ -1480,8 +1498,15 @@ ath5k_receive_frame_ok(struct ath5k_hw *ah, struct ath5k_rx_status *rs)
return true;
}
- /* reject any frames with non-crypto errors */
- if (rs->rs_status & ~(AR5K_RXERR_DECRYPT))
+ /*
+ * Reject any frames with non-crypto errors, and take into account the
+ * current FIF_* filters.
+ */
+ filters = AR5K_RXERR_DECRYPT;
+ if (ah->fif_filter_flags & FIF_FCSFAIL)
+ filters |= AR5K_RXERR_CRC;
+
+ if (rs->rs_status & ~filters)
return false;
}
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index afb23b3cc7be..b65c38fdaa4b 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -473,6 +473,8 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,
/* Set the cached hw filter flags, this will later actually
* be set in HW */
ah->filter_flags = rfilt;
+ /* Store current FIF filter flags */
+ ah->fif_filter_flags = *new_flags;
mutex_unlock(&ah->lock);
}
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 8fcd586d1c39..6b4020a57984 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -5,7 +5,8 @@ ath9k-y += beacon.o \
recv.o \
xmit.o \
link.o \
- antenna.o
+ antenna.o \
+ channel.o
ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
ath9k-$(CONFIG_ATH9K_PCI) += pci.o
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 235053ba7737..80c6eacbda53 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -3535,7 +3535,8 @@ static void ar9003_hw_xpa_bias_level_apply(struct ath_hw *ah, bool is2ghz)
{
int bias = ar9003_modal_header(ah, is2ghz)->xpaBiasLvl;
- if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah))
+ if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah) ||
+ AR_SREV_9531(ah))
REG_RMW_FIELD(ah, AR_CH0_TOP2, AR_CH0_TOP2_XPABIASLVL, bias);
else if (AR_SREV_9462(ah) || AR_SREV_9550(ah) || AR_SREV_9565(ah))
REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
index ec1da0cc25f5..ddef9eedbac6 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c
@@ -314,10 +314,17 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah)
qca953x_1p0_mac_core);
INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST],
qca953x_1p0_mac_postamble);
- INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
- qca953x_1p0_baseband_core);
- INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
- qca953x_1p0_baseband_postamble);
+ if (AR_SREV_9531_20(ah)) {
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+ qca953x_2p0_baseband_core);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+ qca953x_2p0_baseband_postamble);
+ } else {
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE],
+ qca953x_1p0_baseband_core);
+ INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST],
+ qca953x_1p0_baseband_postamble);
+ }
INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE],
qca953x_1p0_radio_core);
INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST],
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 8927fc34d84c..542a8d51d3b0 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -1552,13 +1552,15 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
u8 *ini_reloaded)
{
unsigned int regWrites = 0;
- u32 modesIndex;
+ u32 modesIndex, txgain_index;
if (IS_CHAN_5GHZ(chan))
modesIndex = IS_CHAN_HT40(chan) ? 2 : 1;
else
modesIndex = IS_CHAN_HT40(chan) ? 3 : 4;
+ txgain_index = AR_SREV_9531(ah) ? 1 : modesIndex;
+
if (modesIndex == ah->modes_index) {
*ini_reloaded = false;
goto set_rfmode;
@@ -1573,7 +1575,7 @@ static int ar9003_hw_fast_chan_change(struct ath_hw *ah,
ar9003_hw_prog_ini(ah, &ah->ini_radio_post_sys2ant,
modesIndex);
- REG_WRITE_ARRAY(&ah->iniModesTxGain, modesIndex, regWrites);
+ REG_WRITE_ARRAY(&ah->iniModesTxGain, txgain_index, regWrites);
if (AR_SREV_9462_20_OR_LATER(ah)) {
/*
diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
index 8e5c3b9786e3..812a9d787bf3 100644
--- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
+++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h
@@ -219,7 +219,7 @@ static const u32 qca953x_1p0_baseband_core[][2] = {
{0x00009d04, 0x40206c10},
{0x00009d08, 0x009c4060},
{0x00009d0c, 0x9883800a},
- {0x00009d10, 0x01884061},
+ {0x00009d10, 0x018848c6},
{0x00009d14, 0x00c0040b},
{0x00009d18, 0x00000000},
{0x00009e08, 0x0038230c},
@@ -715,4 +715,203 @@ static const u32 qca953x_1p1_modes_no_xpa_tx_gain_table[][2] = {
{0x00016448, 0x6c927a70},
};
+static const u32 qca953x_2p0_baseband_core[][2] = {
+ /* Addr allmodes */
+ {0x00009800, 0xafe68e30},
+ {0x00009804, 0xfd14e000},
+ {0x00009808, 0x9c0a9f6b},
+ {0x0000980c, 0x04900000},
+ {0x00009814, 0x0280c00a},
+ {0x00009818, 0x00000000},
+ {0x0000981c, 0x00020028},
+ {0x00009834, 0x6400a190},
+ {0x00009838, 0x0108ecff},
+ {0x0000983c, 0x14000600},
+ {0x00009880, 0x201fff00},
+ {0x00009884, 0x00001042},
+ {0x000098a4, 0x00200400},
+ {0x000098b0, 0x32840bbe},
+ {0x000098bc, 0x00000002},
+ {0x000098d0, 0x004b6a8e},
+ {0x000098d4, 0x00000820},
+ {0x000098dc, 0x00000000},
+ {0x000098f0, 0x00000000},
+ {0x000098f4, 0x00000000},
+ {0x00009c04, 0xff55ff55},
+ {0x00009c08, 0x0320ff55},
+ {0x00009c0c, 0x00000000},
+ {0x00009c10, 0x00000000},
+ {0x00009c14, 0x00046384},
+ {0x00009c18, 0x05b6b440},
+ {0x00009c1c, 0x00b6b440},
+ {0x00009d00, 0xc080a333},
+ {0x00009d04, 0x40206c10},
+ {0x00009d08, 0x009c4060},
+ {0x00009d0c, 0x9883800a},
+ {0x00009d10, 0x018848c6},
+ {0x00009d14, 0x00c0040b},
+ {0x00009d18, 0x00000000},
+ {0x00009e08, 0x0038230c},
+ {0x00009e24, 0x990bb515},
+ {0x00009e28, 0x0c6f0000},
+ {0x00009e30, 0x06336f77},
+ {0x00009e34, 0x6af6532f},
+ {0x00009e38, 0x0cc80c00},
+ {0x00009e40, 0x0d261820},
+ {0x00009e4c, 0x00001004},
+ {0x00009e50, 0x00ff03f1},
+ {0x00009fc0, 0x813e4788},
+ {0x00009fc4, 0x0001efb5},
+ {0x00009fcc, 0x40000014},
+ {0x00009fd0, 0x02993b93},
+ {0x0000a20c, 0x00000000},
+ {0x0000a220, 0x00000000},
+ {0x0000a224, 0x00000000},
+ {0x0000a228, 0x10002310},
+ {0x0000a23c, 0x00000000},
+ {0x0000a244, 0x0c000000},
+ {0x0000a248, 0x00000140},
+ {0x0000a2a0, 0x00000007},
+ {0x0000a2c0, 0x00000007},
+ {0x0000a2c8, 0x00000000},
+ {0x0000a2d4, 0x00000000},
+ {0x0000a2ec, 0x00000000},
+ {0x0000a2f0, 0x00000000},
+ {0x0000a2f4, 0x00000000},
+ {0x0000a2f8, 0x00000000},
+ {0x0000a344, 0x00000000},
+ {0x0000a34c, 0x00000000},
+ {0x0000a350, 0x0000a000},
+ {0x0000a364, 0x00000000},
+ {0x0000a370, 0x00000000},
+ {0x0000a390, 0x00000001},
+ {0x0000a394, 0x00000444},
+ {0x0000a398, 0x001f0e0f},
+ {0x0000a39c, 0x0075393f},
+ {0x0000a3a0, 0xb79f6427},
+ {0x0000a3a4, 0x000400ff},
+ {0x0000a3a8, 0x6a6a6a6a},
+ {0x0000a3ac, 0x6a6a6a6a},
+ {0x0000a3b0, 0x00c8641a},
+ {0x0000a3b4, 0x0000001a},
+ {0x0000a3b8, 0x0088642a},
+ {0x0000a3bc, 0x000001fa},
+ {0x0000a3c0, 0x20202020},
+ {0x0000a3c4, 0x22222220},
+ {0x0000a3c8, 0x20200020},
+ {0x0000a3cc, 0x20202020},
+ {0x0000a3d0, 0x20202020},
+ {0x0000a3d4, 0x20202020},
+ {0x0000a3d8, 0x20202020},
+ {0x0000a3dc, 0x20202020},
+ {0x0000a3e0, 0x20202020},
+ {0x0000a3e4, 0x20202020},
+ {0x0000a3e8, 0x20202020},
+ {0x0000a3ec, 0x20202020},
+ {0x0000a3f0, 0x00000000},
+ {0x0000a3f4, 0x00000000},
+ {0x0000a3f8, 0x0c9bd380},
+ {0x0000a3fc, 0x000f0f01},
+ {0x0000a400, 0x8fa91f01},
+ {0x0000a404, 0x00000000},
+ {0x0000a408, 0x0e79e5c6},
+ {0x0000a40c, 0x00820820},
+ {0x0000a414, 0x1ce42108},
+ {0x0000a418, 0x2d001dce},
+ {0x0000a41c, 0x1ce73908},
+ {0x0000a420, 0x000001ce},
+ {0x0000a424, 0x1ce738e7},
+ {0x0000a428, 0x000001ce},
+ {0x0000a42c, 0x1ce739ce},
+ {0x0000a430, 0x1ce739ce},
+ {0x0000a434, 0x00000000},
+ {0x0000a438, 0x00001801},
+ {0x0000a43c, 0x00100000},
+ {0x0000a444, 0x00000000},
+ {0x0000a448, 0x05000080},
+ {0x0000a44c, 0x00000001},
+ {0x0000a450, 0x00010000},
+ {0x0000a458, 0x00000000},
+ {0x0000a644, 0xbfad9d74},
+ {0x0000a648, 0x0048060a},
+ {0x0000a64c, 0x00003c37},
+ {0x0000a670, 0x03020100},
+ {0x0000a674, 0x09080504},
+ {0x0000a678, 0x0d0c0b0a},
+ {0x0000a67c, 0x13121110},
+ {0x0000a680, 0x31301514},
+ {0x0000a684, 0x35343332},
+ {0x0000a688, 0x00000036},
+ {0x0000a690, 0x08000838},
+ {0x0000a7cc, 0x00000000},
+ {0x0000a7d0, 0x00000000},
+ {0x0000a7d4, 0x00000004},
+ {0x0000a7dc, 0x00000000},
+ {0x0000a8d0, 0x004b6a8e},
+ {0x0000a8d4, 0x00000820},
+ {0x0000a8dc, 0x00000000},
+ {0x0000a8f0, 0x00000000},
+ {0x0000a8f4, 0x00000000},
+ {0x0000b2d0, 0x00000080},
+ {0x0000b2d4, 0x00000000},
+ {0x0000b2ec, 0x00000000},
+ {0x0000b2f0, 0x00000000},
+ {0x0000b2f4, 0x00000000},
+ {0x0000b2f8, 0x00000000},
+ {0x0000b408, 0x0e79e5c0},
+ {0x0000b40c, 0x00820820},
+ {0x0000b420, 0x00000000},
+};
+
+static const u32 qca953x_2p0_baseband_postamble[][5] = {
+ /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */
+ {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011},
+ {0x00009820, 0x206a022e, 0x206a022e, 0x206a012e, 0x206a012e},
+ {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0},
+ {0x00009828, 0x06903081, 0x06903081, 0x06903881, 0x06903881},
+ {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4},
+ {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c},
+ {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4},
+ {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0},
+ {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020},
+ {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2},
+ {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec84d2e},
+ {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x3379605e, 0x33795d5e},
+ {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c},
+ {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce},
+ {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021},
+ {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946222, 0xcf946222},
+ {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27},
+ {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012},
+ {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000},
+ {0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x005c0ec4, 0x005c0ec0},
+ {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004},
+ {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f},
+ {0x0000a230, 0x0000000a, 0x00000014, 0x00000016, 0x0000000b},
+ {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff},
+ {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018},
+ {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108},
+ {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898},
+ {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002},
+ {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e},
+ {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501},
+ {0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e},
+ {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b},
+ {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+ {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110},
+ {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222},
+ {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18},
+ {0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33},
+ {0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982},
+ {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b},
+ {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+ {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000},
+ {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000},
+ {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c},
+ {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001ce, 0x000001ce},
+ {0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010},
+};
+
#endif /* INITVALS_953X_H */
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 2ca8f7e06174..11b5e4dd6294 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -22,6 +22,7 @@
#include <linux/interrupt.h>
#include <linux/leds.h>
#include <linux/completion.h>
+#include <linux/time.h>
#include "common.h"
#include "debug.h"
@@ -35,10 +36,7 @@ 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;
-};
+extern int ath9k_use_chanctx;
/*************************/
/* Descriptor Management */
@@ -167,7 +165,6 @@ struct ath_txq {
u32 axq_ampdu_depth;
bool stopped;
bool axq_tx_inprogress;
- struct list_head axq_acq;
struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
u8 txq_headidx;
u8 txq_tailidx;
@@ -280,8 +277,9 @@ struct ath_node {
struct ath_tx_control {
struct ath_txq *txq;
struct ath_node *an;
- u8 paprd;
struct ieee80211_sta *sta;
+ u8 paprd;
+ bool force_channel;
};
@@ -325,6 +323,116 @@ struct ath_rx {
u32 ampdu_ref;
};
+struct ath_chanctx {
+ struct cfg80211_chan_def chandef;
+ struct list_head vifs;
+ struct list_head acq[IEEE80211_NUM_ACS];
+ int hw_queue_base;
+
+ /* do not dereference, use for comparison only */
+ struct ieee80211_vif *primary_sta;
+
+ struct ath_beacon_config beacon;
+ struct ath9k_hw_cal_data caldata;
+ struct timespec tsf_ts;
+ u64 tsf_val;
+ u32 last_beacon;
+
+ u16 txpower;
+ bool offchannel;
+ bool stopped;
+ bool active;
+ bool assigned;
+ bool switch_after_beacon;
+};
+
+enum ath_chanctx_event {
+ ATH_CHANCTX_EVENT_BEACON_PREPARE,
+ ATH_CHANCTX_EVENT_BEACON_SENT,
+ ATH_CHANCTX_EVENT_TSF_TIMER,
+ ATH_CHANCTX_EVENT_BEACON_RECEIVED,
+ ATH_CHANCTX_EVENT_ASSOC,
+ ATH_CHANCTX_EVENT_SWITCH,
+ ATH_CHANCTX_EVENT_UNASSIGN,
+ ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL,
+};
+
+enum ath_chanctx_state {
+ ATH_CHANCTX_STATE_IDLE,
+ ATH_CHANCTX_STATE_WAIT_FOR_BEACON,
+ ATH_CHANCTX_STATE_WAIT_FOR_TIMER,
+ ATH_CHANCTX_STATE_SWITCH,
+ ATH_CHANCTX_STATE_FORCE_ACTIVE,
+};
+
+struct ath_chanctx_sched {
+ bool beacon_pending;
+ bool offchannel_pending;
+ enum ath_chanctx_state state;
+ u8 beacon_miss;
+
+ u32 next_tbtt;
+ u32 switch_start_time;
+ unsigned int offchannel_duration;
+ unsigned int channel_switch_time;
+
+ /* backup, in case the hardware timer fails */
+ struct timer_list timer;
+};
+
+enum ath_offchannel_state {
+ ATH_OFFCHANNEL_IDLE,
+ ATH_OFFCHANNEL_PROBE_SEND,
+ ATH_OFFCHANNEL_PROBE_WAIT,
+ ATH_OFFCHANNEL_SUSPEND,
+ ATH_OFFCHANNEL_ROC_START,
+ ATH_OFFCHANNEL_ROC_WAIT,
+ ATH_OFFCHANNEL_ROC_DONE,
+};
+
+struct ath_offchannel {
+ struct ath_chanctx chan;
+ struct timer_list timer;
+ struct cfg80211_scan_request *scan_req;
+ struct ieee80211_vif *scan_vif;
+ int scan_idx;
+ enum ath_offchannel_state state;
+ struct ieee80211_channel *roc_chan;
+ struct ieee80211_vif *roc_vif;
+ int roc_duration;
+ int duration;
+};
+#define ath_for_each_chanctx(_sc, _ctx) \
+ for (ctx = &sc->chanctx[0]; \
+ ctx <= &sc->chanctx[ARRAY_SIZE(sc->chanctx) - 1]; \
+ ctx++)
+
+void ath9k_fill_chanctx_ops(void);
+void ath9k_chanctx_force_active(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+static inline struct ath_chanctx *
+ath_chanctx_get(struct ieee80211_chanctx_conf *ctx)
+{
+ struct ath_chanctx **ptr = (void *) ctx->drv_priv;
+ return *ptr;
+}
+void ath_chanctx_init(struct ath_softc *sc);
+void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
+ struct cfg80211_chan_def *chandef);
+void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
+ struct cfg80211_chan_def *chandef);
+void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx);
+void ath_offchannel_timer(unsigned long data);
+void ath_offchannel_channel_change(struct ath_softc *sc);
+void ath_chanctx_offchan_switch(struct ath_softc *sc,
+ struct ieee80211_channel *chan);
+struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc,
+ bool active);
+void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
+ enum ath_chanctx_event ev);
+void ath_chanctx_timer(unsigned long data);
+
+int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan);
int ath_startrecv(struct ath_softc *sc);
bool ath_stoprecv(struct ath_softc *sc);
u32 ath_calcrxfilter(struct ath_softc *sc);
@@ -341,6 +449,7 @@ void ath_draintxq(struct ath_softc *sc, struct ath_txq *txq);
void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an);
void ath_tx_node_cleanup(struct ath_softc *sc, struct ath_node *an);
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq);
+void ath_txq_schedule_all(struct ath_softc *sc);
int ath_tx_init(struct ath_softc *sc, int nbufs);
int ath_txq_update(struct ath_softc *sc, int qnum,
struct ath9k_tx_queue_info *q);
@@ -370,32 +479,47 @@ void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
/********/
struct ath_vif {
+ struct list_head list;
+
struct ieee80211_vif *vif;
struct ath_node mcast_node;
int av_bslot;
- bool primary_sta_vif;
__le64 tsf_adjust; /* TSF adjustment for staggered beacons */
struct ath_buf *av_bcbuf;
+ struct ath_chanctx *chanctx;
/* P2P Client */
struct ieee80211_noa_data noa;
+
+ /* P2P GO */
+ u8 noa_index;
+ u32 offchannel_start;
+ u32 offchannel_duration;
+
+ u32 periodic_noa_start;
+ u32 periodic_noa_duration;
};
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;
+ u8 slottime;
+ bool beacons;
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 */
+ struct ieee80211_vif *primary_sta;
};
-void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
+void ath9k_calculate_iter_data(struct ath_softc *sc,
+ struct ath_chanctx *ctx,
struct ath9k_vif_iter_data *iter_data);
+void ath9k_calculate_summary_state(struct ath_softc *sc,
+ struct ath_chanctx *ctx);
/*******************/
/* Beacon Handling */
@@ -458,6 +582,7 @@ void ath9k_csa_update(struct ath_softc *sc);
#define ATH_PAPRD_TIMEOUT 100 /* msecs */
#define ATH_PLL_WORK_INTERVAL 100
+void ath_chanctx_work(struct work_struct *work);
void ath_tx_complete_poll_work(struct work_struct *work);
void ath_reset_work(struct work_struct *work);
bool ath_hw_check(struct ath_softc *sc);
@@ -473,6 +598,7 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type);
void ath_ps_full_sleep(unsigned long data);
void ath9k_p2p_ps_timer(void *priv);
void ath9k_update_p2p_ps(struct ath_softc *sc, struct ieee80211_vif *vif);
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop);
/**********/
/* BTCOEX */
@@ -702,6 +828,8 @@ void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs);
#define PS_BEACON_SYNC BIT(4)
#define PS_WAIT_FOR_ANI BIT(5)
+#define ATH9K_NUM_CHANCTX 2 /* supports 2 operating channels */
+
struct ath_softc {
struct ieee80211_hw *hw;
struct device *dev;
@@ -720,6 +848,7 @@ struct ath_softc {
struct mutex mutex;
struct work_struct paprd_work;
struct work_struct hw_reset_work;
+ struct work_struct chanctx_work;
struct completion paprd_complete;
wait_queue_head_t tx_wait;
@@ -738,23 +867,27 @@ struct ath_softc {
short nvifs;
unsigned long ps_usecount;
- struct ath_config config;
struct ath_rx rx;
struct ath_tx tx;
struct ath_beacon beacon;
+ struct cfg80211_chan_def cur_chandef;
+ struct ath_chanctx chanctx[ATH9K_NUM_CHANCTX];
+ struct ath_chanctx *cur_chan;
+ struct ath_chanctx *next_chan;
+ spinlock_t chan_lock;
+ struct ath_offchannel offchannel;
+ struct ath_chanctx_sched sched;
+
#ifdef CONFIG_MAC80211_LEDS
bool led_registered;
char led_name[32];
struct led_classdev led_cdev;
#endif
- struct ath9k_hw_cal_data caldata;
-
#ifdef CONFIG_ATH9K_DEBUGFS
struct ath9k_debug debug;
#endif
- struct ath_beacon_config cur_beacon_conf;
struct delayed_work tx_complete_work;
struct delayed_work hw_pll_work;
struct timer_list sleep_timer;
diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c
index e387f0b2954a..eaf8f058c151 100644
--- a/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/drivers/net/wireless/ath/ath9k/beacon.c
@@ -80,7 +80,7 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
u8 chainmask = ah->txchainmask;
u8 rate = 0;
- sband = &common->sbands[common->hw->conf.chandef.chan->band];
+ sband = &common->sbands[sc->cur_chandef.chan->band];
rate = sband->bitrates[rateidx].hw_value;
if (vif->bss_conf.use_short_preamble)
rate |= sband->bitrates[rateidx].hw_value_short;
@@ -108,6 +108,55 @@ static void ath9k_beacon_setup(struct ath_softc *sc, struct ieee80211_vif *vif,
ath9k_hw_set_txdesc(ah, bf->bf_desc, &info);
}
+static void ath9k_beacon_add_noa(struct ath_softc *sc, struct ath_vif *avp,
+ struct sk_buff *skb)
+{
+ static const u8 noa_ie_hdr[] = {
+ WLAN_EID_VENDOR_SPECIFIC, /* type */
+ 0, /* length */
+ 0x50, 0x6f, 0x9a, /* WFA OUI */
+ 0x09, /* P2P subtype */
+ 0x0c, /* Notice of Absence */
+ 0x00, /* LSB of little-endian len */
+ 0x00, /* MSB of little-endian len */
+ };
+
+ struct ieee80211_p2p_noa_attr *noa;
+ int noa_len, noa_desc, i = 0;
+ u8 *hdr;
+
+ if (!avp->offchannel_duration && !avp->periodic_noa_duration)
+ return;
+
+ noa_desc = !!avp->offchannel_duration + !!avp->periodic_noa_duration;
+ noa_len = 2 + sizeof(struct ieee80211_p2p_noa_desc) * noa_desc;
+
+ hdr = skb_put(skb, sizeof(noa_ie_hdr));
+ memcpy(hdr, noa_ie_hdr, sizeof(noa_ie_hdr));
+ hdr[1] = sizeof(noa_ie_hdr) + noa_len - 2;
+ hdr[7] = noa_len;
+
+ noa = (void *) skb_put(skb, noa_len);
+ memset(noa, 0, noa_len);
+
+ noa->index = avp->noa_index;
+ if (avp->periodic_noa_duration) {
+ u32 interval = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
+
+ noa->desc[i].count = 255;
+ noa->desc[i].start_time = cpu_to_le32(avp->periodic_noa_start);
+ noa->desc[i].duration = cpu_to_le32(avp->periodic_noa_duration);
+ noa->desc[i].interval = cpu_to_le32(interval);
+ i++;
+ }
+
+ if (avp->offchannel_duration) {
+ noa->desc[i].count = 1;
+ noa->desc[i].start_time = cpu_to_le32(avp->offchannel_start);
+ noa->desc[i].duration = cpu_to_le32(avp->offchannel_duration);
+ }
+}
+
static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
@@ -155,6 +204,9 @@ static struct ath_buf *ath9k_beacon_generate(struct ieee80211_hw *hw,
hdr->seq_ctrl |= cpu_to_le16(sc->tx.seq_no);
}
+ if (vif->p2p)
+ ath9k_beacon_add_noa(sc, avp, skb);
+
bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
skb->len, DMA_TO_DEVICE);
if (unlikely(dma_mapping_error(sc->dev, bf->bf_buf_addr))) {
@@ -249,7 +301,7 @@ void ath9k_beacon_remove_slot(struct ath_softc *sc, struct ieee80211_vif *vif)
static int ath9k_beacon_choose_slot(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+ struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
u16 intval;
u32 tsftu;
u64 tsf;
@@ -277,8 +329,8 @@ static int ath9k_beacon_choose_slot(struct ath_softc *sc)
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;
+ struct ath_beacon_config *cur_conf = &avp->chanctx->beacon;
u32 tsfadjust;
if (avp->av_bslot == 0)
@@ -374,12 +426,19 @@ void ath9k_beacon_tasklet(unsigned long data)
vif = sc->beacon.bslot[slot];
/* EDMA devices check that in the tx completion function. */
- if (!edma && ath9k_csa_is_finished(sc, vif))
- return;
+ if (!edma) {
+ if (sc->sched.beacon_pending)
+ ath_chanctx_event(sc, NULL,
+ ATH_CHANCTX_EVENT_BEACON_SENT);
+
+ if (ath9k_csa_is_finished(sc, vif))
+ return;
+ }
if (!vif || !vif->bss_conf.enable_beacon)
return;
+ ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_BEACON_PREPARE);
bf = ath9k_beacon_generate(sc->hw, vif);
if (sc->beacon.bmisscnt != 0) {
@@ -500,7 +559,6 @@ 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;
if (sc->sc_ah->opmode == NL80211_IFTYPE_AP) {
if ((vif->type != NL80211_IFTYPE_AP) ||
@@ -514,7 +572,7 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
if (sc->sc_ah->opmode == NL80211_IFTYPE_STATION) {
if ((vif->type == NL80211_IFTYPE_STATION) &&
test_bit(ATH_OP_BEACONS, &common->op_flags) &&
- !avp->primary_sta_vif) {
+ vif != sc->cur_chan->primary_sta) {
ath_dbg(common, CONFIG,
"Beacon already configured for a station interface\n");
return false;
@@ -525,10 +583,11 @@ static bool ath9k_allow_beacon_config(struct ath_softc *sc,
}
static void ath9k_cache_beacon_config(struct ath_softc *sc,
+ struct ath_chanctx *ctx,
struct ieee80211_bss_conf *bss_conf)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+ struct ath_beacon_config *cur_conf = &ctx->beacon;
ath_dbg(common, BEACON,
"Caching beacon data for BSS: %pM\n", bss_conf->bssid);
@@ -564,20 +623,29 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
u32 changed)
{
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
- struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ struct ath_chanctx *ctx = avp->chanctx;
+ struct ath_beacon_config *cur_conf;
unsigned long flags;
bool skip_beacon = false;
+ if (!ctx)
+ return;
+
+ cur_conf = &avp->chanctx->beacon;
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);
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ ath9k_cache_beacon_config(sc, ctx, bss_conf);
+ if (ctx != sc->cur_chan)
+ return;
+
ath9k_set_beacon(sc);
set_bit(ATH_OP_BEACONS, &common->op_flags);
return;
@@ -593,10 +661,13 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
cur_conf->enable_beacon = false;
} else if (bss_conf->enable_beacon) {
cur_conf->enable_beacon = true;
- ath9k_cache_beacon_config(sc, bss_conf);
+ ath9k_cache_beacon_config(sc, ctx, bss_conf);
}
}
+ if (ctx != sc->cur_chan)
+ return;
+
/*
* Configure the HW beacon registers only when we have a valid
* beacon interval.
@@ -631,7 +702,7 @@ void ath9k_beacon_config(struct ath_softc *sc, struct ieee80211_vif *vif,
void ath9k_set_beacon(struct ath_softc *sc)
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+ struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
switch (sc->sc_ah->opmode) {
case NL80211_IFTYPE_AP:
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c
new file mode 100644
index 000000000000..ba214ebdcd16
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -0,0 +1,685 @@
+/*
+ * Copyright (c) 2014 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 "ath9k.h"
+
+/* Set/change channels. If the channel is really being changed, it's done
+ * by reseting the chip. To accomplish this we must first cleanup any pending
+ * DMA, then restart stuff.
+ */
+static int ath_set_channel(struct ath_softc *sc)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ieee80211_hw *hw = sc->hw;
+ struct ath9k_channel *hchan;
+ struct cfg80211_chan_def *chandef = &sc->cur_chan->chandef;
+ struct ieee80211_channel *chan = chandef->chan;
+ int pos = chan->hw_value;
+ int old_pos = -1;
+ int r;
+
+ if (test_bit(ATH_OP_INVALID, &common->op_flags))
+ return -EIO;
+
+ if (ah->curchan)
+ old_pos = ah->curchan - &ah->channels[0];
+
+ ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
+ chan->center_freq, chandef->width);
+
+ /* update survey stats for the old channel before switching */
+ spin_lock_bh(&common->cc_lock);
+ ath_update_survey_stats(sc);
+ spin_unlock_bh(&common->cc_lock);
+
+ ath9k_cmn_get_channel(hw, ah, chandef);
+
+ /* If the operating channel changes, change the survey in-use flags
+ * along with it.
+ * Reset the survey data for the new channel, unless we're switching
+ * back to the operating channel from an off-channel operation.
+ */
+ if (!sc->cur_chan->offchannel && sc->cur_survey != &sc->survey[pos]) {
+ if (sc->cur_survey)
+ sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
+
+ sc->cur_survey = &sc->survey[pos];
+
+ memset(sc->cur_survey, 0, sizeof(struct survey_info));
+ sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
+ } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
+ memset(&sc->survey[pos], 0, sizeof(struct survey_info));
+ }
+
+ hchan = &sc->sc_ah->channels[pos];
+ r = ath_reset_internal(sc, hchan);
+ if (r)
+ return r;
+
+ /* The most recent snapshot of channel->noisefloor for the old
+ * channel is only available after the hardware reset. Copy it to
+ * the survey stats now.
+ */
+ if (old_pos >= 0)
+ ath_update_survey_nf(sc, old_pos);
+
+ /* Enable radar pulse detection if on a DFS channel. Spectral
+ * scanning and radar detection can not be used concurrently.
+ */
+ if (hw->conf.radar_enabled) {
+ u32 rxfilter;
+
+ /* set HW specific DFS configuration */
+ ath9k_hw_set_radar_params(ah);
+ rxfilter = ath9k_hw_getrxfilter(ah);
+ rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
+ ATH9K_RX_FILTER_PHYERR;
+ ath9k_hw_setrxfilter(ah, rxfilter);
+ ath_dbg(common, DFS, "DFS enabled at freq %d\n",
+ chan->center_freq);
+ } else {
+ /* perform spectral scan if requested. */
+ if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
+ sc->spectral_mode == SPECTRAL_CHANSCAN)
+ ath9k_spectral_scan_trigger(hw);
+ }
+
+ return 0;
+}
+
+static bool
+ath_chanctx_send_vif_ps_frame(struct ath_softc *sc, struct ath_vif *avp,
+ bool powersave)
+{
+ struct ieee80211_vif *vif = avp->vif;
+ struct ieee80211_sta *sta = NULL;
+ struct ieee80211_hdr_3addr *nullfunc;
+ struct ath_tx_control txctl;
+ struct sk_buff *skb;
+ int band = sc->cur_chan->chandef.chan->band;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ if (!vif->bss_conf.assoc)
+ return false;
+
+ skb = ieee80211_nullfunc_get(sc->hw, vif);
+ if (!skb)
+ return false;
+
+ nullfunc = (struct ieee80211_hdr_3addr *) skb->data;
+ if (powersave)
+ nullfunc->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_PM);
+
+ skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+ if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, &sta)) {
+ dev_kfree_skb_any(skb);
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ memset(&txctl, 0, sizeof(txctl));
+ txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
+ txctl.sta = sta;
+ txctl.force_channel = true;
+ if (ath_tx_start(sc->hw, skb, &txctl)) {
+ ieee80211_free_txskb(sc->hw, skb);
+ return false;
+ }
+
+ return true;
+}
+
+void ath_chanctx_check_active(struct ath_softc *sc, struct ath_chanctx *ctx)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_vif *avp;
+ bool active = false;
+ u8 n_active = 0;
+
+ if (!ctx)
+ return;
+
+ list_for_each_entry(avp, &ctx->vifs, list) {
+ struct ieee80211_vif *vif = avp->vif;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_STATION:
+ if (vif->bss_conf.assoc)
+ active = true;
+ break;
+ default:
+ active = true;
+ break;
+ }
+ }
+ ctx->active = active;
+
+ ath_for_each_chanctx(sc, ctx) {
+ if (!ctx->assigned || list_empty(&ctx->vifs))
+ continue;
+ n_active++;
+ }
+
+ if (n_active <= 1) {
+ clear_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags);
+ return;
+ }
+ if (test_and_set_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+ return;
+ ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL);
+}
+
+static bool
+ath_chanctx_send_ps_frame(struct ath_softc *sc, bool powersave)
+{
+ struct ath_vif *avp;
+ bool sent = false;
+
+ rcu_read_lock();
+ list_for_each_entry(avp, &sc->cur_chan->vifs, list) {
+ if (ath_chanctx_send_vif_ps_frame(sc, avp, powersave))
+ sent = true;
+ }
+ rcu_read_unlock();
+
+ return sent;
+}
+
+static bool ath_chanctx_defer_switch(struct ath_softc *sc)
+{
+ if (sc->cur_chan == &sc->offchannel.chan)
+ return false;
+
+ switch (sc->sched.state) {
+ case ATH_CHANCTX_STATE_SWITCH:
+ return false;
+ case ATH_CHANCTX_STATE_IDLE:
+ if (!sc->cur_chan->switch_after_beacon)
+ return false;
+
+ sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+static void ath_chanctx_set_next(struct ath_softc *sc, bool force)
+{
+ struct timespec ts;
+ bool measure_time = false;
+ bool send_ps = false;
+
+ spin_lock_bh(&sc->chan_lock);
+ if (!sc->next_chan) {
+ spin_unlock_bh(&sc->chan_lock);
+ return;
+ }
+
+ if (!force && ath_chanctx_defer_switch(sc)) {
+ spin_unlock_bh(&sc->chan_lock);
+ return;
+ }
+
+ if (sc->cur_chan != sc->next_chan) {
+ sc->cur_chan->stopped = true;
+ spin_unlock_bh(&sc->chan_lock);
+
+ if (sc->next_chan == &sc->offchannel.chan) {
+ getrawmonotonic(&ts);
+ measure_time = true;
+ }
+ __ath9k_flush(sc->hw, ~0, true);
+
+ if (ath_chanctx_send_ps_frame(sc, true))
+ __ath9k_flush(sc->hw, BIT(IEEE80211_AC_VO), false);
+
+ send_ps = true;
+ spin_lock_bh(&sc->chan_lock);
+
+ if (sc->cur_chan != &sc->offchannel.chan) {
+ getrawmonotonic(&sc->cur_chan->tsf_ts);
+ sc->cur_chan->tsf_val = ath9k_hw_gettsf64(sc->sc_ah);
+ }
+ }
+ sc->cur_chan = sc->next_chan;
+ sc->cur_chan->stopped = false;
+ sc->next_chan = NULL;
+ sc->sched.offchannel_duration = 0;
+ if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE)
+ sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+
+ spin_unlock_bh(&sc->chan_lock);
+
+ if (sc->sc_ah->chip_fullsleep ||
+ memcmp(&sc->cur_chandef, &sc->cur_chan->chandef,
+ sizeof(sc->cur_chandef))) {
+ ath_set_channel(sc);
+ if (measure_time)
+ sc->sched.channel_switch_time =
+ ath9k_hw_get_tsf_offset(&ts, NULL);
+ }
+ if (send_ps)
+ ath_chanctx_send_ps_frame(sc, false);
+
+ ath_offchannel_channel_change(sc);
+ ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_SWITCH);
+}
+
+void ath_chanctx_work(struct work_struct *work)
+{
+ struct ath_softc *sc = container_of(work, struct ath_softc,
+ chanctx_work);
+ mutex_lock(&sc->mutex);
+ ath_chanctx_set_next(sc, false);
+ mutex_unlock(&sc->mutex);
+}
+
+void ath_chanctx_init(struct ath_softc *sc)
+{
+ struct ath_chanctx *ctx;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_channel *chan;
+ int i, j;
+
+ sband = &common->sbands[IEEE80211_BAND_2GHZ];
+ if (!sband->n_channels)
+ sband = &common->sbands[IEEE80211_BAND_5GHZ];
+
+ chan = &sband->channels[0];
+ for (i = 0; i < ATH9K_NUM_CHANCTX; i++) {
+ ctx = &sc->chanctx[i];
+ cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
+ INIT_LIST_HEAD(&ctx->vifs);
+ ctx->txpower = ATH_TXPOWER_MAX;
+ for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
+ INIT_LIST_HEAD(&ctx->acq[j]);
+ }
+ ctx = &sc->offchannel.chan;
+ cfg80211_chandef_create(&ctx->chandef, chan, NL80211_CHAN_HT20);
+ INIT_LIST_HEAD(&ctx->vifs);
+ ctx->txpower = ATH_TXPOWER_MAX;
+ for (j = 0; j < ARRAY_SIZE(ctx->acq); j++)
+ INIT_LIST_HEAD(&ctx->acq[j]);
+ sc->offchannel.chan.offchannel = true;
+
+}
+
+void ath9k_chanctx_force_active(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_vif *avp = (struct ath_vif *) vif->drv_priv;
+ bool changed = false;
+
+ if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags))
+ return;
+
+ if (!avp->chanctx)
+ return;
+
+ mutex_lock(&sc->mutex);
+
+ spin_lock_bh(&sc->chan_lock);
+ if (sc->next_chan || (sc->cur_chan != avp->chanctx)) {
+ sc->next_chan = avp->chanctx;
+ changed = true;
+ }
+ sc->sched.state = ATH_CHANCTX_STATE_FORCE_ACTIVE;
+ spin_unlock_bh(&sc->chan_lock);
+
+ if (changed)
+ ath_chanctx_set_next(sc, true);
+
+ mutex_unlock(&sc->mutex);
+}
+
+void ath_chanctx_switch(struct ath_softc *sc, struct ath_chanctx *ctx,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ spin_lock_bh(&sc->chan_lock);
+
+ if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) &&
+ (sc->cur_chan != ctx) && (ctx == &sc->offchannel.chan)) {
+ sc->sched.offchannel_pending = true;
+ spin_unlock_bh(&sc->chan_lock);
+ return;
+ }
+
+ sc->next_chan = ctx;
+ if (chandef)
+ ctx->chandef = *chandef;
+
+ if (sc->next_chan == &sc->offchannel.chan) {
+ sc->sched.offchannel_duration =
+ TU_TO_USEC(sc->offchannel.duration) +
+ sc->sched.channel_switch_time;
+ }
+ spin_unlock_bh(&sc->chan_lock);
+ ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+}
+
+void ath_chanctx_set_channel(struct ath_softc *sc, struct ath_chanctx *ctx,
+ struct cfg80211_chan_def *chandef)
+{
+ bool cur_chan;
+
+ spin_lock_bh(&sc->chan_lock);
+ if (chandef)
+ memcpy(&ctx->chandef, chandef, sizeof(*chandef));
+ cur_chan = sc->cur_chan == ctx;
+ spin_unlock_bh(&sc->chan_lock);
+
+ if (!cur_chan)
+ return;
+
+ ath_set_channel(sc);
+}
+
+struct ath_chanctx *ath_chanctx_get_oper_chan(struct ath_softc *sc, bool active)
+{
+ struct ath_chanctx *ctx;
+
+ ath_for_each_chanctx(sc, ctx) {
+ if (!ctx->assigned || list_empty(&ctx->vifs))
+ continue;
+ if (active && !ctx->active)
+ continue;
+
+ if (ctx->switch_after_beacon)
+ return ctx;
+ }
+
+ return &sc->chanctx[0];
+}
+
+void ath_chanctx_offchan_switch(struct ath_softc *sc,
+ struct ieee80211_channel *chan)
+{
+ struct cfg80211_chan_def chandef;
+
+ cfg80211_chandef_create(&chandef, chan, NL80211_CHAN_NO_HT);
+
+ ath_chanctx_switch(sc, &sc->offchannel.chan, &chandef);
+}
+
+static struct ath_chanctx *
+ath_chanctx_get_next(struct ath_softc *sc, struct ath_chanctx *ctx)
+{
+ int idx = ctx - &sc->chanctx[0];
+
+ return &sc->chanctx[!idx];
+}
+
+static void ath_chanctx_adjust_tbtt_delta(struct ath_softc *sc)
+{
+ struct ath_chanctx *prev, *cur;
+ struct timespec ts;
+ u32 cur_tsf, prev_tsf, beacon_int;
+ s32 offset;
+
+ beacon_int = TU_TO_USEC(sc->cur_chan->beacon.beacon_interval);
+
+ cur = sc->cur_chan;
+ prev = ath_chanctx_get_next(sc, cur);
+
+ getrawmonotonic(&ts);
+ cur_tsf = (u32) cur->tsf_val +
+ ath9k_hw_get_tsf_offset(&cur->tsf_ts, &ts);
+
+ prev_tsf = prev->last_beacon - (u32) prev->tsf_val + cur_tsf;
+ prev_tsf -= ath9k_hw_get_tsf_offset(&prev->tsf_ts, &ts);
+
+ /* Adjust the TSF time of the AP chanctx to keep its beacons
+ * at half beacon interval offset relative to the STA chanctx.
+ */
+ offset = cur_tsf - prev_tsf;
+
+ /* Ignore stale data or spurious timestamps */
+ if (offset < 0 || offset > 3 * beacon_int)
+ return;
+
+ offset = beacon_int / 2 - (offset % beacon_int);
+ prev->tsf_val += offset;
+}
+
+void ath_chanctx_timer(unsigned long data)
+{
+ struct ath_softc *sc = (struct ath_softc *) data;
+
+ ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
+}
+
+/* Configure the TSF based hardware timer for a channel switch.
+ * Also set up backup software timer, in case the gen timer fails.
+ * This could be caused by a hardware reset.
+ */
+static void ath_chanctx_setup_timer(struct ath_softc *sc, u32 tsf_time)
+{
+ struct ath_hw *ah = sc->sc_ah;
+
+ ath9k_hw_gen_timer_start(ah, sc->p2p_ps_timer, tsf_time, 1000000);
+ tsf_time -= ath9k_hw_gettsf32(ah);
+ tsf_time = msecs_to_jiffies(tsf_time / 1000) + 1;
+ mod_timer(&sc->sched.timer, tsf_time);
+}
+
+void ath_chanctx_event(struct ath_softc *sc, struct ieee80211_vif *vif,
+ enum ath_chanctx_event ev)
+{
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_beacon_config *cur_conf;
+ struct ath_vif *avp = NULL;
+ struct ath_chanctx *ctx;
+ u32 tsf_time;
+ u32 beacon_int;
+ bool noa_changed = false;
+
+ if (vif)
+ avp = (struct ath_vif *) vif->drv_priv;
+
+ spin_lock_bh(&sc->chan_lock);
+
+ switch (ev) {
+ case ATH_CHANCTX_EVENT_BEACON_PREPARE:
+ if (avp->offchannel_duration)
+ avp->offchannel_duration = 0;
+
+ if (avp->chanctx != sc->cur_chan)
+ break;
+
+ if (sc->sched.offchannel_pending) {
+ sc->sched.offchannel_pending = false;
+ sc->next_chan = &sc->offchannel.chan;
+ sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+ }
+
+ ctx = ath_chanctx_get_next(sc, sc->cur_chan);
+ if (ctx->active && sc->sched.state == ATH_CHANCTX_STATE_IDLE) {
+ sc->next_chan = ctx;
+ sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+ }
+
+ /* if the timer missed its window, use the next interval */
+ if (sc->sched.state == ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
+ sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_BEACON;
+
+ if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
+ break;
+
+ sc->sched.beacon_pending = true;
+ sc->sched.next_tbtt = REG_READ(ah, AR_NEXT_TBTT_TIMER);
+
+ cur_conf = &sc->cur_chan->beacon;
+ beacon_int = TU_TO_USEC(cur_conf->beacon_interval);
+
+ /* defer channel switch by a quarter beacon interval */
+ tsf_time = sc->sched.next_tbtt + beacon_int / 4;
+ sc->sched.switch_start_time = tsf_time;
+ sc->cur_chan->last_beacon = sc->sched.next_tbtt;
+
+ /* Prevent wrap-around issues */
+ if (avp->periodic_noa_duration &&
+ tsf_time - avp->periodic_noa_start > BIT(30))
+ avp->periodic_noa_duration = 0;
+
+ if (ctx->active && !avp->periodic_noa_duration) {
+ avp->periodic_noa_start = tsf_time;
+ avp->periodic_noa_duration =
+ TU_TO_USEC(cur_conf->beacon_interval) / 2 -
+ sc->sched.channel_switch_time;
+ noa_changed = true;
+ } else if (!ctx->active && avp->periodic_noa_duration) {
+ avp->periodic_noa_duration = 0;
+ noa_changed = true;
+ }
+
+ /* If at least two consecutive beacons were missed on the STA
+ * chanctx, stay on the STA channel for one extra beacon period,
+ * to resync the timer properly.
+ */
+ if (ctx->active && sc->sched.beacon_miss >= 2)
+ sc->sched.offchannel_duration = 3 * beacon_int / 2;
+
+ if (sc->sched.offchannel_duration) {
+ noa_changed = true;
+ avp->offchannel_start = tsf_time;
+ avp->offchannel_duration =
+ sc->sched.offchannel_duration;
+ }
+
+ if (noa_changed)
+ avp->noa_index++;
+ break;
+ case ATH_CHANCTX_EVENT_BEACON_SENT:
+ if (!sc->sched.beacon_pending)
+ break;
+
+ sc->sched.beacon_pending = false;
+ if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_BEACON)
+ break;
+
+ sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
+ ath_chanctx_setup_timer(sc, sc->sched.switch_start_time);
+ break;
+ case ATH_CHANCTX_EVENT_TSF_TIMER:
+ if (sc->sched.state != ATH_CHANCTX_STATE_WAIT_FOR_TIMER)
+ break;
+
+ if (!sc->cur_chan->switch_after_beacon &&
+ sc->sched.beacon_pending)
+ sc->sched.beacon_miss++;
+
+ sc->sched.state = ATH_CHANCTX_STATE_SWITCH;
+ ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+ break;
+ case ATH_CHANCTX_EVENT_BEACON_RECEIVED:
+ if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) ||
+ sc->cur_chan == &sc->offchannel.chan)
+ break;
+
+ ath_chanctx_adjust_tbtt_delta(sc);
+ sc->sched.beacon_pending = false;
+ sc->sched.beacon_miss = 0;
+
+ /* TSF time might have been updated by the incoming beacon,
+ * need update the channel switch timer to reflect the change.
+ */
+ tsf_time = sc->sched.switch_start_time;
+ tsf_time -= (u32) sc->cur_chan->tsf_val +
+ ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts, NULL);
+ tsf_time += ath9k_hw_gettsf32(ah);
+
+
+ ath_chanctx_setup_timer(sc, tsf_time);
+ break;
+ case ATH_CHANCTX_EVENT_ASSOC:
+ if (sc->sched.state != ATH_CHANCTX_STATE_FORCE_ACTIVE ||
+ avp->chanctx != sc->cur_chan)
+ break;
+
+ sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+ /* fall through */
+ case ATH_CHANCTX_EVENT_SWITCH:
+ if (!test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags) ||
+ sc->sched.state == ATH_CHANCTX_STATE_FORCE_ACTIVE ||
+ sc->cur_chan->switch_after_beacon ||
+ sc->cur_chan == &sc->offchannel.chan)
+ break;
+
+ /* If this is a station chanctx, stay active for a half
+ * beacon period (minus channel switch time)
+ */
+ sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan);
+ cur_conf = &sc->cur_chan->beacon;
+
+ sc->sched.state = ATH_CHANCTX_STATE_WAIT_FOR_TIMER;
+
+ tsf_time = TU_TO_USEC(cur_conf->beacon_interval) / 2;
+ if (sc->sched.beacon_miss >= 2) {
+ sc->sched.beacon_miss = 0;
+ tsf_time *= 3;
+ }
+
+ tsf_time -= sc->sched.channel_switch_time;
+ tsf_time += ath9k_hw_gettsf32(sc->sc_ah);
+ sc->sched.switch_start_time = tsf_time;
+
+ ath_chanctx_setup_timer(sc, tsf_time);
+ sc->sched.beacon_pending = true;
+ break;
+ case ATH_CHANCTX_EVENT_ENABLE_MULTICHANNEL:
+ if (sc->cur_chan == &sc->offchannel.chan ||
+ sc->cur_chan->switch_after_beacon)
+ break;
+
+ sc->next_chan = ath_chanctx_get_next(sc, sc->cur_chan);
+ ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+ break;
+ case ATH_CHANCTX_EVENT_UNASSIGN:
+ if (sc->cur_chan->assigned) {
+ if (sc->next_chan && !sc->next_chan->assigned &&
+ sc->next_chan != &sc->offchannel.chan)
+ sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+ break;
+ }
+
+ ctx = ath_chanctx_get_next(sc, sc->cur_chan);
+ sc->sched.state = ATH_CHANCTX_STATE_IDLE;
+ if (!ctx->assigned)
+ break;
+
+ sc->next_chan = ctx;
+ ieee80211_queue_work(sc->hw, &sc->chanctx_work);
+ break;
+ }
+
+ spin_unlock_bh(&sc->chan_lock);
+}
diff --git a/drivers/net/wireless/ath/ath9k/common-beacon.c b/drivers/net/wireless/ath/ath9k/common-beacon.c
index 775d1d20ce0b..733be5178481 100644
--- a/drivers/net/wireless/ath/ath9k/common-beacon.c
+++ b/drivers/net/wireless/ath/ath9k/common-beacon.c
@@ -57,7 +57,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
struct ath9k_beacon_state *bs)
{
struct ath_common *common = ath9k_hw_common(ah);
- int dtim_intval;
+ int dtim_intval, sleepduration;
u64 tsf;
/* No need to configure beacon if we are not associated */
@@ -75,6 +75,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
* last beacon we received (which may be none).
*/
dtim_intval = conf->intval * conf->dtim_period;
+ sleepduration = ah->hw->conf.listen_interval * conf->intval;
/*
* Pull nexttbtt forward to reflect the current
@@ -112,7 +113,7 @@ int ath9k_cmn_beacon_config_sta(struct ath_hw *ah,
*/
bs->bs_sleepduration = TU_TO_USEC(roundup(IEEE80211_MS_TO_TU(100),
- conf->intval));
+ sleepduration));
if (bs->bs_sleepduration > bs->bs_dtimperiod)
bs->bs_sleepduration = bs->bs_dtimperiod;
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 6cc42be48d4e..ce073e995dfe 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -750,13 +750,13 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
{
struct ath_softc *sc = file->private_data;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ieee80211_hw *hw = sc->hw;
struct ath9k_vif_iter_data iter_data;
+ struct ath_chanctx *ctx;
char buf[512];
unsigned int len = 0;
ssize_t retval = 0;
unsigned int reg;
- u32 rxfilter;
+ u32 rxfilter, i;
len += scnprintf(buf + len, sizeof(buf) - len,
"BSSID: %pM\n", common->curbssid);
@@ -826,14 +826,20 @@ static ssize_t read_file_misc(struct file *file, char __user *user_buf,
len += scnprintf(buf + len, sizeof(buf) - len, "\n");
- ath9k_calculate_iter_data(hw, NULL, &iter_data);
-
- len += scnprintf(buf + len, sizeof(buf) - len,
- "VIF-COUNTS: AP: %i STA: %i MESH: %i WDS: %i"
- " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
- iter_data.naps, iter_data.nstations, iter_data.nmeshes,
- iter_data.nwds, iter_data.nadhocs,
- sc->nvifs, sc->nbcnvifs);
+ i = 0;
+ ath_for_each_chanctx(sc, ctx) {
+ if (!ctx->assigned || list_empty(&ctx->vifs))
+ continue;
+ ath9k_calculate_iter_data(sc, ctx, &iter_data);
+
+ len += scnprintf(buf + len, sizeof(buf) - len,
+ "VIF-COUNTS: CTX %i AP: %i STA: %i MESH: %i WDS: %i",
+ i++, iter_data.naps, iter_data.nstations,
+ iter_data.nmeshes, iter_data.nwds);
+ len += scnprintf(buf + len, sizeof(buf) - len,
+ " ADHOC: %i TOTAL: %hi BEACON-VIF: %hi\n",
+ iter_data.nadhocs, sc->nvifs, sc->nbcnvifs);
+ }
if (len > sizeof(buf))
len = sizeof(buf);
@@ -1080,7 +1086,7 @@ static ssize_t read_file_dump_nfcal(struct file *file, char __user *user_buf,
{
struct ath_softc *sc = file->private_data;
struct ath_hw *ah = sc->sc_ah;
- struct ath9k_nfcal_hist *h = sc->caldata.nfCalHist;
+ struct ath9k_nfcal_hist *h = sc->cur_chan->caldata.nfCalHist;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &common->hw->conf;
u32 len = 0, size = 1500;
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 2a8ed8375ec0..fd0158fdf144 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -791,7 +791,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah,
refdiv = 5;
} else {
pll2_divint = 0x11;
- pll2_divfrac = 0x26666;
+ pll2_divfrac =
+ AR_SREV_9531(ah) ? 0x26665 : 0x26666;
refdiv = 1;
}
}
@@ -1730,6 +1731,23 @@ fail:
return -EINVAL;
}
+u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur)
+{
+ struct timespec ts;
+ s64 usec;
+
+ if (!cur) {
+ getrawmonotonic(&ts);
+ cur = &ts;
+ }
+
+ usec = cur->tv_sec * 1000000ULL + cur->tv_nsec / 1000;
+ usec -= last->tv_sec * 1000000ULL + last->tv_nsec / 1000;
+
+ return (u32) usec;
+}
+EXPORT_SYMBOL(ath9k_hw_get_tsf_offset);
+
int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
struct ath9k_hw_cal_data *caldata, bool fastcc)
{
@@ -1739,7 +1757,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
u32 saveDefAntenna;
u32 macStaId1;
u64 tsf = 0;
- s64 usec = 0;
int r;
bool start_mci_reset = false;
bool save_fullsleep = ah->chip_fullsleep;
@@ -1785,7 +1802,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
/* Save TSF before chip reset, a cold reset clears it */
tsf = ath9k_hw_gettsf64(ah);
getrawmonotonic(&ts);
- usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000;
saveLedState = REG_READ(ah, AR_CFG_LED) &
(AR_CFG_LED_ASSOC_CTL | AR_CFG_LED_MODE_SEL |
@@ -1818,9 +1834,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
}
/* Restore TSF */
- getrawmonotonic(&ts);
- usec = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000 - usec;
- ath9k_hw_settsf64(ah, tsf + usec);
+ ath9k_hw_settsf64(ah, tsf + ath9k_hw_get_tsf_offset(&ts, NULL));
if (AR_SREV_9280_20_OR_LATER(ah))
REG_SET_BIT(ah, AR_GPIO_INPUT_EN_VAL, AR_GPIO_JTAG_DISABLE);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 0acd4b5a4892..51b4ebe04c04 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -1000,6 +1000,7 @@ u32 ath9k_hw_gettsf32(struct ath_hw *ah);
u64 ath9k_hw_gettsf64(struct ath_hw *ah);
void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64);
void ath9k_hw_reset_tsf(struct ath_hw *ah);
+u32 ath9k_hw_get_tsf_offset(struct timespec *last, struct timespec *cur);
void ath9k_hw_set_tsfadjust(struct ath_hw *ah, bool set);
void ath9k_hw_init_global_settings(struct ath_hw *ah);
u32 ar9003_get_pll_sqsum_dvc(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 0246b990fe87..39419ea845cc 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -61,7 +61,7 @@ static int ath9k_ps_enable;
module_param_named(ps_enable, ath9k_ps_enable, int, 0444);
MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave");
-static int ath9k_use_chanctx;
+int ath9k_use_chanctx;
module_param_named(use_chanctx, ath9k_use_chanctx, int, 0444);
MODULE_PARM_DESC(use_chanctx, "Enable channel context for concurrency");
@@ -169,9 +169,9 @@ static void ath9k_reg_notifier(struct wiphy *wiphy,
/* Set tx power */
if (ah->curchan) {
- sc->config.txpowlimit = 2 * ah->curchan->chan->max_power;
+ sc->cur_chan->txpower = 2 * ah->curchan->chan->max_power;
ath9k_ps_wakeup(sc);
- ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
+ ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
sc->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
/* synchronize DFS detector if regulatory domain changed */
if (sc->dfs_detector != NULL)
@@ -335,7 +335,6 @@ static void ath9k_init_misc(struct ath_softc *sc)
setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc);
common->last_rssi = ATH_RSSI_DUMMY_MARKER;
- sc->config.txpowlimit = ATH_TXPOWER_MAX;
memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN);
sc->beacon.slottime = ATH9K_SLOT_TIME_9;
@@ -511,6 +510,9 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
sc->dfs_detector = dfs_pattern_detector_init(common, NL80211_DFS_UNSET);
sc->tx99_power = MAX_RATE_POWER + 1;
init_waitqueue_head(&sc->tx_wait);
+ sc->cur_chan = &sc->chanctx[0];
+ if (!ath9k_use_chanctx)
+ sc->cur_chan->hw_queue_base = 0;
if (!pdata || pdata->use_eeprom) {
ah->ah_flags |= AH_USE_EEPROM;
@@ -556,6 +558,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
spin_lock_init(&common->cc_lock);
spin_lock_init(&sc->sc_serial_rw);
spin_lock_init(&sc->sc_pm_lock);
+ spin_lock_init(&sc->chan_lock);
mutex_init(&sc->mutex);
tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc);
tasklet_init(&sc->bcon_tasklet, ath9k_beacon_tasklet,
@@ -564,7 +567,11 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
setup_timer(&sc->sleep_timer, ath_ps_full_sleep, (unsigned long)sc);
INIT_WORK(&sc->hw_reset_work, ath_reset_work);
INIT_WORK(&sc->paprd_work, ath_paprd_calibrate);
+ INIT_WORK(&sc->chanctx_work, ath_chanctx_work);
INIT_DELAYED_WORK(&sc->hw_pll_work, ath_hw_pll_work);
+ setup_timer(&sc->offchannel.timer, ath_offchannel_timer,
+ (unsigned long)sc);
+ setup_timer(&sc->sched.timer, ath_chanctx_timer, (unsigned long)sc);
/*
* Cache line size is used to size and align various
@@ -599,6 +606,7 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
ath9k_cmn_init_crypto(sc->sc_ah);
ath9k_init_misc(sc);
ath_fill_led_pin(sc);
+ ath_chanctx_init(sc);
if (common->bus_ops->aspm_init)
common->bus_ops->aspm_init(common);
@@ -664,6 +672,12 @@ static const struct ieee80211_iface_limit wds_limits[] = {
{ .max = 2048, .types = BIT(NL80211_IFTYPE_WDS) },
};
+static const struct ieee80211_iface_limit if_limits_multi[] = {
+ { .max = 1, .types = BIT(NL80211_IFTYPE_STATION) },
+ { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO) },
+};
+
static const struct ieee80211_iface_limit if_dfs_limits[] = {
{ .max = 1, .types = BIT(NL80211_IFTYPE_AP) |
#ifdef CONFIG_MAC80211_MESH
@@ -672,6 +686,16 @@ static const struct ieee80211_iface_limit if_dfs_limits[] = {
BIT(NL80211_IFTYPE_ADHOC) },
};
+static const struct ieee80211_iface_combination if_comb_multi[] = {
+ {
+ .limits = if_limits_multi,
+ .n_limits = ARRAY_SIZE(if_limits_multi),
+ .max_interfaces = 2,
+ .num_different_channels = 2,
+ .beacon_int_infra_match = true,
+ },
+};
+
static const struct ieee80211_iface_combination if_comb[] = {
{
.limits = if_limits,
@@ -712,6 +736,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
IEEE80211_HW_SPECTRUM_MGMT |
IEEE80211_HW_REPORTS_TX_ACK_STATUS |
IEEE80211_HW_SUPPORTS_RC_TABLE |
+ IEEE80211_HW_QUEUE_CONTROL |
IEEE80211_HW_SUPPORTS_HT_CCK_RATES;
if (ath9k_ps_enable)
@@ -739,12 +764,21 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC) |
BIT(NL80211_IFTYPE_MESH_POINT);
- hw->wiphy->iface_combinations = if_comb;
if (!ath9k_use_chanctx) {
+ hw->wiphy->iface_combinations = if_comb;
hw->wiphy->n_iface_combinations = ARRAY_SIZE(if_comb);
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_WDS);
- } else
- hw->wiphy->n_iface_combinations = 1;
+ } else {
+ hw->wiphy->iface_combinations = if_comb_multi;
+ hw->wiphy->n_iface_combinations =
+ ARRAY_SIZE(if_comb_multi);
+ hw->wiphy->max_scan_ssids = 255;
+ hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+ hw->wiphy->max_remain_on_channel_duration = 10000;
+ hw->chanctx_data_size = sizeof(void *);
+ hw->extra_beacon_tailroom =
+ sizeof(struct ieee80211_p2p_noa_attr) + 9;
+ }
}
hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
@@ -756,9 +790,14 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw)
hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH;
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
- hw->queues = 4;
+ /* allow 4 queues per channel context +
+ * 1 cab queue + 1 offchannel tx queue
+ */
+ hw->queues = 10;
+ /* last queue for offchannel */
+ hw->offchannel_tx_hw_queue = hw->queues - 1;
hw->max_rates = 4;
- hw->max_listen_interval = 1;
+ hw->max_listen_interval = 10;
hw->max_rate_tries = 10;
hw->sta_data_size = sizeof(struct ath_node);
hw->vif_data_size = sizeof(struct ath_vif);
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 72a715fe8f24..2343f56e6498 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -178,7 +178,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int
txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE];
memset(tx_info, 0, sizeof(*tx_info));
- tx_info->band = hw->conf.chandef.chan->band;
+ tx_info->band = sc->cur_chandef.chan->band;
tx_info->flags |= IEEE80211_TX_CTL_NO_ACK;
tx_info->control.rates[0].idx = 0;
tx_info->control.rates[0].count = 1;
@@ -416,7 +416,7 @@ void ath_start_ani(struct ath_softc *sc)
if (common->disable_ani ||
!test_bit(ATH_OP_ANI_RUN, &common->op_flags) ||
- (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
+ sc->cur_chan->offchannel)
return;
common->ani.longcal_timer = timestamp;
@@ -440,7 +440,7 @@ void ath_check_ani(struct ath_softc *sc)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
- struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+ struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
/*
* Check for the various conditions in which ANI has to
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 62ac95d6bb9d..e6ac8d2e610c 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -19,9 +19,6 @@
#include "ath9k.h"
#include "btcoex.h"
-static void ath9k_set_assoc_state(struct ath_softc *sc,
- struct ieee80211_vif *vif);
-
u8 ath9k_parse_mpdudensity(u8 mpdudensity)
{
/*
@@ -63,9 +60,16 @@ static bool ath9k_has_pending_frames(struct ath_softc *sc, struct ath_txq *txq)
spin_lock_bh(&txq->axq_lock);
- if (txq->axq_depth || !list_empty(&txq->axq_acq))
+ if (txq->axq_depth)
pending = true;
+ if (txq->mac80211_qnum >= 0) {
+ struct list_head *list;
+
+ list = &sc->cur_chan->acq[txq->mac80211_qnum];
+ if (!list_empty(list))
+ pending = true;
+ }
spin_unlock_bh(&txq->axq_lock);
return pending;
}
@@ -227,13 +231,22 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
}
ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->config.txpowlimit, &sc->curtxpow);
+ sc->cur_chan->txpower, &sc->curtxpow);
clear_bit(ATH_OP_HW_RESET, &common->op_flags);
- ath9k_hw_set_interrupts(ah);
- ath9k_hw_enable_interrupts(ah);
+ ath9k_calculate_summary_state(sc, sc->cur_chan);
+
+ if (!sc->cur_chan->offchannel && start) {
+ /* restore per chanctx TSF timer */
+ if (sc->cur_chan->tsf_val) {
+ u32 offset;
+
+ offset = ath9k_hw_get_tsf_offset(&sc->cur_chan->tsf_ts,
+ NULL);
+ ath9k_hw_settsf64(ah, sc->cur_chan->tsf_val + offset);
+ }
+
- if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL) && start) {
if (!test_bit(ATH_OP_BEACONS, &common->op_flags))
goto work;
@@ -247,26 +260,35 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
}
work:
ath_restart_work(sc);
+ ath_txq_schedule_all(sc);
+ }
- for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) {
- if (!ATH_TXQ_SETUP(sc, i))
- continue;
+ sc->gtt_cnt = 0;
- spin_lock_bh(&sc->tx.txq[i].axq_lock);
- ath_txq_schedule(sc, &sc->tx.txq[i]);
- spin_unlock_bh(&sc->tx.txq[i].axq_lock);
+ ath9k_hw_set_interrupts(ah);
+ ath9k_hw_enable_interrupts(ah);
+
+ if (!ath9k_use_chanctx)
+ ieee80211_wake_queues(sc->hw);
+ else {
+ if (sc->cur_chan == &sc->offchannel.chan)
+ ieee80211_wake_queue(sc->hw,
+ sc->hw->offchannel_tx_hw_queue);
+ else {
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ ieee80211_wake_queue(sc->hw,
+ sc->cur_chan->hw_queue_base + i);
}
+ if (ah->opmode == NL80211_IFTYPE_AP)
+ ieee80211_wake_queue(sc->hw, sc->hw->queues - 2);
}
- sc->gtt_cnt = 0;
- ieee80211_wake_queues(sc->hw);
-
ath9k_p2p_ps_timer(sc);
return true;
}
-static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
+int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
{
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
@@ -279,9 +301,9 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
tasklet_disable(&sc->intr_tq);
spin_lock_bh(&sc->sc_pcu_lock);
- if (!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) {
+ if (!sc->cur_chan->offchannel) {
fastcc = false;
- caldata = &sc->caldata;
+ caldata = &sc->cur_chan->caldata;
}
if (!hchan) {
@@ -292,6 +314,10 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
if (!ath_prepare_reset(sc))
fastcc = false;
+ spin_lock_bh(&sc->chan_lock);
+ sc->cur_chandef = sc->cur_chan->chandef;
+ spin_unlock_bh(&sc->chan_lock);
+
ath_dbg(common, CONFIG, "Reset to %u MHz, HT40: %d fastcc: %d\n",
hchan->channel, IS_CHAN_HT40(hchan), fastcc);
@@ -307,7 +333,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan)
}
if (ath9k_hw_mci_is_enabled(sc->sc_ah) &&
- (sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
+ sc->cur_chan->offchannel)
ath9k_mci_set_txpower(sc, true, false);
if (!ath_complete_reset(sc, true))
@@ -320,98 +346,6 @@ out:
return r;
}
-
-/*
- * Set/change channels. If the channel is really being changed, it's done
- * by reseting the chip. To accomplish this we must first cleanup any pending
- * DMA, then restart stuff.
-*/
-static int ath_set_channel(struct ath_softc *sc, struct cfg80211_chan_def *chandef)
-{
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_hw *hw = sc->hw;
- struct ath9k_channel *hchan;
- struct ieee80211_channel *chan = chandef->chan;
- bool offchannel;
- int pos = chan->hw_value;
- int old_pos = -1;
- int r;
-
- if (test_bit(ATH_OP_INVALID, &common->op_flags))
- return -EIO;
-
- offchannel = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
-
- if (ah->curchan)
- old_pos = ah->curchan - &ah->channels[0];
-
- ath_dbg(common, CONFIG, "Set channel: %d MHz width: %d\n",
- chan->center_freq, chandef->width);
-
- /* update survey stats for the old channel before switching */
- spin_lock_bh(&common->cc_lock);
- ath_update_survey_stats(sc);
- spin_unlock_bh(&common->cc_lock);
-
- ath9k_cmn_get_channel(hw, ah, chandef);
-
- /*
- * If the operating channel changes, change the survey in-use flags
- * along with it.
- * Reset the survey data for the new channel, unless we're switching
- * back to the operating channel from an off-channel operation.
- */
- if (!offchannel && sc->cur_survey != &sc->survey[pos]) {
- if (sc->cur_survey)
- sc->cur_survey->filled &= ~SURVEY_INFO_IN_USE;
-
- sc->cur_survey = &sc->survey[pos];
-
- memset(sc->cur_survey, 0, sizeof(struct survey_info));
- sc->cur_survey->filled |= SURVEY_INFO_IN_USE;
- } else if (!(sc->survey[pos].filled & SURVEY_INFO_IN_USE)) {
- memset(&sc->survey[pos], 0, sizeof(struct survey_info));
- }
-
- hchan = &sc->sc_ah->channels[pos];
- r = ath_reset_internal(sc, hchan);
- if (r)
- return r;
-
- /*
- * The most recent snapshot of channel->noisefloor for the old
- * channel is only available after the hardware reset. Copy it to
- * the survey stats now.
- */
- if (old_pos >= 0)
- ath_update_survey_nf(sc, old_pos);
-
- /*
- * Enable radar pulse detection if on a DFS channel. Spectral
- * scanning and radar detection can not be used concurrently.
- */
- if (hw->conf.radar_enabled) {
- u32 rxfilter;
-
- /* set HW specific DFS configuration */
- ath9k_hw_set_radar_params(ah);
- rxfilter = ath9k_hw_getrxfilter(ah);
- rxfilter |= ATH9K_RX_FILTER_PHYRADAR |
- ATH9K_RX_FILTER_PHYERR;
- ath9k_hw_setrxfilter(ah, rxfilter);
- ath_dbg(common, DFS, "DFS enabled at freq %d\n",
- chan->center_freq);
- } else {
- /* perform spectral scan if requested. */
- if (test_bit(ATH_OP_SCANNING, &common->op_flags) &&
- sc->spectral_mode == SPECTRAL_CHANSCAN)
- ath9k_spectral_scan_trigger(hw);
- }
-
- return 0;
-}
-
static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta,
struct ieee80211_vif *vif)
{
@@ -712,7 +646,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
- struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+ struct ieee80211_channel *curchan = sc->cur_chan->chandef.chan;
+ struct ath_chanctx *ctx = sc->cur_chan;
struct ath9k_channel *init_channel;
int r;
@@ -723,7 +658,8 @@ static int ath9k_start(struct ieee80211_hw *hw)
ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
- init_channel = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
+ init_channel = ath9k_cmn_get_channel(hw, ah, &ctx->chandef);
+ sc->cur_chandef = hw->conf.chandef;
/* Reset SERDES registers */
ath9k_hw_configpcipowersave(ah, false);
@@ -886,6 +822,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
struct ath_common *common = ath9k_hw_common(ah);
bool prev_idle;
+ cancel_work_sync(&sc->chanctx_work);
mutex_lock(&sc->mutex);
ath_cancel_work(sc);
@@ -934,7 +871,8 @@ static void ath9k_stop(struct ieee80211_hw *hw)
}
if (!ah->curchan)
- ah->curchan = ath9k_cmn_get_channel(hw, ah, &hw->conf.chandef);
+ ah->curchan = ath9k_cmn_get_channel(hw, ah,
+ &sc->cur_chan->chandef);
ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
ath9k_hw_phy_disable(ah);
@@ -979,18 +917,29 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
iter_data->has_hw_macaddr = true;
}
+ if (!vif->bss_conf.use_short_slot)
+ iter_data->slottime = ATH9K_SLOT_TIME_20;
+
switch (vif->type) {
case NL80211_IFTYPE_AP:
iter_data->naps++;
+ if (vif->bss_conf.enable_beacon)
+ iter_data->beacons = true;
break;
case NL80211_IFTYPE_STATION:
iter_data->nstations++;
+ if (vif->bss_conf.assoc && !iter_data->primary_sta)
+ iter_data->primary_sta = vif;
break;
case NL80211_IFTYPE_ADHOC:
iter_data->nadhocs++;
+ if (vif->bss_conf.enable_beacon)
+ iter_data->beacons = true;
break;
case NL80211_IFTYPE_MESH_POINT:
iter_data->nmeshes++;
+ if (vif->bss_conf.enable_beacon)
+ iter_data->beacons = true;
break;
case NL80211_IFTYPE_WDS:
iter_data->nwds++;
@@ -1000,26 +949,12 @@ static void ath9k_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
}
}
-static void ath9k_sta_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
- struct ath_softc *sc = data;
- struct ath_vif *avp = (void *)vif->drv_priv;
-
- if (vif->type != NL80211_IFTYPE_STATION)
- return;
-
- if (avp->primary_sta_vif)
- ath9k_set_assoc_state(sc, vif);
-}
-
/* Called with sc->mutex held. */
-void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
+void ath9k_calculate_iter_data(struct ath_softc *sc,
+ struct ath_chanctx *ctx,
struct ath9k_vif_iter_data *iter_data)
{
- struct ath_softc *sc = hw->priv;
- struct ath_hw *ah = sc->sc_ah;
- struct ath_common *common = ath9k_hw_common(ah);
+ struct ath_vif *avp;
/*
* Pick the MAC address of the first interface as the new hardware
@@ -1028,29 +963,80 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw,
*/
memset(iter_data, 0, sizeof(*iter_data));
memset(&iter_data->mask, 0xff, ETH_ALEN);
+ iter_data->slottime = ATH9K_SLOT_TIME_9;
+
+ list_for_each_entry(avp, &ctx->vifs, list)
+ ath9k_vif_iter(iter_data, avp->vif->addr, avp->vif);
+
+ if (ctx == &sc->offchannel.chan) {
+ struct ieee80211_vif *vif;
+
+ if (sc->offchannel.state < ATH_OFFCHANNEL_ROC_START)
+ vif = sc->offchannel.scan_vif;
+ else
+ vif = sc->offchannel.roc_vif;
+
+ if (vif)
+ ath9k_vif_iter(iter_data, vif->addr, vif);
+ iter_data->beacons = false;
+ }
+}
+
+static void ath9k_set_assoc_state(struct ath_softc *sc,
+ struct ieee80211_vif *vif, bool changed)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+ unsigned long flags;
- if (vif)
- ath9k_vif_iter(iter_data, vif->addr, vif);
+ set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
+ /* Set the AID, BSSID and do beacon-sync only when
+ * the HW opmode is STATION.
+ *
+ * But the primary bit is set above in any case.
+ */
+ if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION)
+ return;
+
+ ether_addr_copy(common->curbssid, bss_conf->bssid);
+ common->curaid = bss_conf->aid;
+ ath9k_hw_write_associd(sc->sc_ah);
- /* Get list of all active MAC addresses */
- ieee80211_iterate_active_interfaces_atomic(
- sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- ath9k_vif_iter, iter_data);
+ if (changed) {
+ common->last_rssi = ATH_RSSI_DUMMY_MARKER;
+ sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
- memcpy(common->macaddr, iter_data->hw_macaddr, ETH_ALEN);
+ spin_lock_irqsave(&sc->sc_pm_lock, flags);
+ sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
+ spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
+ }
+
+ if (ath9k_hw_mci_is_enabled(sc->sc_ah))
+ ath9k_mci_update_wlan_channels(sc, false);
+
+ ath_dbg(common, CONFIG,
+ "Primary Station interface: %pM, BSSID: %pM\n",
+ vif->addr, common->curbssid);
}
/* Called with sc->mutex held. */
-static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif)
+void ath9k_calculate_summary_state(struct ath_softc *sc,
+ struct ath_chanctx *ctx)
{
- struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ath9k_vif_iter_data iter_data;
- enum nl80211_iftype old_opmode = ah->opmode;
- ath9k_calculate_iter_data(hw, vif, &iter_data);
+ ath_chanctx_check_active(sc, ctx);
+
+ if (ctx != sc->cur_chan)
+ return;
+
+ ath9k_ps_wakeup(sc);
+ ath9k_calculate_iter_data(sc, ctx, &iter_data);
+
+ if (iter_data.has_hw_macaddr)
+ ether_addr_copy(common->macaddr, iter_data.hw_macaddr);
memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
ath_hw_setbssidmask(common);
@@ -1073,24 +1059,57 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw,
ath9k_hw_setopmode(ah);
+ ctx->switch_after_beacon = false;
if ((iter_data.nstations + iter_data.nadhocs + iter_data.nmeshes) > 0)
ah->imask |= ATH9K_INT_TSFOOR;
- else
+ else {
ah->imask &= ~ATH9K_INT_TSFOOR;
+ if (iter_data.naps == 1 && iter_data.beacons)
+ ctx->switch_after_beacon = true;
+ }
+
+ ah->imask &= ~ATH9K_INT_SWBA;
+ if (ah->opmode == NL80211_IFTYPE_STATION) {
+ bool changed = (iter_data.primary_sta != ctx->primary_sta);
+ iter_data.beacons = true;
+ if (iter_data.primary_sta) {
+ ath9k_set_assoc_state(sc, iter_data.primary_sta,
+ changed);
+ if (!ctx->primary_sta ||
+ !ctx->primary_sta->bss_conf.assoc)
+ ctx->primary_sta = iter_data.primary_sta;
+ } else {
+ ctx->primary_sta = NULL;
+ memset(common->curbssid, 0, ETH_ALEN);
+ common->curaid = 0;
+ ath9k_hw_write_associd(sc->sc_ah);
+ if (ath9k_hw_mci_is_enabled(sc->sc_ah))
+ ath9k_mci_update_wlan_channels(sc, true);
+ }
+ } else if (iter_data.beacons) {
+ ah->imask |= ATH9K_INT_SWBA;
+ }
ath9k_hw_set_interrupts(ah);
- /*
- * If we are changing the opmode to STATION,
- * a beacon sync needs to be done.
- */
- if (ah->opmode == NL80211_IFTYPE_STATION &&
- old_opmode == NL80211_IFTYPE_AP &&
- test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) {
- ieee80211_iterate_active_interfaces_atomic(
- sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- ath9k_sta_vif_iter, sc);
+ if (iter_data.beacons)
+ set_bit(ATH_OP_BEACONS, &common->op_flags);
+ else
+ clear_bit(ATH_OP_BEACONS, &common->op_flags);
+
+ if (ah->slottime != iter_data.slottime) {
+ ah->slottime = iter_data.slottime;
+ ath9k_hw_init_global_settings(ah);
}
+
+ if (iter_data.primary_sta)
+ set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
+ else
+ clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
+
+ ctx->primary_sta = iter_data.primary_sta;
+
+ ath9k_ps_restore(sc);
}
static int ath9k_add_interface(struct ieee80211_hw *hw,
@@ -1101,6 +1120,7 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
struct ath_common *common = ath9k_hw_common(ah);
struct ath_vif *avp = (void *)vif->drv_priv;
struct ath_node *an = &avp->mcast_node;
+ int i;
mutex_lock(&sc->mutex);
@@ -1115,14 +1135,20 @@ static int ath9k_add_interface(struct ieee80211_hw *hw,
ath_dbg(common, CONFIG, "Attach a VIF of type: %d\n", vif->type);
sc->nvifs++;
- ath9k_ps_wakeup(sc);
- ath9k_calculate_summary_state(hw, vif);
- ath9k_ps_restore(sc);
-
if (ath9k_uses_beacons(vif->type))
ath9k_beacon_assign_slot(sc, vif);
avp->vif = vif;
+ if (!ath9k_use_chanctx) {
+ avp->chanctx = sc->cur_chan;
+ list_add_tail(&avp->list, &avp->chanctx->vifs);
+ }
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ vif->hw_queue[i] = i;
+ if (vif->type == NL80211_IFTYPE_AP)
+ vif->cab_queue = hw->queues - 2;
+ else
+ vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
an->sc = sc;
an->sta = NULL;
@@ -1141,6 +1167,8 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
{
struct ath_softc *sc = hw->priv;
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ int i;
mutex_lock(&sc->mutex);
@@ -1157,13 +1185,19 @@ static int ath9k_change_interface(struct ieee80211_hw *hw,
vif->type = new_type;
vif->p2p = p2p;
- ath9k_ps_wakeup(sc);
- ath9k_calculate_summary_state(hw, vif);
- ath9k_ps_restore(sc);
-
if (ath9k_uses_beacons(vif->type))
ath9k_beacon_assign_slot(sc, vif);
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ vif->hw_queue[i] = i;
+
+ if (vif->type == NL80211_IFTYPE_AP)
+ vif->cab_queue = hw->queues - 2;
+ else
+ vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
+
+ ath9k_calculate_summary_state(sc, avp->chanctx);
+
mutex_unlock(&sc->mutex);
return 0;
}
@@ -1211,14 +1245,12 @@ static void ath9k_remove_interface(struct ieee80211_hw *hw,
sc->nvifs--;
sc->tx99_vif = NULL;
+ if (!ath9k_use_chanctx)
+ list_del(&avp->list);
if (ath9k_uses_beacons(vif->type))
ath9k_beacon_remove_slot(sc, vif);
- ath9k_ps_wakeup(sc);
- ath9k_calculate_summary_state(hw, NULL);
- ath9k_ps_restore(sc);
-
ath_tx_node_cleanup(sc, &avp->mcast_node);
mutex_unlock(&sc->mutex);
@@ -1345,7 +1377,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
struct ieee80211_conf *conf = &hw->conf;
- bool reset_channel = false;
+ struct ath_chanctx *ctx = sc->cur_chan;
ath9k_ps_wakeup(sc);
mutex_lock(&sc->mutex);
@@ -1361,7 +1393,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
* The chip needs a reset to properly wake up from
* full sleep
*/
- reset_channel = ah->chip_fullsleep;
+ ath_chanctx_set_channel(sc, ctx, &ctx->chandef);
}
}
@@ -1391,20 +1423,16 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed)
}
}
- if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) || reset_channel) {
- if (ath_set_channel(sc, &hw->conf.chandef) < 0) {
- ath_err(common, "Unable to set channel\n");
- mutex_unlock(&sc->mutex);
- ath9k_ps_restore(sc);
- return -EINVAL;
- }
+ if (!ath9k_use_chanctx && (changed & IEEE80211_CONF_CHANGE_CHANNEL)) {
+ ctx->offchannel = !!(conf->flags & IEEE80211_CONF_OFFCHANNEL);
+ ath_chanctx_set_channel(sc, ctx, &hw->conf.chandef);
}
if (changed & IEEE80211_CONF_CHANGE_POWER) {
ath_dbg(common, CONFIG, "Set power: %d\n", conf->power_level);
- sc->config.txpowlimit = 2 * conf->power_level;
+ sc->cur_chan->txpower = 2 * conf->power_level;
ath9k_cmn_update_txpow(ah, sc->curtxpow,
- sc->config.txpowlimit, &sc->curtxpow);
+ sc->cur_chan->txpower, &sc->curtxpow);
}
mutex_unlock(&sc->mutex);
@@ -1659,58 +1687,6 @@ static int ath9k_set_key(struct ieee80211_hw *hw,
return ret;
}
-static void ath9k_set_assoc_state(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;
- struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
- unsigned long flags;
-
- set_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
- avp->primary_sta_vif = true;
-
- /*
- * Set the AID, BSSID and do beacon-sync only when
- * the HW opmode is STATION.
- *
- * But the primary bit is set above in any case.
- */
- if (sc->sc_ah->opmode != NL80211_IFTYPE_STATION)
- return;
-
- memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
- common->curaid = bss_conf->aid;
- ath9k_hw_write_associd(sc->sc_ah);
-
- common->last_rssi = ATH_RSSI_DUMMY_MARKER;
- sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
-
- spin_lock_irqsave(&sc->sc_pm_lock, flags);
- sc->ps_flags |= PS_BEACON_SYNC | PS_WAIT_FOR_BEACON;
- spin_unlock_irqrestore(&sc->sc_pm_lock, flags);
-
- if (ath9k_hw_mci_is_enabled(sc->sc_ah))
- ath9k_mci_update_wlan_channels(sc, false);
-
- ath_dbg(common, CONFIG,
- "Primary Station interface: %pM, BSSID: %pM\n",
- vif->addr, common->curbssid);
-}
-
-static void ath9k_bss_assoc_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
-{
- struct ath_softc *sc = data;
- struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
- struct ath_common *common = ath9k_hw_common(sc->sc_ah);
-
- if (test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags))
- return;
-
- if (bss_conf->assoc)
- ath9k_set_assoc_state(sc, vif);
-}
-
void ath9k_p2p_ps_timer(void *priv)
{
struct ath_softc *sc = priv;
@@ -1720,7 +1696,11 @@ void ath9k_p2p_ps_timer(void *priv)
struct ath_node *an;
u32 tsf;
- if (!avp)
+ del_timer_sync(&sc->sched.timer);
+ ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer);
+ ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER);
+
+ if (!avp || avp->chanctx != sc->cur_chan)
return;
tsf = ath9k_hw_gettsf32(sc->sc_ah);
@@ -1795,26 +1775,9 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
ath_dbg(common, CONFIG, "BSSID %pM Changed ASSOC %d\n",
bss_conf->bssid, bss_conf->assoc);
- if (avp->primary_sta_vif && !bss_conf->assoc) {
- clear_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags);
- avp->primary_sta_vif = false;
-
- if (ah->opmode == NL80211_IFTYPE_STATION)
- clear_bit(ATH_OP_BEACONS, &common->op_flags);
- }
-
- ieee80211_iterate_active_interfaces_atomic(
- sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
- ath9k_bss_assoc_iter, sc);
-
- if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags) &&
- ah->opmode == NL80211_IFTYPE_STATION) {
- memset(common->curbssid, 0, ETH_ALEN);
- common->curaid = 0;
- ath9k_hw_write_associd(sc->sc_ah);
- if (ath9k_hw_mci_is_enabled(sc->sc_ah))
- ath9k_mci_update_wlan_channels(sc, true);
- }
+ ath9k_calculate_summary_state(sc, avp->chanctx);
+ if (bss_conf->assoc)
+ ath_chanctx_event(sc, vif, ATH_CHANCTX_EVENT_ASSOC);
}
if (changed & BSS_CHANGED_IBSS) {
@@ -1824,10 +1787,15 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
}
if ((changed & BSS_CHANGED_BEACON_ENABLED) ||
- (changed & BSS_CHANGED_BEACON_INT))
+ (changed & BSS_CHANGED_BEACON_INT) ||
+ (changed & BSS_CHANGED_BEACON_INFO)) {
+ if (changed & BSS_CHANGED_BEACON_ENABLED)
+ ath9k_calculate_summary_state(sc, avp->chanctx);
ath9k_beacon_config(sc, vif, changed);
+ }
- if (changed & BSS_CHANGED_ERP_SLOT) {
+ if ((avp->chanctx == sc->cur_chan) &&
+ (changed & BSS_CHANGED_ERP_SLOT)) {
if (bss_conf->use_short_slot)
slottime = 9;
else
@@ -2032,23 +2000,30 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
u32 queues, bool drop)
{
struct ath_softc *sc = hw->priv;
+
+ mutex_lock(&sc->mutex);
+ __ath9k_flush(hw, queues, drop);
+ mutex_unlock(&sc->mutex);
+}
+
+void __ath9k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
+{
+ struct ath_softc *sc = hw->priv;
struct ath_hw *ah = sc->sc_ah;
struct ath_common *common = ath9k_hw_common(ah);
int timeout = HZ / 5; /* 200 ms */
bool drain_txq;
+ int i;
- mutex_lock(&sc->mutex);
cancel_delayed_work_sync(&sc->tx_complete_work);
if (ah->ah_flags & AH_UNPLUGGED) {
ath_dbg(common, ANY, "Device has been unplugged!\n");
- mutex_unlock(&sc->mutex);
return;
}
if (test_bit(ATH_OP_INVALID, &common->op_flags)) {
ath_dbg(common, ANY, "Device not present\n");
- mutex_unlock(&sc->mutex);
return;
}
@@ -2066,11 +2041,13 @@ static void ath9k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
ath_reset(sc);
ath9k_ps_restore(sc);
- ieee80211_wake_queues(hw);
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ ieee80211_wake_queue(sc->hw,
+ sc->cur_chan->hw_queue_base + i);
+ }
}
ieee80211_queue_delayed_work(hw, &sc->tx_complete_work, 0);
- mutex_unlock(&sc->mutex);
}
static bool ath9k_tx_frames_pending(struct ieee80211_hw *hw)
@@ -2230,6 +2207,403 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw)
clear_bit(ATH_OP_SCANNING, &common->op_flags);
}
+static int ath_scan_channel_duration(struct ath_softc *sc,
+ struct ieee80211_channel *chan)
+{
+ struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+
+ if (!req->n_ssids || (chan->flags & IEEE80211_CHAN_NO_IR))
+ return (HZ / 9); /* ~110 ms */
+
+ return (HZ / 16); /* ~60 ms */
+}
+
+static void
+ath_scan_next_channel(struct ath_softc *sc)
+{
+ struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+ struct ieee80211_channel *chan;
+
+ if (sc->offchannel.scan_idx >= req->n_channels) {
+ sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+ ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
+ NULL);
+ return;
+ }
+
+ chan = req->channels[sc->offchannel.scan_idx++];
+ sc->offchannel.duration = ath_scan_channel_duration(sc, chan);
+ sc->offchannel.state = ATH_OFFCHANNEL_PROBE_SEND;
+ ath_chanctx_offchan_switch(sc, chan);
+}
+
+static void ath_offchannel_next(struct ath_softc *sc)
+{
+ struct ieee80211_vif *vif;
+
+ if (sc->offchannel.scan_req) {
+ vif = sc->offchannel.scan_vif;
+ sc->offchannel.chan.txpower = vif->bss_conf.txpower;
+ ath_scan_next_channel(sc);
+ } else if (sc->offchannel.roc_vif) {
+ vif = sc->offchannel.roc_vif;
+ sc->offchannel.chan.txpower = vif->bss_conf.txpower;
+ sc->offchannel.duration = sc->offchannel.roc_duration;
+ sc->offchannel.state = ATH_OFFCHANNEL_ROC_START;
+ ath_chanctx_offchan_switch(sc, sc->offchannel.roc_chan);
+ } else {
+ ath_chanctx_switch(sc, ath_chanctx_get_oper_chan(sc, false),
+ NULL);
+ sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+ if (sc->ps_idle)
+ ath_cancel_work(sc);
+ }
+}
+
+static void ath_roc_complete(struct ath_softc *sc, bool abort)
+{
+ sc->offchannel.roc_vif = NULL;
+ sc->offchannel.roc_chan = NULL;
+ if (!abort)
+ ieee80211_remain_on_channel_expired(sc->hw);
+ ath_offchannel_next(sc);
+ ath9k_ps_restore(sc);
+}
+
+static void ath_scan_complete(struct ath_softc *sc, bool abort)
+{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+
+ sc->offchannel.scan_req = NULL;
+ sc->offchannel.scan_vif = NULL;
+ sc->offchannel.state = ATH_OFFCHANNEL_IDLE;
+ ieee80211_scan_completed(sc->hw, abort);
+ clear_bit(ATH_OP_SCANNING, &common->op_flags);
+ ath_offchannel_next(sc);
+ ath9k_ps_restore(sc);
+}
+
+static void ath_scan_send_probe(struct ath_softc *sc,
+ struct cfg80211_ssid *ssid)
+{
+ struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+ struct ieee80211_vif *vif = sc->offchannel.scan_vif;
+ struct ath_tx_control txctl = {};
+ struct sk_buff *skb;
+ struct ieee80211_tx_info *info;
+ int band = sc->offchannel.chan.chandef.chan->band;
+
+ skb = ieee80211_probereq_get(sc->hw, vif,
+ ssid->ssid, ssid->ssid_len, req->ie_len);
+ if (!skb)
+ return;
+
+ info = IEEE80211_SKB_CB(skb);
+ if (req->no_cck)
+ info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
+
+ if (req->ie_len)
+ memcpy(skb_put(skb, req->ie_len), req->ie, req->ie_len);
+
+ skb_set_queue_mapping(skb, IEEE80211_AC_VO);
+
+ if (!ieee80211_tx_prepare_skb(sc->hw, vif, skb, band, NULL))
+ goto error;
+
+ txctl.txq = sc->tx.txq_map[IEEE80211_AC_VO];
+ txctl.force_channel = true;
+ if (ath_tx_start(sc->hw, skb, &txctl))
+ goto error;
+
+ return;
+
+error:
+ ieee80211_free_txskb(sc->hw, skb);
+}
+
+static void ath_scan_channel_start(struct ath_softc *sc)
+{
+ struct cfg80211_scan_request *req = sc->offchannel.scan_req;
+ int i;
+
+ if (!(sc->cur_chan->chandef.chan->flags & IEEE80211_CHAN_NO_IR) &&
+ req->n_ssids) {
+ for (i = 0; i < req->n_ssids; i++)
+ ath_scan_send_probe(sc, &req->ssids[i]);
+
+ }
+
+ sc->offchannel.state = ATH_OFFCHANNEL_PROBE_WAIT;
+ mod_timer(&sc->offchannel.timer, jiffies + sc->offchannel.duration);
+}
+
+void ath_offchannel_channel_change(struct ath_softc *sc)
+{
+ switch (sc->offchannel.state) {
+ case ATH_OFFCHANNEL_PROBE_SEND:
+ if (!sc->offchannel.scan_req)
+ return;
+
+ if (sc->cur_chan->chandef.chan !=
+ sc->offchannel.chan.chandef.chan)
+ return;
+
+ ath_scan_channel_start(sc);
+ break;
+ case ATH_OFFCHANNEL_IDLE:
+ if (!sc->offchannel.scan_req)
+ return;
+
+ ath_scan_complete(sc, false);
+ break;
+ case ATH_OFFCHANNEL_ROC_START:
+ if (sc->cur_chan != &sc->offchannel.chan)
+ break;
+
+ sc->offchannel.state = ATH_OFFCHANNEL_ROC_WAIT;
+ mod_timer(&sc->offchannel.timer, jiffies +
+ msecs_to_jiffies(sc->offchannel.duration));
+ ieee80211_ready_on_channel(sc->hw);
+ break;
+ case ATH_OFFCHANNEL_ROC_DONE:
+ ath_roc_complete(sc, false);
+ break;
+ default:
+ break;
+ }
+}
+
+void ath_offchannel_timer(unsigned long data)
+{
+ struct ath_softc *sc = (struct ath_softc *)data;
+ struct ath_chanctx *ctx;
+
+ switch (sc->offchannel.state) {
+ case ATH_OFFCHANNEL_PROBE_WAIT:
+ if (!sc->offchannel.scan_req)
+ return;
+
+ /* get first active channel context */
+ ctx = ath_chanctx_get_oper_chan(sc, true);
+ if (ctx->active) {
+ sc->offchannel.state = ATH_OFFCHANNEL_SUSPEND;
+ ath_chanctx_switch(sc, ctx, NULL);
+ mod_timer(&sc->offchannel.timer, jiffies + HZ / 10);
+ break;
+ }
+ /* fall through */
+ case ATH_OFFCHANNEL_SUSPEND:
+ if (!sc->offchannel.scan_req)
+ return;
+
+ ath_scan_next_channel(sc);
+ break;
+ case ATH_OFFCHANNEL_ROC_START:
+ case ATH_OFFCHANNEL_ROC_WAIT:
+ ctx = ath_chanctx_get_oper_chan(sc, false);
+ sc->offchannel.state = ATH_OFFCHANNEL_ROC_DONE;
+ ath_chanctx_switch(sc, ctx, NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+static int ath9k_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
+{
+ struct cfg80211_scan_request *req = &hw_req->req;
+ struct ath_softc *sc = hw->priv;
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
+ int ret = 0;
+
+ mutex_lock(&sc->mutex);
+
+ if (WARN_ON(sc->offchannel.scan_req)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ath9k_ps_wakeup(sc);
+ set_bit(ATH_OP_SCANNING, &common->op_flags);
+ sc->offchannel.scan_vif = vif;
+ sc->offchannel.scan_req = req;
+ sc->offchannel.scan_idx = 0;
+
+ if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE)
+ ath_offchannel_next(sc);
+
+out:
+ mutex_unlock(&sc->mutex);
+
+ return ret;
+}
+
+static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct ath_softc *sc = hw->priv;
+
+ mutex_lock(&sc->mutex);
+ del_timer_sync(&sc->offchannel.timer);
+ ath_scan_complete(sc, true);
+ mutex_unlock(&sc->mutex);
+}
+
+static int ath9k_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *chan, int duration,
+ enum ieee80211_roc_type type)
+{
+ struct ath_softc *sc = hw->priv;
+ int ret = 0;
+
+ mutex_lock(&sc->mutex);
+
+ if (WARN_ON(sc->offchannel.roc_vif)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ath9k_ps_wakeup(sc);
+ sc->offchannel.roc_vif = vif;
+ sc->offchannel.roc_chan = chan;
+ sc->offchannel.roc_duration = duration;
+
+ if (sc->offchannel.state == ATH_OFFCHANNEL_IDLE)
+ ath_offchannel_next(sc);
+
+out:
+ mutex_unlock(&sc->mutex);
+
+ return ret;
+}
+
+static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+ struct ath_softc *sc = hw->priv;
+
+ mutex_lock(&sc->mutex);
+
+ del_timer_sync(&sc->offchannel.timer);
+
+ if (sc->offchannel.roc_vif) {
+ if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START)
+ ath_roc_complete(sc, true);
+ }
+
+ mutex_unlock(&sc->mutex);
+
+ return 0;
+}
+
+static int ath9k_add_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_chanctx *ctx, **ptr;
+ int pos;
+
+ mutex_lock(&sc->mutex);
+
+ ath_for_each_chanctx(sc, ctx) {
+ if (ctx->assigned)
+ continue;
+
+ ptr = (void *) conf->drv_priv;
+ *ptr = ctx;
+ ctx->assigned = true;
+ pos = ctx - &sc->chanctx[0];
+ ctx->hw_queue_base = pos * IEEE80211_NUM_ACS;
+ ath_chanctx_set_channel(sc, ctx, &conf->def);
+ mutex_unlock(&sc->mutex);
+ return 0;
+ }
+ mutex_unlock(&sc->mutex);
+ return -ENOSPC;
+}
+
+
+static void ath9k_remove_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_chanctx *ctx = ath_chanctx_get(conf);
+
+ mutex_lock(&sc->mutex);
+ ctx->assigned = false;
+ ctx->hw_queue_base = -1;
+ ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_UNASSIGN);
+ mutex_unlock(&sc->mutex);
+}
+
+static void ath9k_change_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *conf,
+ u32 changed)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_chanctx *ctx = ath_chanctx_get(conf);
+
+ mutex_lock(&sc->mutex);
+ ath_chanctx_set_channel(sc, ctx, &conf->def);
+ mutex_unlock(&sc->mutex);
+}
+
+static int ath9k_assign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ struct ath_chanctx *ctx = ath_chanctx_get(conf);
+ int i;
+
+ mutex_lock(&sc->mutex);
+ avp->chanctx = ctx;
+ list_add_tail(&avp->list, &ctx->vifs);
+ ath9k_calculate_summary_state(sc, ctx);
+ for (i = 0; i < IEEE80211_NUM_ACS; i++)
+ vif->hw_queue[i] = ctx->hw_queue_base + i;
+ mutex_unlock(&sc->mutex);
+
+ return 0;
+}
+
+static void ath9k_unassign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ath_softc *sc = hw->priv;
+ struct ath_vif *avp = (void *)vif->drv_priv;
+ struct ath_chanctx *ctx = ath_chanctx_get(conf);
+ int ac;
+
+ mutex_lock(&sc->mutex);
+ avp->chanctx = NULL;
+ list_del(&avp->list);
+ ath9k_calculate_summary_state(sc, ctx);
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
+ vif->hw_queue[ac] = IEEE80211_INVAL_HW_QUEUE;
+ mutex_unlock(&sc->mutex);
+}
+
+void ath9k_fill_chanctx_ops(void)
+{
+ if (!ath9k_use_chanctx)
+ return;
+
+ ath9k_ops.hw_scan = ath9k_hw_scan;
+ ath9k_ops.cancel_hw_scan = ath9k_cancel_hw_scan;
+ ath9k_ops.remain_on_channel = ath9k_remain_on_channel;
+ ath9k_ops.cancel_remain_on_channel = ath9k_cancel_remain_on_channel;
+ ath9k_ops.add_chanctx = ath9k_add_chanctx;
+ ath9k_ops.remove_chanctx = ath9k_remove_chanctx;
+ ath9k_ops.change_chanctx = ath9k_change_chanctx;
+ ath9k_ops.assign_vif_chanctx = ath9k_assign_vif_chanctx;
+ ath9k_ops.unassign_vif_chanctx = ath9k_unassign_vif_chanctx;
+ ath9k_ops.mgd_prepare_tx = ath9k_chanctx_force_active;
+}
+
struct ieee80211_ops ath9k_ops = {
.tx = ath9k_tx,
.start = ath9k_start,
diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c
index a0dbcc412384..3f7a11edb82a 100644
--- a/drivers/net/wireless/ath/ath9k/mci.c
+++ b/drivers/net/wireless/ath/ath9k/mci.c
@@ -706,7 +706,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
return;
if (setchannel) {
- struct ath9k_hw_cal_data *caldata = &sc->caldata;
+ struct ath9k_hw_cal_data *caldata = &sc->cur_chan->caldata;
if (IS_CHAN_HT40PLUS(ah->curchan) &&
(ah->curchan->channel > caldata->channel) &&
(ah->curchan->channel <= caldata->channel + 20))
@@ -720,7 +720,7 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel,
mci_hw->concur_tx = concur_tx;
if (old_concur_tx != mci_hw->concur_tx)
- ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false);
+ ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false);
}
static void ath9k_mci_stomp_audio(struct ath_softc *sc)
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 4dec09e565ed..7a2b2c5caced 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -843,6 +843,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return -ENODEV;
}
+ ath9k_fill_chanctx_ops();
hw = ieee80211_alloc_hw(sizeof(struct ath_softc), &ath9k_ops);
if (!hw) {
dev_err(&pdev->dev, "No memory for ieee80211_hw\n");
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 9105a92364f7..74ab1d02013b 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -259,7 +259,7 @@ static void ath_edma_start_recv(struct ath_softc *sc)
ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_HP);
ath_rx_addbuffer_edma(sc, ATH9K_RX_QUEUE_LP);
ath_opmode_init(sc);
- ath9k_hw_startpcureceive(sc->sc_ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
+ ath9k_hw_startpcureceive(sc->sc_ah, sc->cur_chan->offchannel);
}
static void ath_edma_stop_recv(struct ath_softc *sc)
@@ -374,6 +374,7 @@ void ath_rx_cleanup(struct ath_softc *sc)
u32 ath_calcrxfilter(struct ath_softc *sc)
{
+ struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u32 rfilt;
if (config_enabled(CONFIG_ATH9K_TX99))
@@ -424,6 +425,10 @@ u32 ath_calcrxfilter(struct ath_softc *sc)
if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah))
rfilt |= ATH9K_RX_FILTER_4ADDRESS;
+ if (ath9k_use_chanctx &&
+ test_bit(ATH_OP_SCANNING, &common->op_flags))
+ rfilt |= ATH9K_RX_FILTER_BEACON;
+
return rfilt;
}
@@ -457,7 +462,7 @@ int ath_startrecv(struct ath_softc *sc)
start_recv:
ath_opmode_init(sc);
- ath9k_hw_startpcureceive(ah, !!(sc->hw->conf.flags & IEEE80211_CONF_OFFCHANNEL));
+ ath9k_hw_startpcureceive(ah, sc->cur_chan->offchannel);
return 0;
}
@@ -540,7 +545,7 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb)
sc->ps_flags &= ~PS_BEACON_SYNC;
ath_dbg(common, PS,
"Reconfigure beacon timers based on synchronized timestamp\n");
- if (!(WARN_ON_ONCE(sc->cur_beacon_conf.beacon_interval == 0)))
+ if (!(WARN_ON_ONCE(sc->cur_chan->beacon.beacon_interval == 0)))
ath9k_set_beacon(sc);
if (sc->p2p_ps_vif)
ath9k_update_p2p_ps(sc, sc->p2p_ps_vif->vif);
@@ -887,6 +892,11 @@ static int ath9k_rx_skb_preprocess(struct ath_softc *sc,
return -EINVAL;
}
+ if (rx_stats->is_mybeacon) {
+ sc->sched.next_tbtt = rx_stats->rs_tstamp;
+ ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_BEACON_RECEIVED);
+ }
+
ath9k_cmn_process_rssi(common, hw, rx_stats, rx_status);
rx_status->band = ah->curchan->chan->band;
diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h
index f1bbce3f7774..a1499700bcf2 100644
--- a/drivers/net/wireless/ath/ath9k/reg.h
+++ b/drivers/net/wireless/ath/ath9k/reg.h
@@ -813,6 +813,7 @@
#define AR_SREV_VERSION_9531 0x500
#define AR_SREV_REVISION_9531_10 0
#define AR_SREV_REVISION_9531_11 1
+#define AR_SREV_REVISION_9531_20 2
#define AR_SREV_5416(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \
@@ -958,6 +959,9 @@
#define AR_SREV_9531_11(_ah) \
(((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \
((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_11))
+#define AR_SREV_9531_20(_ah) \
+ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \
+ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_20))
/* NOTE: When adding chips newer than Peacock, add chip check here */
#define AR_SREV_9580_10_OR_LATER(_ah) \
diff --git a/drivers/net/wireless/ath/ath9k/tx99.c b/drivers/net/wireless/ath/ath9k/tx99.c
index a65cfb91adca..23972924c774 100644
--- a/drivers/net/wireless/ath/ath9k/tx99.c
+++ b/drivers/net/wireless/ath/ath9k/tx99.c
@@ -76,7 +76,7 @@ static struct sk_buff *ath9k_build_tx99_skb(struct ath_softc *sc)
tx_info = IEEE80211_SKB_CB(skb);
memset(tx_info, 0, sizeof(*tx_info));
rate = &tx_info->control.rates[0];
- tx_info->band = hw->conf.chandef.chan->band;
+ tx_info->band = sc->cur_chan->chandef.chan->band;
tx_info->flags = IEEE80211_TX_CTL_NO_ACK;
tx_info->control.vif = sc->tx99_vif;
rate->count = 1;
diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c
index 2879887f5691..a4f4f0da81f6 100644
--- a/drivers/net/wireless/ath/ath9k/wow.c
+++ b/drivers/net/wireless/ath/ath9k/wow.c
@@ -193,6 +193,7 @@ int ath9k_suspend(struct ieee80211_hw *hw,
u32 wow_triggers_enabled = 0;
int ret = 0;
+ cancel_work_sync(&sc->chanctx_work);
mutex_lock(&sc->mutex);
ath_cancel_work(sc);
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index 66acb2cbd9df..d4927c9a6bae 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -103,9 +103,16 @@ void ath_txq_unlock_complete(struct ath_softc *sc, struct ath_txq *txq)
ieee80211_tx_status(sc->hw, skb);
}
-static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
+static void ath_tx_queue_tid(struct ath_softc *sc, struct ath_txq *txq,
+ struct ath_atx_tid *tid)
{
struct ath_atx_ac *ac = tid->ac;
+ struct list_head *list;
+ struct ath_vif *avp = (struct ath_vif *) tid->an->vif->drv_priv;
+ struct ath_chanctx *ctx = avp->chanctx;
+
+ if (!ctx)
+ return;
if (tid->sched)
return;
@@ -117,7 +124,9 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
return;
ac->sched = true;
- list_add_tail(&ac->list, &txq->axq_acq);
+
+ list = &ctx->acq[TID_TO_WME_AC(tid->tidno)];
+ list_add_tail(&ac->list, list);
}
static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
@@ -147,7 +156,8 @@ static void ath_set_rates(struct ieee80211_vif *vif, struct ieee80211_sta *sta,
static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
struct sk_buff *skb)
{
- int q;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int q, hw_queue;
q = skb_get_queue_mapping(skb);
if (txq == sc->tx.uapsdq)
@@ -159,9 +169,10 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
if (WARN_ON(--txq->pending_frames < 0))
txq->pending_frames = 0;
+ hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue;
if (txq->stopped &&
txq->pending_frames < sc->tx.txq_max_pending[q]) {
- ieee80211_wake_queue(sc->hw, q);
+ ieee80211_wake_queue(sc->hw, hw_queue);
txq->stopped = false;
}
}
@@ -626,7 +637,7 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
skb_queue_splice_tail(&bf_pending, &tid->retry_q);
if (!an->sleeping) {
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(sc, txq, tid);
if (ts->ts_status & (ATH9K_TXERR_FILT | ATH9K_TXERR_XRETRY))
tid->ac->clear_ps_filter = true;
@@ -1483,7 +1494,7 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an)
ac->clear_ps_filter = true;
if (ath_tid_has_buffered(tid)) {
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(sc, txq, tid);
ath_txq_schedule(sc, txq);
}
@@ -1507,7 +1518,7 @@ void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
tid->baw_size = IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
if (ath_tid_has_buffered(tid)) {
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(sc, txq, tid);
ath_txq_schedule(sc, txq);
}
@@ -1642,7 +1653,6 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype)
txq->axq_link = NULL;
__skb_queue_head_init(&txq->complete_q);
INIT_LIST_HEAD(&txq->axq_q);
- INIT_LIST_HEAD(&txq->axq_acq);
spin_lock_init(&txq->axq_lock);
txq->axq_depth = 0;
txq->axq_ampdu_depth = 0;
@@ -1686,7 +1696,7 @@ int ath_txq_update(struct ath_softc *sc, int qnum,
int ath_cabq_update(struct ath_softc *sc)
{
struct ath9k_tx_queue_info qi;
- struct ath_beacon_config *cur_conf = &sc->cur_beacon_conf;
+ struct ath_beacon_config *cur_conf = &sc->cur_chan->beacon;
int qnum = sc->beacon.cabq->axq_qnum;
ath9k_hw_get_txq_props(sc->sc_ah, qnum, &qi);
@@ -1804,7 +1814,7 @@ void ath_tx_cleanupq(struct ath_softc *sc, struct ath_txq *txq)
sc->tx.txqsetup &= ~(1<<txq->axq_qnum);
}
-/* For each axq_acq entry, for each tid, try to schedule packets
+/* For each acq entry, for each tid, try to schedule packets
* for transmit until ampdu_depth has reached min Q depth.
*/
void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
@@ -1812,19 +1822,31 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
struct ath_atx_ac *ac, *last_ac;
struct ath_atx_tid *tid, *last_tid;
+ struct list_head *ac_list;
bool sent = false;
+ if (txq->mac80211_qnum < 0)
+ return;
+
+ spin_lock_bh(&sc->chan_lock);
+ ac_list = &sc->cur_chan->acq[txq->mac80211_qnum];
+ spin_unlock_bh(&sc->chan_lock);
+
if (test_bit(ATH_OP_HW_RESET, &common->op_flags) ||
- list_empty(&txq->axq_acq))
+ list_empty(ac_list))
return;
+ spin_lock_bh(&sc->chan_lock);
rcu_read_lock();
- last_ac = list_entry(txq->axq_acq.prev, struct ath_atx_ac, list);
- while (!list_empty(&txq->axq_acq)) {
+ last_ac = list_entry(ac_list->prev, struct ath_atx_ac, list);
+ while (!list_empty(ac_list)) {
bool stop = false;
- ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, list);
+ if (sc->cur_chan->stopped)
+ break;
+
+ ac = list_first_entry(ac_list, struct ath_atx_ac, list);
last_tid = list_entry(ac->tid_q.prev, struct ath_atx_tid, list);
list_del(&ac->list);
ac->sched = false;
@@ -1844,7 +1866,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
* are pending for the tid
*/
if (ath_tid_has_buffered(tid))
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(sc, txq, tid);
if (stop || tid == last_tid)
break;
@@ -1852,7 +1874,7 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
if (!list_empty(&ac->tid_q) && !ac->sched) {
ac->sched = true;
- list_add_tail(&ac->list, &txq->axq_acq);
+ list_add_tail(&ac->list, ac_list);
}
if (stop)
@@ -1863,12 +1885,27 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
break;
sent = false;
- last_ac = list_entry(txq->axq_acq.prev,
+ last_ac = list_entry(ac_list->prev,
struct ath_atx_ac, list);
}
}
rcu_read_unlock();
+ spin_unlock_bh(&sc->chan_lock);
+}
+
+void ath_txq_schedule_all(struct ath_softc *sc)
+{
+ struct ath_txq *txq;
+ int i;
+
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ txq = sc->tx.txq_map[i];
+
+ spin_lock_bh(&txq->axq_lock);
+ ath_txq_schedule(sc, txq);
+ spin_unlock_bh(&txq->axq_lock);
+ }
}
/***********/
@@ -2150,13 +2187,21 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_sta *sta = txctl->sta;
struct ieee80211_vif *vif = info->control.vif;
+ struct ath_vif *avp = NULL;
struct ath_softc *sc = hw->priv;
struct ath_txq *txq = txctl->txq;
struct ath_atx_tid *tid = NULL;
struct ath_buf *bf;
- int q;
+ bool queue;
+ int q, hw_queue;
int ret;
+ if (vif)
+ avp = (void *)vif->drv_priv;
+
+ if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
+ txctl->force_channel = true;
+
ret = ath_tx_prepare(hw, skb, txctl);
if (ret)
return ret;
@@ -2168,24 +2213,39 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
*/
q = skb_get_queue_mapping(skb);
+ hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue;
ath_txq_lock(sc, txq);
if (txq == sc->tx.txq_map[q] &&
++txq->pending_frames > sc->tx.txq_max_pending[q] &&
!txq->stopped) {
- ieee80211_stop_queue(sc->hw, q);
+ ieee80211_stop_queue(sc->hw, hw_queue);
txq->stopped = true;
}
- if (txctl->an && ieee80211_is_data_present(hdr->frame_control))
+ queue = ieee80211_is_data_present(hdr->frame_control);
+
+ /* Force queueing of all frames that belong to a virtual interface on
+ * a different channel context, to ensure that they are sent on the
+ * correct channel.
+ */
+ if (((avp && avp->chanctx != sc->cur_chan) ||
+ sc->cur_chan->stopped) && !txctl->force_channel) {
+ if (!txctl->an)
+ txctl->an = &avp->mcast_node;
+ info->flags &= ~IEEE80211_TX_CTL_PS_RESPONSE;
+ queue = true;
+ }
+
+ if (txctl->an && queue)
tid = ath_get_skb_tid(sc, txctl->an, skb);
- if (info->flags & IEEE80211_TX_CTL_PS_RESPONSE) {
+ if (info->flags & (IEEE80211_TX_CTL_PS_RESPONSE |
+ IEEE80211_TX_CTL_TX_OFFCHAN)) {
ath_txq_unlock(sc, txq);
txq = sc->tx.uapsdq;
ath_txq_lock(sc, txq);
- } else if (txctl->an &&
- ieee80211_is_data_present(hdr->frame_control)) {
+ } else if (txctl->an && queue) {
WARN_ON(tid->ac->txq != txctl->txq);
if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
@@ -2198,7 +2258,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb,
TX_STAT_INC(txq->axq_qnum, a_queued_sw);
__skb_queue_tail(&tid->buf_q, skb);
if (!txctl->an->sleeping)
- ath_tx_queue_tid(txq, tid);
+ ath_tx_queue_tid(sc, txq, tid);
ath_txq_schedule(sc, txq);
goto out;
@@ -2244,8 +2304,8 @@ void ath_tx_cabq(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
int max_duration;
max_duration =
- sc->cur_beacon_conf.beacon_interval * 1000 *
- sc->cur_beacon_conf.dtim_period / ATH_BCBUF;
+ sc->cur_chan->beacon.beacon_interval * 1000 *
+ sc->cur_chan->beacon.dtim_period / ATH_BCBUF;
do {
struct ath_frame_info *fi = get_frame_info(skb);
@@ -2560,6 +2620,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
sc->beacon.tx_processed = true;
sc->beacon.tx_last = !(ts.ts_status & ATH9K_TXERR_MASK);
+ ath_chanctx_event(sc, NULL,
+ ATH_CHANCTX_EVENT_BEACON_SENT);
ath9k_csa_update(sc);
continue;
}
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 820d4ebd9322..4ac2c208c9ba 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -104,8 +104,8 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type)
return -EOPNOTSUPP;
}
-static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
- struct station_info *sinfo)
+int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
+ struct station_info *sinfo)
{
struct wmi_notify_req_cmd cmd = {
.cid = cid,
@@ -287,6 +287,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
return -EBUSY;
}
+ wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
wil->scan_request = request;
mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
@@ -443,15 +444,15 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
return rc;
}
-static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- struct cfg80211_mgmt_tx_params *params,
- u64 *cookie)
+int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
{
const u8 *buf = params->buf;
size_t len = params->len;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
int rc;
+ bool tx_status = false;
struct ieee80211_mgmt *mgmt_frame = (void *)buf;
struct wmi_sw_tx_req_cmd *cmd;
struct {
@@ -460,8 +461,10 @@ static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
} __packed evt;
cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
- if (!cmd)
- return -ENOMEM;
+ if (!cmd) {
+ rc = -ENOMEM;
+ goto out;
+ }
memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN);
cmd->len = cpu_to_le16(len);
@@ -470,10 +473,12 @@ static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
if (rc == 0)
- rc = evt.evt.status;
+ tx_status = !evt.evt.status;
kfree(cmd);
-
+ out:
+ cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
+ tx_status, GFP_KERNEL);
return rc;
}
@@ -562,6 +567,34 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
return rc;
}
+static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
+{
+ print_hex_dump_bytes("head ", DUMP_PREFIX_OFFSET,
+ b->head, b->head_len);
+ print_hex_dump_bytes("tail ", DUMP_PREFIX_OFFSET,
+ b->tail, b->tail_len);
+ print_hex_dump_bytes("BCON IE ", DUMP_PREFIX_OFFSET,
+ b->beacon_ies, b->beacon_ies_len);
+ print_hex_dump_bytes("PROBE ", DUMP_PREFIX_OFFSET,
+ b->probe_resp, b->probe_resp_len);
+ print_hex_dump_bytes("PROBE IE ", DUMP_PREFIX_OFFSET,
+ b->proberesp_ies, b->proberesp_ies_len);
+ print_hex_dump_bytes("ASSOC IE ", DUMP_PREFIX_OFFSET,
+ b->assocresp_ies, b->assocresp_ies_len);
+}
+
+static void wil_print_crypto(struct wil6210_priv *wil,
+ struct cfg80211_crypto_settings *c)
+{
+ wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
+ c->wpa_versions, c->cipher_group);
+ wil_dbg_misc(wil, "Pairwise ciphers [%d]\n", c->n_ciphers_pairwise);
+ wil_dbg_misc(wil, "AKM suites [%d]\n", c->n_akm_suites);
+ wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
+ c->control_port, be16_to_cpu(c->control_port_ethertype),
+ c->control_port_no_encrypt);
+}
+
static int wil_fix_bcon(struct wil6210_priv *wil,
struct cfg80211_beacon_data *bcon)
{
@@ -595,8 +628,11 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
struct wireless_dev *wdev = ndev->ieee80211_ptr;
struct ieee80211_channel *channel = info->chandef.chan;
struct cfg80211_beacon_data *bcon = &info->beacon;
+ struct cfg80211_crypto_settings *crypto = &info->crypto;
u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
if (!channel) {
wil_err(wil, "AP: No channel???\n");
return -EINVAL;
@@ -604,11 +640,19 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy,
wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
channel->center_freq, info->privacy ? "secure" : "open");
+ wil_dbg_misc(wil, "Privacy: %d auth_type %d\n",
+ info->privacy, info->auth_type);
+ wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
+ info->dtim_period);
print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
info->ssid, info->ssid_len);
+ wil_print_bcon_data(bcon);
+ wil_print_crypto(wil, crypto);
- if (wil_fix_bcon(wil, bcon))
+ if (wil_fix_bcon(wil, bcon)) {
wil_dbg_misc(wil, "Fixed bcon\n");
+ wil_print_bcon_data(bcon);
+ }
mutex_lock(&wil->mutex);
@@ -663,6 +707,8 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
int rc = 0;
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+ wil_dbg_misc(wil, "%s()\n", __func__);
+
mutex_lock(&wil->mutex);
rc = wmi_pcp_stop(wil);
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 8d4bc4bfb664..a868c5eebe37 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -19,6 +19,7 @@
#include <linux/seq_file.h>
#include <linux/pci.h>
#include <linux/rtnetlink.h>
+#include <linux/power_supply.h>
#include "wil6210.h"
#include "txrx.h"
@@ -69,14 +70,32 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data)
for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) {
struct vring *vring = &(wil->vring_tx[i]);
+ struct vring_tx_data *txdata = &wil->vring_tx_data[i];
+
if (vring->va) {
int cid = wil->vring2cid_tid[i][0];
int tid = wil->vring2cid_tid[i][1];
+ u32 swhead = vring->swhead;
+ u32 swtail = vring->swtail;
+ int used = (vring->size + swhead - swtail)
+ % vring->size;
+ int avail = vring->size - used - 1;
char name[10];
+ /* performance monitoring */
+ cycles_t now = get_cycles();
+ cycles_t idle = txdata->idle * 100;
+ cycles_t total = now - txdata->begin;
+
+ do_div(idle, total);
+ txdata->begin = now;
+ txdata->idle = 0ULL;
+
snprintf(name, sizeof(name), "tx_%2d", i);
- seq_printf(s, "\n%pM CID %d TID %d\n",
- wil->sta[cid].addr, cid, tid);
+ seq_printf(s, "\n%pM CID %d TID %d [%3d|%3d] idle %3d%%\n",
+ wil->sta[cid].addr, cid, tid, used, avail,
+ (int)idle);
+
wil_print_vring(s, wil, name, vring, '_', 'H');
}
}
@@ -231,6 +250,26 @@ static struct dentry *wil_debugfs_create_iomem_x32(const char *name,
&fops_iomem_x32);
}
+static int wil_debugfs_ulong_set(void *data, u64 val)
+{
+ *(ulong *)data = val;
+ return 0;
+}
+static int wil_debugfs_ulong_get(void *data, u64 *val)
+{
+ *val = *(ulong *)data;
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(wil_fops_ulong, wil_debugfs_ulong_get,
+ wil_debugfs_ulong_set, "%llu\n");
+
+static struct dentry *wil_debugfs_create_ulong(const char *name, umode_t mode,
+ struct dentry *parent,
+ ulong *value)
+{
+ return debugfs_create_file(name, mode, parent, value, &wil_fops_ulong);
+}
+
static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil,
const char *name,
struct dentry *parent, u32 off)
@@ -284,11 +323,11 @@ static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil,
if (IS_ERR_OR_NULL(d))
return -ENODEV;
- wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr +
+ wil_debugfs_create_iomem_x32("TRSH", S_IRUGO | S_IWUSR, d, wil->csr +
HOSTADDR(RGF_DMA_ITR_CNT_TRSH));
- wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr +
+ wil_debugfs_create_iomem_x32("DATA", S_IRUGO | S_IWUSR, d, wil->csr +
HOSTADDR(RGF_DMA_ITR_CNT_DATA));
- wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr +
+ wil_debugfs_create_iomem_x32("CTL", S_IRUGO | S_IWUSR, d, wil->csr +
HOSTADDR(RGF_DMA_ITR_CNT_CRL));
return 0;
@@ -397,6 +436,124 @@ static const struct file_operations fops_reset = {
.write = wil_write_file_reset,
.open = simple_open,
};
+/*---write channel 1..4 to rxon for it, 0 to rxoff---*/
+static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct wil6210_priv *wil = file->private_data;
+ int rc;
+ long channel;
+ bool on;
+
+ char *kbuf = kmalloc(len + 1, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+ if (copy_from_user(kbuf, buf, len))
+ return -EIO;
+
+ kbuf[len] = '\0';
+ rc = kstrtol(kbuf, 0, &channel);
+ kfree(kbuf);
+ if (rc)
+ return rc;
+
+ if ((channel < 0) || (channel > 4)) {
+ wil_err(wil, "Invalid channel %ld\n", channel);
+ return -EINVAL;
+ }
+ on = !!channel;
+
+ if (on) {
+ rc = wmi_set_channel(wil, (int)channel);
+ if (rc)
+ return rc;
+ }
+
+ rc = wmi_rxon(wil, on);
+ if (rc)
+ return rc;
+
+ return len;
+}
+
+static const struct file_operations fops_rxon = {
+ .write = wil_write_file_rxon,
+ .open = simple_open,
+};
+/*---tx_mgmt---*/
+/* Write mgmt frame to this file to send it */
+static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct wil6210_priv *wil = file->private_data;
+ struct wiphy *wiphy = wil_to_wiphy(wil);
+ struct wireless_dev *wdev = wil_to_wdev(wil);
+ struct cfg80211_mgmt_tx_params params;
+ int rc;
+
+ void *frame = kmalloc(len, GFP_KERNEL);
+ if (!frame)
+ return -ENOMEM;
+
+ if (copy_from_user(frame, buf, len))
+ return -EIO;
+
+ params.buf = frame;
+ params.len = len;
+ params.chan = wdev->preset_chandef.chan;
+
+ rc = wil_cfg80211_mgmt_tx(wiphy, wdev, &params, NULL);
+
+ kfree(frame);
+ wil_info(wil, "%s() -> %d\n", __func__, rc);
+
+ return len;
+}
+
+static const struct file_operations fops_txmgmt = {
+ .write = wil_write_file_txmgmt,
+ .open = simple_open,
+};
+
+/* Write WMI command (w/o mbox header) to this file to send it
+ * WMI starts from wil6210_mbox_hdr_wmi header
+ */
+static ssize_t wil_write_file_wmi(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct wil6210_priv *wil = file->private_data;
+ struct wil6210_mbox_hdr_wmi *wmi;
+ void *cmd;
+ int cmdlen = len - sizeof(struct wil6210_mbox_hdr_wmi);
+ u16 cmdid;
+ int rc, rc1;
+
+ if (cmdlen <= 0)
+ return -EINVAL;
+
+ wmi = kmalloc(len, GFP_KERNEL);
+ if (!wmi)
+ return -ENOMEM;
+
+ rc = simple_write_to_buffer(wmi, len, ppos, buf, len);
+ if (rc < 0)
+ return rc;
+
+ cmd = &wmi[1];
+ cmdid = le16_to_cpu(wmi->id);
+
+ rc1 = wmi_send(wil, cmdid, cmd, cmdlen);
+ kfree(wmi);
+
+ wil_info(wil, "%s(0x%04x[%d]) -> %d\n", __func__, cmdid, cmdlen, rc1);
+
+ return rc;
+}
+
+static const struct file_operations fops_wmi = {
+ .write = wil_write_file_wmi,
+ .open = simple_open,
+};
static void wil_seq_hexdump(struct seq_file *s, void *p, int len,
const char *prefix)
@@ -600,8 +757,8 @@ static int wil_temp_debugfs_show(struct seq_file *s, void *data)
return 0;
}
- print_temp(s, "MAC temperature :", t_m);
- print_temp(s, "Radio temperature :", t_r);
+ print_temp(s, "T_mac =", t_m);
+ print_temp(s, "T_radio =", t_r);
return 0;
}
@@ -618,6 +775,130 @@ static const struct file_operations fops_temp = {
.llseek = seq_lseek,
};
+/*---------freq------------*/
+static int wil_freq_debugfs_show(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+ struct wireless_dev *wdev = wil_to_wdev(wil);
+ u16 freq = wdev->chandef.chan ? wdev->chandef.chan->center_freq : 0;
+
+ seq_printf(s, "Freq = %d\n", freq);
+
+ return 0;
+}
+
+static int wil_freq_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_freq_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_freq = {
+ .open = wil_freq_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+/*---------link------------*/
+static int wil_link_debugfs_show(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+ struct station_info sinfo;
+ int i, rc;
+
+ for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
+ struct wil_sta_info *p = &wil->sta[i];
+ char *status = "unknown";
+ switch (p->status) {
+ case wil_sta_unused:
+ status = "unused ";
+ break;
+ case wil_sta_conn_pending:
+ status = "pending ";
+ break;
+ case wil_sta_connected:
+ status = "connected";
+ break;
+ }
+ seq_printf(s, "[%d] %pM %s%s\n", i, p->addr, status,
+ (p->data_port_open ? " data_port_open" : ""));
+
+ if (p->status == wil_sta_connected) {
+ rc = wil_cid_fill_sinfo(wil, i, &sinfo);
+ if (rc)
+ return rc;
+
+ seq_printf(s, " Tx_mcs = %d\n", sinfo.txrate.mcs);
+ seq_printf(s, " Rx_mcs = %d\n", sinfo.rxrate.mcs);
+ seq_printf(s, " SQ = %d\n", sinfo.signal);
+ }
+ }
+
+ return 0;
+}
+
+static int wil_link_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_link_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_link = {
+ .open = wil_link_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+/*---------info------------*/
+static int wil_info_debugfs_show(struct seq_file *s, void *data)
+{
+ struct wil6210_priv *wil = s->private;
+ struct net_device *ndev = wil_to_ndev(wil);
+ int is_ac = power_supply_is_system_supplied();
+ int rx = atomic_xchg(&wil->isr_count_rx, 0);
+ int tx = atomic_xchg(&wil->isr_count_tx, 0);
+ static ulong rxf_old, txf_old;
+ ulong rxf = ndev->stats.rx_packets;
+ ulong txf = ndev->stats.tx_packets;
+ unsigned int i;
+
+ /* >0 : AC; 0 : battery; <0 : error */
+ seq_printf(s, "AC powered : %d\n", is_ac);
+ seq_printf(s, "Rx irqs:packets : %8d : %8ld\n", rx, rxf - rxf_old);
+ seq_printf(s, "Tx irqs:packets : %8d : %8ld\n", tx, txf - txf_old);
+ rxf_old = rxf;
+ txf_old = txf;
+
+
+#define CHECK_QSTATE(x) (state & BIT(__QUEUE_STATE_ ## x)) ? \
+ " " __stringify(x) : ""
+
+ for (i = 0; i < ndev->num_tx_queues; i++) {
+ struct netdev_queue *txq = netdev_get_tx_queue(ndev, i);
+ unsigned long state = txq->state;
+
+ seq_printf(s, "Tx queue[%i] state : 0x%lx%s%s%s\n", i, state,
+ CHECK_QSTATE(DRV_XOFF),
+ CHECK_QSTATE(STACK_XOFF),
+ CHECK_QSTATE(FROZEN)
+ );
+ }
+#undef CHECK_QSTATE
+ return 0;
+}
+
+static int wil_info_seq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, wil_info_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_info = {
+ .open = wil_info_seq_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
/*---------Station matrix------------*/
static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
{
@@ -630,7 +911,7 @@ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r)
else
seq_printf(s, "%c", r->reorder_buf[i] ? '*' : '_');
}
- seq_puts(s, "]\n");
+ seq_printf(s, "] last drop 0x%03x\n", r->ssn_last_drop);
}
static int wil_sta_debugfs_show(struct seq_file *s, void *data)
@@ -703,6 +984,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid);
debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg,
&wil->secure_pcp);
+ wil_debugfs_create_ulong("status", S_IRUGO | S_IWUSR, dbg,
+ &wil->status);
wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg,
HOSTADDR(RGF_USER_USER_ICR));
@@ -719,7 +1002,13 @@ int wil6210_debugfs_init(struct wil6210_priv *wil)
debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread);
debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset);
+ debugfs_create_file("rxon", S_IWUSR, dbg, wil, &fops_rxon);
+ debugfs_create_file("tx_mgmt", S_IWUSR, dbg, wil, &fops_txmgmt);
+ debugfs_create_file("wmi_send", S_IWUSR, dbg, wil, &fops_wmi);
debugfs_create_file("temp", S_IRUGO, dbg, wil, &fops_temp);
+ debugfs_create_file("freq", S_IRUGO, dbg, wil, &fops_freq);
+ debugfs_create_file("link", S_IRUGO, dbg, wil, &fops_link);
+ debugfs_create_file("info", S_IRUGO, dbg, wil, &fops_info);
wil->rgf_blob.data = (void * __force)wil->csr + 0;
wil->rgf_blob.size = 0xa000;
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c
index 73593aa3cd98..67f1002a03a1 100644
--- a/drivers/net/wireless/ath/wil6210/interrupt.c
+++ b/drivers/net/wireless/ath/wil6210/interrupt.c
@@ -208,6 +208,7 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie)
/* Rx IRQ will be enabled when NAPI processing finished */
+ atomic_inc(&wil->isr_count_rx);
return IRQ_HANDLED;
}
@@ -246,6 +247,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie)
/* Tx IRQ will be enabled when NAPI processing finished */
+ atomic_inc(&wil->isr_count_tx);
return IRQ_HANDLED;
}
@@ -257,6 +259,7 @@ static void wil_notify_fw_error(struct wil6210_priv *wil)
[1] = "EVENT=FW_ERROR",
[2] = NULL,
};
+ wil_err(wil, "Notify about firmware error\n");
kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
}
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index 11e6d9d22eae..53a689ed7c7d 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -61,11 +61,24 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src,
static void wil_disconnect_cid(struct wil6210_priv *wil, int cid)
{
uint i;
+ struct net_device *ndev = wil_to_ndev(wil);
+ struct wireless_dev *wdev = wil->wdev;
struct wil_sta_info *sta = &wil->sta[cid];
+ wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid,
+ sta->status);
sta->data_port_open = false;
if (sta->status != wil_sta_unused) {
wmi_disconnect_sta(wil, sta->addr, WLAN_REASON_DEAUTH_LEAVING);
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ /* AP-like interface */
+ cfg80211_del_sta(ndev, sta->addr, GFP_KERNEL);
+ break;
+ default:
+ break;
+ }
sta->status = wil_sta_unused;
}
@@ -119,11 +132,6 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid)
clear_bit(wil_status_fwconnecting, &wil->status);
break;
default:
- /* AP-like interface and monitor:
- * never scan, always connected
- */
- if (bssid)
- cfg80211_del_sta(ndev, bssid, GFP_KERNEL);
break;
}
}
@@ -465,6 +473,7 @@ void wil_link_on(struct wil6210_priv *wil)
wil_dbg_misc(wil, "%s()\n", __func__);
netif_carrier_on(ndev);
+ wil_dbg_misc(wil, "netif_tx_wake : link on\n");
netif_tx_wake_all_queues(ndev);
}
@@ -475,6 +484,7 @@ void wil_link_off(struct wil6210_priv *wil)
wil_dbg_misc(wil, "%s()\n", __func__);
netif_tx_stop_all_queues(ndev);
+ wil_dbg_misc(wil, "netif_tx_stop : link off\n");
netif_carrier_off(ndev);
}
@@ -552,6 +562,8 @@ static int __wil_down(struct wil6210_priv *wil)
napi_disable(&wil->napi_tx);
if (wil->scan_request) {
+ wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
+ wil->scan_request);
del_timer_sync(&wil->scan_timer);
cfg80211_scan_done(wil->scan_request, true);
wil->scan_request = NULL;
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c
index 1e2e07b9d13d..77b6272d93fb 100644
--- a/drivers/net/wireless/ath/wil6210/pcie_bus.c
+++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c
@@ -15,7 +15,6 @@
*/
#include <linux/module.h>
-#include <linux/debugfs.h>
#include <linux/pci.h>
#include <linux/moduleparam.h>
@@ -27,11 +26,22 @@ MODULE_PARM_DESC(use_msi,
" Use MSI interrupt: "
"0 - don't, 1 - (default) - single, or 3");
+static bool debug_fw; /* = false; */
+module_param(debug_fw, bool, S_IRUGO);
+MODULE_PARM_DESC(debug_fw, " load driver if FW not ready. For FW debug");
+
/* Bus ops */
static int wil_if_pcie_enable(struct wil6210_priv *wil)
{
struct pci_dev *pdev = wil->pdev;
int rc;
+ /* on platforms with buggy ACPI, pdev->msi_enabled may be set to
+ * allow pci_enable_device to work. This indicates INTx was not routed
+ * and only MSI should be used
+ */
+ int msi_only = pdev->msi_enabled;
+
+ pdev->msi_enabled = 0;
pci_set_master(pdev);
@@ -63,6 +73,12 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
wil->n_msi = use_msi;
+ if ((wil->n_msi == 0) && msi_only) {
+ wil_err(wil, "Interrupt pin not routed, unable to use INTx\n");
+ rc = -ENODEV;
+ goto stop_master;
+ }
+
rc = wil6210_init_irq(wil, pdev->irq);
if (rc)
goto stop_master;
@@ -71,6 +87,8 @@ static int wil_if_pcie_enable(struct wil6210_priv *wil)
mutex_lock(&wil->mutex);
rc = wil_reset(wil);
mutex_unlock(&wil->mutex);
+ if (debug_fw)
+ rc = 0;
if (rc)
goto release_irq;
@@ -119,9 +137,16 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
rc = pci_enable_device(pdev);
if (rc) {
- dev_err(&pdev->dev, "pci_enable_device failed\n");
- return -ENODEV;
+ dev_err(&pdev->dev,
+ "pci_enable_device failed, retry with MSI only\n");
+ /* Work around for platforms that can't allocate IRQ:
+ * retry with MSI only
+ */
+ pdev->msi_enabled = 1;
+ rc = pci_enable_device(pdev);
}
+ if (rc)
+ return -ENODEV;
/* rollback to err_disable_pdev */
rc = pci_request_region(pdev, 0, WIL_NAME);
diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c
index 747ae1275877..180ca4793904 100644
--- a/drivers/net/wireless/ath/wil6210/rx_reorder.c
+++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c
@@ -116,6 +116,7 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb)
/* frame with out of date sequence number */
if (seq_less(seq, r->head_seq_num)) {
+ r->ssn_last_drop = seq;
dev_kfree_skb(skb);
goto out;
}
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c
index 0784ef3d4ce2..af4b93e4beb5 100644
--- a/drivers/net/wireless/ath/wil6210/txrx.c
+++ b/drivers/net/wireless/ath/wil6210/txrx.c
@@ -525,6 +525,17 @@ void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev)
ndev->stats.rx_bytes += len;
stats->rx_bytes += len;
}
+ {
+ static const char * const gro_res_str[] = {
+ [GRO_MERGED] = "GRO_MERGED",
+ [GRO_MERGED_FREE] = "GRO_MERGED_FREE",
+ [GRO_HELD] = "GRO_HELD",
+ [GRO_NORMAL] = "GRO_NORMAL",
+ [GRO_DROP] = "GRO_DROP",
+ };
+ wil_dbg_txrx(wil, "Rx complete %d bytes => %s,\n",
+ len, gro_res_str[rc]);
+ }
}
/**
@@ -760,7 +771,7 @@ static struct vring *wil_tx_bcast(struct wil6210_priv *wil,
goto found;
}
- wil_err(wil, "Tx while no vrings active?\n");
+ wil_dbg_txrx(wil, "Tx while no vrings active?\n");
return NULL;
@@ -881,6 +892,7 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
int nr_frags = skb_shinfo(skb)->nr_frags;
uint f = 0;
int vring_index = vring - wil->vring_tx;
+ struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index];
uint i = swhead;
dma_addr_t pa;
@@ -953,6 +965,9 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring,
wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4,
(const void *)d, sizeof(*d), false);
+ if (wil_vring_is_empty(vring)) /* performance monitoring */
+ txdata->idle += get_cycles() - txdata->last_idle;
+
/* advance swhead */
wil_vring_advance_head(vring, nr_frags + 1);
wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead);
@@ -1016,15 +1031,17 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev)
vring = wil_tx_bcast(wil, skb);
}
if (!vring) {
- wil_err(wil, "No Tx VRING found for %pM\n", eth->h_dest);
+ wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest);
goto drop;
}
/* set up vring entry */
rc = wil_tx_vring(wil, vring, skb);
/* do we still have enough room in the vring? */
- if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring))
+ if (wil_vring_avail_tx(vring) < wil_vring_wmark_low(vring)) {
netif_tx_stop_all_queues(wil_to_ndev(wil));
+ wil_dbg_txrx(wil, "netif_tx_stop : ring full\n");
+ }
switch (rc) {
case 0:
@@ -1132,8 +1149,16 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid)
done++;
}
}
- if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring))
+
+ if (wil_vring_is_empty(vring)) { /* performance monitoring */
+ wil_dbg_txrx(wil, "Ring[%2d] empty\n", ringid);
+ txdata->last_idle = get_cycles();
+ }
+
+ if (wil_vring_avail_tx(vring) > wil_vring_wmark_high(vring)) {
+ wil_dbg_txrx(wil, "netif_tx_wake : ring not full\n");
netif_tx_wake_all_queues(wil_to_ndev(wil));
+ }
return done;
}
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index e25edc52398f..424906635f05 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -20,6 +20,7 @@
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <net/cfg80211.h>
+#include <linux/timex.h>
#define WIL_NAME "wil6210"
@@ -251,7 +252,7 @@ struct vring {
*/
struct vring_tx_data {
int enabled;
-
+ cycles_t idle, last_idle, begin;
};
enum { /* for wil6210_priv.status */
@@ -303,6 +304,7 @@ struct wil_tid_ampdu_rx {
u16 ssn;
u16 buf_size;
u16 timeout;
+ u16 ssn_last_drop;
u8 dialog_token;
bool first_time; /* is it 1-st time this buffer used? */
};
@@ -410,6 +412,7 @@ struct wil6210_priv {
struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */
/* statistics */
struct wil6210_stats stats;
+ atomic_t isr_count_rx, isr_count_tx;
/* debugfs */
struct dentry *debug;
struct debugfs_blob_wrapper fw_code_blob;
@@ -504,9 +507,14 @@ int wil6210_init_irq(struct wil6210_priv *wil, int irq);
void wil6210_fini_irq(struct wil6210_priv *wil, int irq);
void wil6210_disable_irq(struct wil6210_priv *wil);
void wil6210_enable_irq(struct wil6210_priv *wil);
+int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie);
int wil6210_debugfs_init(struct wil6210_priv *wil);
void wil6210_debugfs_remove(struct wil6210_priv *wil);
+int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
+ struct station_info *sinfo);
struct wireless_dev *wil_cfg80211_init(struct device *dev);
void wil_wdev_free(struct wil6210_priv *wil);
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 6cc0e182cc70..a136dab560e2 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -75,6 +75,7 @@ static const struct {
{0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */
{0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */
{0x880000, 0x88a000, 0x880000}, /* various RGF */
+ {0x88b000, 0x88c000, 0x88b000}, /* Pcie_ext_rgf */
{0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */
/*
* 920000..930000 ucode code RAM
@@ -327,6 +328,17 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len)
if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) {
struct cfg80211_bss *bss;
+ u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp);
+ u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info);
+ u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int);
+ const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable;
+ size_t ie_len = d_len - offsetof(struct ieee80211_mgmt,
+ u.beacon.variable);
+ wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap);
+ wil_dbg_wmi(wil, "TSF : 0x%016llx\n", tsf);
+ wil_dbg_wmi(wil, "Beacon interval : %d\n", bi);
+ wil_hex_dump_wmi("IE ", DUMP_PREFIX_OFFSET, 16, 1, ie_buf,
+ ie_len, true);
bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame,
d_len, signal, GFP_KERNEL);
@@ -351,6 +363,9 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id,
bool aborted = (data->status != WMI_SCAN_SUCCESS);
wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", data->status);
+ wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n",
+ wil->scan_request, aborted);
+
del_timer_sync(&wil->scan_timer);
cfg80211_scan_done(wil->scan_request, aborted);
wil->scan_request = NULL;
@@ -668,14 +683,12 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
for (n = 0;; n++) {
u16 len;
+ bool q;
r->head = ioread32(wil->csr + HOST_MBOX +
offsetof(struct wil6210_mbox_ctl, rx.head));
- if (r->tail == r->head) {
- if (n == 0)
- wil_dbg_wmi(wil, "No events?\n");
- return;
- }
+ if (r->tail == r->head)
+ break;
wil_dbg_wmi(wil, "Mbox head %08x tail %08x\n",
r->head, r->tail);
@@ -684,14 +697,14 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
sizeof(struct wil6210_mbox_ring_desc));
if (d_tail.sync == 0) {
wil_err(wil, "Mbox evt not owned by FW?\n");
- return;
+ break;
}
/* read cmd header from descriptor */
if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) {
wil_err(wil, "Mbox evt at 0x%08x?\n",
le32_to_cpu(d_tail.addr));
- return;
+ break;
}
len = le16_to_cpu(hdr.len);
wil_dbg_wmi(wil, "Mbox evt %04x %04x %04x %02x\n",
@@ -705,7 +718,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
event.wmi) + len, 4),
GFP_KERNEL);
if (!evt)
- return;
+ break;
evt->event.hdr = hdr;
cmd = (void *)&evt->event.wmi;
@@ -737,14 +750,11 @@ void wmi_recv_cmd(struct wil6210_priv *wil)
spin_lock_irqsave(&wil->wmi_ev_lock, flags);
list_add_tail(&evt->list, &wil->pending_wmi_ev);
spin_unlock_irqrestore(&wil->wmi_ev_lock, flags);
- {
- int q = queue_work(wil->wmi_wq,
- &wil->wmi_event_worker);
- wil_dbg_wmi(wil, "queue_work -> %d\n", q);
- }
+ q = queue_work(wil->wmi_wq, &wil->wmi_event_worker);
+ wil_dbg_wmi(wil, "queue_work -> %d\n", q);
}
- if (n > 1)
- wil_dbg_wmi(wil, "%s -> %d events processed\n", __func__, n);
+ /* normally, 1 event per IRQ should be processed */
+ wil_dbg_wmi(wil, "%s -> %d events queued\n", __func__, n);
}
int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len,
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig
index 40fd9b7b1426..d4c6ae3a9210 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/b43/Kconfig
@@ -122,6 +122,15 @@ config B43_PIO
select SSB_BLOCKIO
default y
+config B43_PHY_G
+ bool "Support for G-PHY (802.11g) devices"
+ depends on B43 && B43_SSB
+ default y
+ ---help---
+ This PHY type can be found in the following chipsets:
+ PCI: BCM4306, BCM4311, BCM4318
+ SoC: BCM4712, BCM5352E
+
config B43_PHY_N
bool "Support for 802.11n (N-PHY) devices"
depends on B43
diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile
index 098fe9ee7096..6e00b8804ada 100644
--- a/drivers/net/wireless/b43/Makefile
+++ b/drivers/net/wireless/b43/Makefile
@@ -1,13 +1,11 @@
b43-y += main.o
b43-y += bus.o
-b43-y += tables.o
+b43-$(CONFIG_B43_PHY_G) += phy_a.o phy_g.o tables.o lo.o wa.o
b43-$(CONFIG_B43_PHY_N) += tables_nphy.o
b43-$(CONFIG_B43_PHY_N) += radio_2055.o
b43-$(CONFIG_B43_PHY_N) += radio_2056.o
b43-$(CONFIG_B43_PHY_N) += radio_2057.o
b43-y += phy_common.o
-b43-y += phy_g.o
-b43-y += phy_a.o
b43-$(CONFIG_B43_PHY_N) += phy_n.o
b43-$(CONFIG_B43_PHY_LP) += phy_lp.o
b43-$(CONFIG_B43_PHY_LP) += tables_lpphy.o
@@ -17,8 +15,6 @@ b43-$(CONFIG_B43_PHY_HT) += radio_2059.o
b43-$(CONFIG_B43_PHY_LCN) += phy_lcn.o tables_phy_lcn.o
b43-y += sysfs.o
b43-y += xmit.o
-b43-y += lo.o
-b43-y += wa.o
b43-y += dma.o
b43-y += pio.o
b43-y += rfkill.o
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 0d6a0bb1f876..15aaeb132a32 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -122,7 +122,11 @@ static const struct bcma_device_id b43_bcma_tbl[] = {
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x11, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x17, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x18, BCMA_ANY_CLASS),
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1C, BCMA_ANY_CLASS),
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1D, BCMA_ANY_CLASS),
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x1E, BCMA_ANY_CLASS),
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x28, BCMA_ANY_CLASS),
+ BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_80211, 0x2A, BCMA_ANY_CLASS),
BCMA_CORETABLE_END
};
MODULE_DEVICE_TABLE(bcma, b43_bcma_tbl);
@@ -2201,52 +2205,82 @@ err_format:
return -EPROTO;
}
+/* http://bcm-v4.sipsolutions.net/802.11/Init/Firmware */
static int b43_try_request_fw(struct b43_request_fw_context *ctx)
{
struct b43_wldev *dev = ctx->dev;
struct b43_firmware *fw = &ctx->dev->fw;
+ struct b43_phy *phy = &dev->phy;
const u8 rev = ctx->dev->dev->core_rev;
const char *filename;
- u32 tmshigh;
int err;
- /* Files for HT and LCN were found by trying one by one */
-
/* Get microcode */
- if ((rev >= 5) && (rev <= 10)) {
- filename = "ucode5";
- } else if ((rev >= 11) && (rev <= 12)) {
- filename = "ucode11";
- } else if (rev == 13) {
- filename = "ucode13";
- } else if (rev == 14) {
- filename = "ucode14";
- } else if (rev == 15) {
+ filename = NULL;
+ switch (rev) {
+ case 42:
+ if (phy->type == B43_PHYTYPE_AC)
+ filename = "ucode42";
+ break;
+ case 40:
+ if (phy->type == B43_PHYTYPE_AC)
+ filename = "ucode40";
+ break;
+ case 33:
+ if (phy->type == B43_PHYTYPE_LCN40)
+ filename = "ucode33_lcn40";
+ break;
+ case 30:
+ if (phy->type == B43_PHYTYPE_N)
+ filename = "ucode30_mimo";
+ break;
+ case 29:
+ if (phy->type == B43_PHYTYPE_HT)
+ filename = "ucode29_mimo";
+ break;
+ case 26:
+ if (phy->type == B43_PHYTYPE_HT)
+ filename = "ucode26_mimo";
+ break;
+ case 28:
+ case 25:
+ if (phy->type == B43_PHYTYPE_N)
+ filename = "ucode25_mimo";
+ else if (phy->type == B43_PHYTYPE_LCN)
+ filename = "ucode25_lcn";
+ break;
+ case 24:
+ if (phy->type == B43_PHYTYPE_LCN)
+ filename = "ucode24_lcn";
+ break;
+ case 23:
+ if (phy->type == B43_PHYTYPE_N)
+ filename = "ucode16_mimo";
+ break;
+ case 16 ... 19:
+ if (phy->type == B43_PHYTYPE_N)
+ filename = "ucode16_mimo";
+ else if (phy->type == B43_PHYTYPE_LP)
+ filename = "ucode16_lp";
+ break;
+ case 15:
filename = "ucode15";
- } else {
- switch (dev->phy.type) {
- case B43_PHYTYPE_N:
- if (rev >= 16)
- filename = "ucode16_mimo";
- else
- goto err_no_ucode;
- break;
- case B43_PHYTYPE_HT:
- if (rev == 29)
- filename = "ucode29_mimo";
- else
- goto err_no_ucode;
- break;
- case B43_PHYTYPE_LCN:
- if (rev == 24)
- filename = "ucode24_mimo";
- else
- goto err_no_ucode;
- break;
- default:
- goto err_no_ucode;
- }
+ break;
+ case 14:
+ filename = "ucode14";
+ break;
+ case 13:
+ filename = "ucode13";
+ break;
+ case 11 ... 12:
+ filename = "ucode11";
+ break;
+ case 5 ... 10:
+ filename = "ucode5";
+ break;
}
+ if (!filename)
+ goto err_no_ucode;
err = b43_do_request_fw(ctx, filename, &fw->ucode, true);
if (err)
goto err_load;
@@ -2268,117 +2302,121 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)
goto err_load;
/* Get initvals */
+ filename = NULL;
switch (dev->phy.type) {
- case B43_PHYTYPE_A:
- if ((rev >= 5) && (rev <= 10)) {
- tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
- if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
- filename = "a0g1initvals5";
- else
- filename = "a0g0initvals5";
- } else
- goto err_no_initvals;
- break;
case B43_PHYTYPE_G:
- if ((rev >= 5) && (rev <= 10))
- filename = "b0g0initvals5";
- else if (rev >= 13)
+ if (rev == 13)
filename = "b0g0initvals13";
- else
- goto err_no_initvals;
+ else if (rev >= 5 && rev <= 10)
+ filename = "b0g0initvals5";
break;
case B43_PHYTYPE_N:
- if (rev >= 16)
+ if (rev == 30)
+ filename = "n16initvals30";
+ else if (rev == 28 || rev == 25)
+ filename = "n0initvals25";
+ else if (rev == 24)
+ filename = "n0initvals24";
+ else if (rev == 23)
+ filename = "n0initvals16"; /* What about n0initvals22? */
+ else if (rev >= 16 && rev <= 18)
filename = "n0initvals16";
- else if ((rev >= 11) && (rev <= 12))
+ else if (rev >= 11 && rev <= 12)
filename = "n0initvals11";
- else
- goto err_no_initvals;
break;
case B43_PHYTYPE_LP:
- if (rev == 13)
- filename = "lp0initvals13";
+ if (rev >= 16 && rev <= 18)
+ filename = "lp0initvals16";
+ else if (rev == 15)
+ filename = "lp0initvals15";
else if (rev == 14)
filename = "lp0initvals14";
- else if (rev >= 15)
- filename = "lp0initvals15";
- else
- goto err_no_initvals;
+ else if (rev == 13)
+ filename = "lp0initvals13";
break;
case B43_PHYTYPE_HT:
if (rev == 29)
filename = "ht0initvals29";
- else
- goto err_no_initvals;
+ else if (rev == 26)
+ filename = "ht0initvals26";
break;
case B43_PHYTYPE_LCN:
if (rev == 24)
filename = "lcn0initvals24";
- else
- goto err_no_initvals;
break;
- default:
- goto err_no_initvals;
+ case B43_PHYTYPE_LCN40:
+ if (rev == 33)
+ filename = "lcn400initvals33";
+ break;
+ case B43_PHYTYPE_AC:
+ if (rev == 42)
+ filename = "ac1initvals42";
+ else if (rev == 40)
+ filename = "ac0initvals40";
+ break;
}
+ if (!filename)
+ goto err_no_initvals;
err = b43_do_request_fw(ctx, filename, &fw->initvals, false);
if (err)
goto err_load;
/* Get bandswitch initvals */
+ filename = NULL;
switch (dev->phy.type) {
- case B43_PHYTYPE_A:
- if ((rev >= 5) && (rev <= 10)) {
- tmshigh = ssb_read32(dev->dev->sdev, SSB_TMSHIGH);
- if (tmshigh & B43_TMSHIGH_HAVE_2GHZ_PHY)
- filename = "a0g1bsinitvals5";
- else
- filename = "a0g0bsinitvals5";
- } else if (rev >= 11)
- filename = NULL;
- else
- goto err_no_initvals;
- break;
case B43_PHYTYPE_G:
- if ((rev >= 5) && (rev <= 10))
+ if (rev == 13)
+ filename = "b0g0bsinitvals13";
+ else if (rev >= 5 && rev <= 10)
filename = "b0g0bsinitvals5";
- else if (rev >= 11)
- filename = NULL;
- else
- goto err_no_initvals;
break;
case B43_PHYTYPE_N:
- if (rev >= 16)
+ if (rev == 30)
+ filename = "n16bsinitvals30";
+ else if (rev == 28 || rev == 25)
+ filename = "n0bsinitvals25";
+ else if (rev == 24)
+ filename = "n0bsinitvals24";
+ else if (rev == 23)
+ filename = "n0bsinitvals16"; /* What about n0bsinitvals22? */
+ else if (rev >= 16 && rev <= 18)
filename = "n0bsinitvals16";
- else if ((rev >= 11) && (rev <= 12))
+ else if (rev >= 11 && rev <= 12)
filename = "n0bsinitvals11";
- else
- goto err_no_initvals;
break;
case B43_PHYTYPE_LP:
- if (rev == 13)
- filename = "lp0bsinitvals13";
+ if (rev >= 16 && rev <= 18)
+ filename = "lp0bsinitvals16";
+ else if (rev == 15)
+ filename = "lp0bsinitvals15";
else if (rev == 14)
filename = "lp0bsinitvals14";
- else if (rev >= 15)
- filename = "lp0bsinitvals15";
- else
- goto err_no_initvals;
+ else if (rev == 13)
+ filename = "lp0bsinitvals13";
break;
case B43_PHYTYPE_HT:
if (rev == 29)
filename = "ht0bsinitvals29";
- else
- goto err_no_initvals;
+ else if (rev == 26)
+ filename = "ht0bsinitvals26";
break;
case B43_PHYTYPE_LCN:
if (rev == 24)
filename = "lcn0bsinitvals24";
- else
- goto err_no_initvals;
break;
- default:
- goto err_no_initvals;
+ case B43_PHYTYPE_LCN40:
+ if (rev == 33)
+ filename = "lcn400bsinitvals33";
+ break;
+ case B43_PHYTYPE_AC:
+ if (rev == 42)
+ filename = "ac1bsinitvals42";
+ else if (rev == 40)
+ filename = "ac0bsinitvals40";
+ break;
}
+ if (!filename)
+ goto err_no_initvals;
err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false);
if (err)
goto err_load;
@@ -3798,38 +3836,29 @@ static void b43_set_retry_limits(struct b43_wldev *dev,
static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
{
struct b43_wl *wl = hw_to_b43_wl(hw);
- struct b43_wldev *dev;
- struct b43_phy *phy;
+ struct b43_wldev *dev = wl->current_dev;
+ struct b43_phy *phy = &dev->phy;
struct ieee80211_conf *conf = &hw->conf;
int antenna;
int err = 0;
- bool reload_bss = false;
mutex_lock(&wl->mutex);
-
- dev = wl->current_dev;
-
b43_mac_suspend(dev);
- /* Switch the band (if necessary). This might change the active core. */
- err = b43_switch_band(dev, conf->chandef.chan);
- if (err)
- goto out_unlock_mutex;
-
- /* Need to reload all settings if the core changed */
- if (dev != wl->current_dev) {
- dev = wl->current_dev;
- changed = ~0;
- reload_bss = true;
- }
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ phy->chandef = &conf->chandef;
+ phy->channel = conf->chandef.chan->hw_value;
- phy = &dev->phy;
+ /* Switch the band (if necessary). */
+ err = b43_switch_band(dev, conf->chandef.chan);
+ if (err)
+ goto out_mac_enable;
- if (conf_is_ht(conf))
- phy->is_40mhz =
- (conf_is_ht40_minus(conf) || conf_is_ht40_plus(conf));
- else
- phy->is_40mhz = false;
+ /* Switch to the requested channel.
+ * The firmware takes care of races with the TX handler.
+ */
+ b43_switch_channel(dev, phy->channel);
+ }
if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS)
b43_set_retry_limits(dev, conf->short_frame_max_tx_count,
@@ -3838,11 +3867,6 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
if (!changed)
goto out_mac_enable;
- /* Switch to the requested channel.
- * The firmware takes care of races with the TX handler. */
- if (conf->chandef.chan->hw_value != phy->channel)
- b43_switch_channel(dev, conf->chandef.chan->hw_value);
-
dev->wl->radiotap_enabled = !!(conf->flags & IEEE80211_CONF_MONITOR);
/* Adjust the desired TX power level. */
@@ -3878,12 +3902,8 @@ static int b43_op_config(struct ieee80211_hw *hw, u32 changed)
out_mac_enable:
b43_mac_enable(dev);
-out_unlock_mutex:
mutex_unlock(&wl->mutex);
- if (wl->vif && reload_bss)
- b43_op_bss_info_changed(hw, wl->vif, &wl->vif->bss_conf, ~0);
-
return err;
}
@@ -4309,6 +4329,7 @@ static char *b43_phy_name(struct b43_wldev *dev, u8 phy_type)
static int b43_phy_versioning(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
+ const u8 core_rev = dev->dev->core_rev;
u32 tmp;
u8 analog_type;
u8 phy_type;
@@ -4323,20 +4344,20 @@ static int b43_phy_versioning(struct b43_wldev *dev)
analog_type = (tmp & B43_PHYVER_ANALOG) >> B43_PHYVER_ANALOG_SHIFT;
phy_type = (tmp & B43_PHYVER_TYPE) >> B43_PHYVER_TYPE_SHIFT;
phy_rev = (tmp & B43_PHYVER_VERSION);
+
+ /* LCNXN is continuation of N which run out of revisions */
+ if (phy_type == B43_PHYTYPE_LCNXN) {
+ phy_type = B43_PHYTYPE_N;
+ phy_rev += 16;
+ }
+
switch (phy_type) {
- case B43_PHYTYPE_A:
- if (phy_rev >= 4)
- unsupported = 1;
- break;
- case B43_PHYTYPE_B:
- if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6
- && phy_rev != 7)
- unsupported = 1;
- break;
+#ifdef CONFIG_B43_PHY_G
case B43_PHYTYPE_G:
if (phy_rev > 9)
unsupported = 1;
break;
+#endif
#ifdef CONFIG_B43_PHY_N
case B43_PHYTYPE_N:
if (phy_rev > 9)
@@ -4374,7 +4395,15 @@ static int b43_phy_versioning(struct b43_wldev *dev)
analog_type, phy_type, b43_phy_name(dev, phy_type), phy_rev);
/* Get RADIO versioning */
- if (dev->dev->core_rev >= 24) {
+ if (core_rev == 40 || core_rev == 42) {
+ radio_manuf = 0x17F;
+
+ b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 0);
+ radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA);
+
+ b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 1);
+ radio_ver = b43_read16(dev, B43_MMIO_RADIO24_DATA);
+ } else if (core_rev >= 24) {
u16 radio24[3];
for (tmp = 0; tmp < 3; tmp++) {
diff --git a/drivers/net/wireless/b43/phy_a.c b/drivers/net/wireless/b43/phy_a.c
index a6c38104693d..25e40432d68b 100644
--- a/drivers/net/wireless/b43/phy_a.c
+++ b/drivers/net/wireless/b43/phy_a.c
@@ -573,7 +573,7 @@ static void b43_aphy_op_pwork_60sec(struct b43_wldev *dev)
{//TODO
}
-const struct b43_phy_operations b43_phyops_a = {
+static const struct b43_phy_operations b43_phyops_a = {
.allocate = b43_aphy_op_allocate,
.free = b43_aphy_op_free,
.prepare_structs = b43_aphy_op_prepare_structs,
diff --git a/drivers/net/wireless/b43/phy_a.h b/drivers/net/wireless/b43/phy_a.h
index 5cfaab7b16ee..f7d0d929a374 100644
--- a/drivers/net/wireless/b43/phy_a.h
+++ b/drivers/net/wireless/b43/phy_a.h
@@ -123,8 +123,4 @@ struct b43_phy_a {
*/
void b43_phy_inita(struct b43_wldev *dev);
-
-struct b43_phy_operations;
-extern const struct b43_phy_operations b43_phyops_a;
-
#endif /* LINUX_B43_PHY_A_H_ */
diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c
index 08244b3b327e..3cbef21b4726 100644
--- a/drivers/net/wireless/b43/phy_common.c
+++ b/drivers/net/wireless/b43/phy_common.c
@@ -45,11 +45,10 @@ int b43_phy_allocate(struct b43_wldev *dev)
phy->ops = NULL;
switch (phy->type) {
- case B43_PHYTYPE_A:
- phy->ops = &b43_phyops_a;
- break;
case B43_PHYTYPE_G:
+#ifdef CONFIG_B43_PHY_G
phy->ops = &b43_phyops_g;
+#endif
break;
case B43_PHYTYPE_N:
#ifdef CONFIG_B43_PHY_N
@@ -94,7 +93,13 @@ int b43_phy_init(struct b43_wldev *dev)
const struct b43_phy_operations *ops = phy->ops;
int err;
- phy->channel = ops->get_default_chan(dev);
+ /* During PHY init we need to use some channel. On the first init this
+ * function is called *before* b43_op_config, so our pointer is NULL.
+ */
+ if (!phy->chandef) {
+ phy->chandef = &dev->wl->hw->conf.chandef;
+ phy->channel = phy->chandef->chan->hw_value;
+ }
phy->ops->switch_analog(dev, true);
b43_software_rfkill(dev, false);
@@ -106,9 +111,7 @@ int b43_phy_init(struct b43_wldev *dev)
}
phy->do_full_init = false;
- /* Make sure to switch hardware and firmware (SHM) to
- * the default channel. */
- err = b43_switch_channel(dev, ops->get_default_chan(dev));
+ err = b43_switch_channel(dev, phy->channel);
if (err) {
b43err(dev->wl, "PHY init: Channel switch to default failed\n");
goto err_phy_exit;
@@ -408,9 +411,6 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
u16 channelcookie, savedcookie;
int err;
- if (new_channel == B43_DEFAULT_CHANNEL)
- new_channel = phy->ops->get_default_chan(dev);
-
/* First we set the channel radio code to prevent the
* firmware from sending ghost packets.
*/
@@ -428,7 +428,6 @@ int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel)
if (err)
goto err_restore_cookie;
- dev->phy.channel = new_channel;
/* Wait for the radio to tune to the channel and stabilize. */
msleep(8);
@@ -547,10 +546,9 @@ void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on)
}
-bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type)
+bool b43_is_40mhz(struct b43_wldev *dev)
{
- return (channel_type == NL80211_CHAN_HT40MINUS ||
- channel_type == NL80211_CHAN_HT40PLUS);
+ return dev->phy.chandef->width == NL80211_CHAN_WIDTH_40;
}
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */
diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h
index 4ad6240d9ff4..3912274f71e3 100644
--- a/drivers/net/wireless/b43/phy_common.h
+++ b/drivers/net/wireless/b43/phy_common.h
@@ -228,9 +228,6 @@ struct b43_phy {
bool supports_2ghz;
bool supports_5ghz;
- /* HT info */
- bool is_40mhz;
-
/* Is GMODE (2 GHz mode) bit enabled? */
bool gmode;
@@ -267,9 +264,8 @@ struct b43_phy {
unsigned long next_txpwr_check_time;
/* Current channel */
+ struct cfg80211_chan_def *chandef;
unsigned int channel;
- u16 channel_freq;
- enum nl80211_channel_type channel_type;
/* PHY TX errors counter. */
atomic_t txerr_cnt;
@@ -400,10 +396,6 @@ void b43_phy_take_out_of_reset(struct b43_wldev *dev);
* b43_switch_channel - Switch to another channel
*/
int b43_switch_channel(struct b43_wldev *dev, unsigned int new_channel);
-/**
- * B43_DEFAULT_CHANNEL - Switch to the default channel.
- */
-#define B43_DEFAULT_CHANNEL UINT_MAX
/**
* b43_software_rfkill - Turn the radio ON or OFF in software.
@@ -454,7 +446,7 @@ int b43_phy_shm_tssi_read(struct b43_wldev *dev, u16 shm_offset);
*/
void b43_phyop_switch_analog_generic(struct b43_wldev *dev, bool on);
-bool b43_channel_type_is_40mhz(enum nl80211_channel_type channel_type);
+bool b43_is_40mhz(struct b43_wldev *dev);
void b43_phy_force_clock(struct b43_wldev *dev, bool force);
diff --git a/drivers/net/wireless/b43/phy_ht.c b/drivers/net/wireless/b43/phy_ht.c
index 5d6833f18498..f2974c6b1c01 100644
--- a/drivers/net/wireless/b43/phy_ht.c
+++ b/drivers/net/wireless/b43/phy_ht.c
@@ -596,7 +596,7 @@ static void b43_phy_ht_tx_power_ctl_setup(struct b43_wldev *dev)
u8 target[3];
s16 a1[3], b0[3], b1[3];
- u16 freq = dev->phy.channel_freq;
+ u16 freq = dev->phy.chandef->chan->center_freq;
int i, c;
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c
index 86569f6a8705..50ca6f87d5e8 100644
--- a/drivers/net/wireless/b43/phy_n.c
+++ b/drivers/net/wireless/b43/phy_n.c
@@ -590,7 +590,103 @@ static void b43_nphy_set_rf_sequence(struct b43_wldev *dev, u8 cmd,
* Radio 0x2057
**************************************************/
-/* http://bcm-v4.sipsolutions.net/PHY/radio2057_rcal */
+static void b43_radio_2057_chantab_upload(struct b43_wldev *dev,
+ const struct b43_nphy_chantabent_rev7 *e_r7,
+ const struct b43_nphy_chantabent_rev7_2g *e_r7_2g)
+{
+ if (e_r7_2g) {
+ b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7_2g->radio_vcocal_countval0);
+ b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7_2g->radio_vcocal_countval1);
+ b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7_2g->radio_rfpll_refmaster_sparextalsize);
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7_2g->radio_rfpll_loopfilter_r1);
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7_2g->radio_rfpll_loopfilter_c2);
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7_2g->radio_rfpll_loopfilter_c1);
+ b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7_2g->radio_cp_kpd_idac);
+ b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7_2g->radio_rfpll_mmd0);
+ b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7_2g->radio_rfpll_mmd1);
+ b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7_2g->radio_vcobuf_tune);
+ b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7_2g->radio_logen_mx2g_tune);
+ b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7_2g->radio_logen_indbuf2g_tune);
+ b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7_2g->radio_txmix2g_tune_boost_pu_core0);
+ b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7_2g->radio_pad2g_tune_pus_core0);
+ b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7_2g->radio_lna2g_tune_core0);
+ b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7_2g->radio_txmix2g_tune_boost_pu_core1);
+ b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7_2g->radio_pad2g_tune_pus_core1);
+ b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7_2g->radio_lna2g_tune_core1);
+
+ } else {
+ b43_radio_write(dev, R2057_VCOCAL_COUNTVAL0, e_r7->radio_vcocal_countval0);
+ b43_radio_write(dev, R2057_VCOCAL_COUNTVAL1, e_r7->radio_vcocal_countval1);
+ b43_radio_write(dev, R2057_RFPLL_REFMASTER_SPAREXTALSIZE, e_r7->radio_rfpll_refmaster_sparextalsize);
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, e_r7->radio_rfpll_loopfilter_r1);
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, e_r7->radio_rfpll_loopfilter_c2);
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, e_r7->radio_rfpll_loopfilter_c1);
+ b43_radio_write(dev, R2057_CP_KPD_IDAC, e_r7->radio_cp_kpd_idac);
+ b43_radio_write(dev, R2057_RFPLL_MMD0, e_r7->radio_rfpll_mmd0);
+ b43_radio_write(dev, R2057_RFPLL_MMD1, e_r7->radio_rfpll_mmd1);
+ b43_radio_write(dev, R2057_VCOBUF_TUNE, e_r7->radio_vcobuf_tune);
+ b43_radio_write(dev, R2057_LOGEN_MX2G_TUNE, e_r7->radio_logen_mx2g_tune);
+ b43_radio_write(dev, R2057_LOGEN_MX5G_TUNE, e_r7->radio_logen_mx5g_tune);
+ b43_radio_write(dev, R2057_LOGEN_INDBUF2G_TUNE, e_r7->radio_logen_indbuf2g_tune);
+ b43_radio_write(dev, R2057_LOGEN_INDBUF5G_TUNE, e_r7->radio_logen_indbuf5g_tune);
+ b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, e_r7->radio_txmix2g_tune_boost_pu_core0);
+ b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, e_r7->radio_pad2g_tune_pus_core0);
+ b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE0, e_r7->radio_pga_boost_tune_core0);
+ b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE0, e_r7->radio_txmix5g_boost_tune_core0);
+ b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE0, e_r7->radio_pad5g_tune_misc_pus_core0);
+ b43_radio_write(dev, R2057_LNA2G_TUNE_CORE0, e_r7->radio_lna2g_tune_core0);
+ b43_radio_write(dev, R2057_LNA5G_TUNE_CORE0, e_r7->radio_lna5g_tune_core0);
+ b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, e_r7->radio_txmix2g_tune_boost_pu_core1);
+ b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, e_r7->radio_pad2g_tune_pus_core1);
+ b43_radio_write(dev, R2057_PGA_BOOST_TUNE_CORE1, e_r7->radio_pga_boost_tune_core1);
+ b43_radio_write(dev, R2057_TXMIX5G_BOOST_TUNE_CORE1, e_r7->radio_txmix5g_boost_tune_core1);
+ b43_radio_write(dev, R2057_PAD5G_TUNE_MISC_PUS_CORE1, e_r7->radio_pad5g_tune_misc_pus_core1);
+ b43_radio_write(dev, R2057_LNA2G_TUNE_CORE1, e_r7->radio_lna2g_tune_core1);
+ b43_radio_write(dev, R2057_LNA5G_TUNE_CORE1, e_r7->radio_lna5g_tune_core1);
+ }
+}
+
+static void b43_radio_2057_setup(struct b43_wldev *dev,
+ const struct b43_nphy_chantabent_rev7 *tabent_r7,
+ const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g)
+{
+ struct b43_phy *phy = &dev->phy;
+
+ b43_radio_2057_chantab_upload(dev, tabent_r7, tabent_r7_2g);
+
+ switch (phy->radio_rev) {
+ case 0 ... 4:
+ case 6:
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x3f);
+ b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f);
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8);
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8);
+ } else {
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1f);
+ b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f);
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x8);
+ b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8);
+ }
+ break;
+ /* TODO */
+ }
+
+ /* TODO */
+
+ usleep_range(50, 100);
+
+ /* VCO calibration */
+ b43_radio_mask(dev, R2057_RFPLL_MISC_EN, ~0x01);
+ b43_radio_mask(dev, R2057_RFPLL_MISC_CAL_RESETN, ~0x04);
+ b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x4);
+ b43_radio_set(dev, R2057_RFPLL_MISC_EN, 0x01);
+ usleep_range(300, 600);
+}
+
+/* Calibrate resistors in LPF of PLL?
+ * http://bcm-v4.sipsolutions.net/PHY/radio205x_rcal
+ */
static u8 b43_radio_2057_rcal(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
@@ -603,15 +699,25 @@ static u8 b43_radio_2057_rcal(struct b43_wldev *dev)
b43_radio_maskset(dev, 0x1ca, ~0x2, 0x1);
}
+ /* Enable */
b43_radio_set(dev, R2057_RCAL_CONFIG, 0x1);
udelay(10);
- b43_radio_set(dev, R2057_RCAL_CONFIG, 0x3);
- if (!b43_radio_wait_value(dev, R2057_RCCAL_N1_1, 1, 1, 100, 1000000)) {
+
+ /* Start */
+ b43_radio_set(dev, R2057_RCAL_CONFIG, 0x2);
+ usleep_range(100, 200);
+
+ /* Stop */
+ b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2);
+
+ /* Wait and check for result */
+ if (!b43_radio_wait_value(dev, R2057_RCAL_STATUS, 1, 1, 100, 1000000)) {
b43err(dev->wl, "Radio 0x2057 rcal timeout\n");
return 0;
}
- b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x2);
tmp = b43_radio_read(dev, R2057_RCAL_STATUS) & 0x3E;
+
+ /* Disable */
b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x1);
if (phy->radio_rev == 5) {
@@ -627,7 +733,9 @@ static u8 b43_radio_2057_rcal(struct b43_wldev *dev)
return tmp & 0x3e;
}
-/* http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal */
+/* Calibrate the internal RC oscillator?
+ * http://bcm-v4.sipsolutions.net/PHY/radio2057_rccal
+ */
static u16 b43_radio_2057_rccal(struct b43_wldev *dev)
{
struct b43_phy *phy = &dev->phy;
@@ -635,49 +743,76 @@ static u16 b43_radio_2057_rccal(struct b43_wldev *dev)
phy->radio_rev == 6);
u16 tmp;
+ /* Setup cal */
if (special) {
b43_radio_write(dev, R2057_RCCAL_MASTER, 0x61);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0xC0);
} else {
- b43_radio_write(dev, 0x1AE, 0x61);
+ b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x61);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE1);
}
b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
+
+ /* Start, wait, stop */
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55);
- if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500,
+ if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500,
5000000))
b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n");
+ usleep_range(35, 70);
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15);
+ usleep_range(70, 140);
+
+ /* Setup cal */
if (special) {
b43_radio_write(dev, R2057_RCCAL_MASTER, 0x69);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0);
} else {
- b43_radio_write(dev, 0x1AE, 0x69);
+ b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x69);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0xD5);
}
b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
+
+ /* Start, wait, stop */
+ usleep_range(35, 70);
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55);
- if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500,
+ usleep_range(70, 140);
+ if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500,
5000000))
b43dbg(dev->wl, "Radio 0x2057 rccal timeout\n");
+ usleep_range(35, 70);
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15);
+ usleep_range(70, 140);
+
+ /* Setup cal */
if (special) {
b43_radio_write(dev, R2057_RCCAL_MASTER, 0x73);
b43_radio_write(dev, R2057_RCCAL_X1, 0x28);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0xB0);
} else {
- b43_radio_write(dev, 0x1AE, 0x73);
+ b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x73);
b43_radio_write(dev, R2057_RCCAL_X1, 0x6E);
b43_radio_write(dev, R2057_RCCAL_TRC0, 0x99);
}
+
+ /* Start, wait, stop */
+ usleep_range(35, 70);
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x55);
- if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 1, 1, 500,
+ usleep_range(70, 140);
+ if (!b43_radio_wait_value(dev, R2057_RCCAL_DONE_OSCCAP, 2, 2, 500,
5000000)) {
b43err(dev->wl, "Radio 0x2057 rcal timeout\n");
return 0;
}
tmp = b43_radio_read(dev, R2057_RCCAL_DONE_OSCCAP);
+ usleep_range(35, 70);
b43_radio_write(dev, R2057_RCCAL_START_R1_Q1_P1, 0x15);
+ usleep_range(70, 140);
+
+ if (special)
+ b43_radio_mask(dev, R2057_RCCAL_MASTER, ~0x1);
+ else
+ b43_radio_mask(dev, R2057v7_RCCAL_MASTER, ~0x1);
+
return tmp;
}
@@ -798,6 +933,7 @@ static void b43_chantab_radio_2056_upload(struct b43_wldev *dev,
static void b43_radio_2056_setup(struct b43_wldev *dev,
const struct b43_nphy_channeltab_entry_rev3 *e)
{
+ struct b43_phy *phy = &dev->phy;
struct ssb_sprom *sprom = dev->dev->bus_sprom;
enum ieee80211_band band = b43_current_band(dev->wl);
u16 offset;
@@ -895,7 +1031,7 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
offset | B2056_TX_MIXG_BOOST_TUNE,
mixg_boost);
} else {
- bias = dev->phy.is_40mhz ? 0x40 : 0x20;
+ bias = b43_is_40mhz(dev) ? 0x40 : 0x20;
b43_radio_write(dev,
offset | B2056_TX_INTPAG_IMAIN_STAT,
bias);
@@ -909,7 +1045,7 @@ static void b43_radio_2056_setup(struct b43_wldev *dev,
b43_radio_write(dev, offset | B2056_TX_PA_SPARE1, 0xee);
}
} else if (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ) {
- u16 freq = dev->phy.channel_freq;
+ u16 freq = phy->chandef->chan->center_freq;
if (freq < 5100) {
paa_boost = 0xA;
pada_boost = 0x77;
@@ -1210,8 +1346,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
u16 bw, len, rot, angle;
struct b43_c32 *samples;
-
- bw = (dev->phy.is_40mhz) ? 40 : 20;
+ bw = b43_is_40mhz(dev) ? 40 : 20;
len = bw << 3;
if (test) {
@@ -1220,7 +1355,7 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
else
bw = 80;
- if (dev->phy.is_40mhz)
+ if (b43_is_40mhz(dev))
bw <<= 1;
len = bw << 1;
@@ -1248,7 +1383,8 @@ static u16 b43_nphy_gen_load_samples(struct b43_wldev *dev, u32 freq, u16 max,
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RunSamples */
static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
- u16 wait, bool iqmode, bool dac_test)
+ u16 wait, bool iqmode, bool dac_test,
+ bool modify_bbmult)
{
struct b43_phy_n *nphy = dev->phy.n;
int i;
@@ -1262,12 +1398,10 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000;
}
- /* TODO: add modify_bbmult argument */
- if (!dev->phy.is_40mhz)
- tmp = 0x6464;
- else
- tmp = 0x4747;
- b43_ntab_write(dev, B43_NTAB16(15, 87), tmp);
+ if (modify_bbmult) {
+ tmp = !b43_is_40mhz(dev) ? 0x6464 : 0x4747;
+ b43_ntab_write(dev, B43_NTAB16(15, 87), tmp);
+ }
b43_phy_write(dev, B43_NPHY_SAMP_DEPCNT, (samps - 1));
@@ -1285,10 +1419,8 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops,
b43_phy_mask(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x7FFF);
b43_phy_set(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8000);
} else {
- if (dac_test)
- b43_phy_write(dev, B43_NPHY_SAMP_CMD, 5);
- else
- b43_phy_write(dev, B43_NPHY_SAMP_CMD, 1);
+ tmp = dac_test ? 5 : 1;
+ b43_phy_write(dev, B43_NPHY_SAMP_CMD, tmp);
}
for (i = 0; i < 100; i++) {
if (!(b43_phy_read(dev, B43_NPHY_RFSEQST) & 1)) {
@@ -1675,6 +1807,7 @@ static int b43_nphy_poll_rssi(struct b43_wldev *dev, enum n_rssi_type rssi_type,
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */
static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
{
+ struct b43_phy *phy = &dev->phy;
struct b43_phy_n *nphy = dev->phy.n;
u16 saved_regs_phy_rfctl[2];
@@ -1897,9 +2030,9 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev)
/* Remember for which channel we store configuration */
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)
- nphy->rssical_chanspec_2G.center_freq = dev->phy.channel_freq;
+ nphy->rssical_chanspec_2G.center_freq = phy->chandef->chan->center_freq;
else
- nphy->rssical_chanspec_5G.center_freq = dev->phy.channel_freq;
+ nphy->rssical_chanspec_5G.center_freq = phy->chandef->chan->center_freq;
/* End of calibration, restore configuration */
b43_nphy_classifier(dev, 7, class);
@@ -2192,7 +2325,7 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
b43_phy_write(dev, B43_NPHY_C1_NBCLIPTHRES, 0x84);
b43_phy_write(dev, B43_NPHY_C2_NBCLIPTHRES, 0x84);
- if (!dev->phy.is_40mhz) {
+ if (!b43_is_40mhz(dev)) {
/* Set dwell lengths */
b43_phy_write(dev, B43_NPHY_CLIP1_NBDWELL_LEN, 0x002B);
b43_phy_write(dev, B43_NPHY_CLIP2_NBDWELL_LEN, 0x002B);
@@ -2206,7 +2339,7 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_NPHY_C2_CLIPWBTHRES,
~B43_NPHY_C2_CLIPWBTHRES_CLIP2, 21);
- if (!dev->phy.is_40mhz) {
+ if (!b43_is_40mhz(dev)) {
b43_phy_maskset(dev, B43_NPHY_C1_CGAINI,
~B43_NPHY_C1_CGAINI_GAINBKOFF, 0x1);
b43_phy_maskset(dev, B43_NPHY_C2_CGAINI,
@@ -2221,12 +2354,12 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev)
if (nphy->gain_boost) {
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ &&
- dev->phy.is_40mhz)
+ b43_is_40mhz(dev))
code = 4;
else
code = 5;
} else {
- code = dev->phy.is_40mhz ? 6 : 7;
+ code = b43_is_40mhz(dev) ? 6 : 7;
}
/* Set HPVGA2 index */
@@ -2298,7 +2431,7 @@ static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev)
static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset)
{
if (!offset)
- offset = (dev->phy.is_40mhz) ? 0x159 : 0x154;
+ offset = b43_is_40mhz(dev) ? 0x159 : 0x154;
return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7;
}
@@ -2371,13 +2504,13 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
lpf_40 = b43_nphy_read_lpf_ctl(dev, 0x159);
lpf_11b = b43_nphy_read_lpf_ctl(dev, 0x152);
if (b43_nphy_ipa(dev)) {
- if ((phy->radio_rev == 5 && phy->is_40mhz) ||
+ if ((phy->radio_rev == 5 && b43_is_40mhz(dev)) ||
phy->radio_rev == 7 || phy->radio_rev == 8) {
bcap_val = b43_radio_read(dev, 0x16b);
scap_val = b43_radio_read(dev, 0x16a);
scap_val_11b = scap_val;
bcap_val_11b = bcap_val;
- if (phy->radio_rev == 5 && phy->is_40mhz) {
+ if (phy->radio_rev == 5 && b43_is_40mhz(dev)) {
scap_val_11n_20 = scap_val;
bcap_val_11n_20 = bcap_val;
scap_val_11n_40 = bcap_val_11n_40 = 0xc;
@@ -2519,7 +2652,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
}
}
} else if (phy->radio_rev == 7 || phy->radio_rev == 8) {
- if (!phy->is_40mhz) {
+ if (!b43_is_40mhz(dev)) {
b43_radio_write(dev, 0x5F, 0x14);
b43_radio_write(dev, 0xE8, 0x12);
} else {
@@ -2528,7 +2661,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
}
}
} else {
- u16 freq = phy->channel_freq;
+ u16 freq = phy->chandef->chan->center_freq;
if ((freq >= 5180 && freq <= 5230) ||
(freq >= 5745 && freq <= 5805)) {
b43_radio_write(dev, 0x7D, 0xFF);
@@ -2592,7 +2725,7 @@ static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev)
b43_ntab_write(dev, B43_NTAB16(7, 0x123), 0x77);
b43_ntab_write(dev, B43_NTAB16(7, 0x12A), 0x77);
- if (!phy->is_40mhz) {
+ if (!b43_is_40mhz(dev)) {
b43_ntab_write(dev, B43_NTAB32(16, 0x03), 0x18D);
b43_ntab_write(dev, B43_NTAB32(16, 0x7F), 0x18D);
} else {
@@ -2691,7 +2824,7 @@ static void b43_nphy_workarounds_rev3plus(struct b43_wldev *dev)
b43_phy_maskset(dev, B43_NPHY_SGILTRNOFFSET, 0xF0FF, 0x0700);
- if (!dev->phy.is_40mhz) {
+ if (!b43_is_40mhz(dev)) {
b43_ntab_write(dev, B43_NTAB32(16, 3), 0x18D);
b43_ntab_write(dev, B43_NTAB32(16, 127), 0x18D);
} else {
@@ -2946,12 +3079,13 @@ static void b43_nphy_workarounds(struct b43_wldev *dev)
* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TXTone
*/
static int b43_nphy_tx_tone(struct b43_wldev *dev, u32 freq, u16 max_val,
- bool iqmode, bool dac_test)
+ bool iqmode, bool dac_test, bool modify_bbmult)
{
u16 samp = b43_nphy_gen_load_samples(dev, freq, max_val, dac_test);
if (samp == 0)
return -1;
- b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test);
+ b43_nphy_run_samples(dev, samp, 0xFFFF, 0, iqmode, dac_test,
+ modify_bbmult);
return 0;
}
@@ -3114,7 +3248,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3,
~B43_NPHY_BPHY_CTL3_SCALE, 0x5A);
- if (dev->phy.rev < 2 && dev->phy.is_40mhz)
+ if (dev->phy.rev < 2 && b43_is_40mhz(dev))
b43_hf_write(dev, b43_hf_read(dev) | B43_HF_TSSIRPSMW);
} else {
b43_ntab_write_bulk(dev, B43_NTAB16(26, 64), 84,
@@ -3168,7 +3302,7 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
else if (dev->phy.rev < 2)
b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, ~0xFF, 0x40);
- if (dev->phy.rev < 2 && dev->phy.is_40mhz)
+ if (dev->phy.rev < 2 && b43_is_40mhz(dev))
b43_hf_write(dev, b43_hf_read(dev) & ~B43_HF_TSSIRPSMW);
if (b43_nphy_ipa(dev)) {
@@ -3184,12 +3318,13 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable)
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrFix */
static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
{
+ struct b43_phy *phy = &dev->phy;
struct b43_phy_n *nphy = dev->phy.n;
struct ssb_sprom *sprom = dev->dev->bus_sprom;
u8 txpi[2], bbmult, i;
u16 tmp, radio_gain, dac_gain;
- u16 freq = dev->phy.channel_freq;
+ u16 freq = phy->chandef->chan->center_freq;
u32 txgain;
/* u32 gaintbl; rev3+ */
@@ -3234,7 +3369,11 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev)
*/
for (i = 0; i < 2; i++) {
- txgain = *(b43_nphy_get_tx_gain_table(dev) + txpi[i]);
+ const u32 *table = b43_nphy_get_tx_gain_table(dev);
+
+ if (!table)
+ break;
+ txgain = *(table + txpi[i]);
if (dev->phy.rev >= 3)
radio_gain = (txgain >> 16) & 0x1FFFF;
@@ -3388,7 +3527,7 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev)
b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, false);
b43_nphy_stop_playback(dev);
- b43_nphy_tx_tone(dev, 0xFA0, 0, false, false);
+ b43_nphy_tx_tone(dev, 4000, 0, false, false, false);
udelay(20);
tmp = b43_nphy_poll_rssi(dev, N_RSSI_TSSI_2G, rssi, 1);
b43_nphy_stop_playback(dev);
@@ -3439,21 +3578,21 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev)
delta = 0;
switch (stf_mode) {
case 0:
- if (dev->phy.is_40mhz && dev->phy.rev >= 5) {
+ if (b43_is_40mhz(dev) && dev->phy.rev >= 5) {
idx = 68;
} else {
delta = 1;
- idx = dev->phy.is_40mhz ? 52 : 4;
+ idx = b43_is_40mhz(dev) ? 52 : 4;
}
break;
case 1:
- idx = dev->phy.is_40mhz ? 76 : 28;
+ idx = b43_is_40mhz(dev) ? 76 : 28;
break;
case 2:
- idx = dev->phy.is_40mhz ? 84 : 36;
+ idx = b43_is_40mhz(dev) ? 84 : 36;
break;
case 3:
- idx = dev->phy.is_40mhz ? 92 : 44;
+ idx = b43_is_40mhz(dev) ? 92 : 44;
break;
}
@@ -3474,6 +3613,7 @@ static void b43_nphy_tx_prepare_adjusted_power_table(struct b43_wldev *dev)
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlSetup */
static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
{
+ struct b43_phy *phy = &dev->phy;
struct b43_phy_n *nphy = dev->phy.n;
struct ssb_sprom *sprom = dev->dev->bus_sprom;
@@ -3483,7 +3623,7 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev)
s32 num, den, pwr;
u32 regval[64];
- u16 freq = dev->phy.channel_freq;
+ u16 freq = phy->chandef->chan->center_freq;
u16 tmp;
u16 r; /* routing */
u8 i, c;
@@ -3647,6 +3787,9 @@ static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev)
int i;
table = b43_nphy_get_tx_gain_table(dev);
+ if (!table)
+ return;
+
b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table);
b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table);
@@ -3705,21 +3848,28 @@ static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable)
}
}
-/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw */
-static void b43_nphy_tx_lp_fbw(struct b43_wldev *dev)
+/*
+ * TX low-pass filter bandwidth setup
+ * http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw
+ */
+static void b43_nphy_tx_lpf_bw(struct b43_wldev *dev)
{
u16 tmp;
- if (dev->phy.rev >= 3) {
- if (b43_nphy_ipa(dev)) {
- tmp = 4;
- b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2,
- (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp);
- }
+ if (dev->phy.rev < 3 || dev->phy.rev >= 7)
+ return;
- tmp = 1;
+ if (b43_nphy_ipa(dev))
+ tmp = b43_is_40mhz(dev) ? 5 : 4;
+ else
+ tmp = b43_is_40mhz(dev) ? 3 : 1;
+ b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2,
+ (tmp << 9) | (tmp << 6) | (tmp << 3) | tmp);
+
+ if (b43_nphy_ipa(dev)) {
+ tmp = b43_is_40mhz(dev) ? 4 : 1;
b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S2,
- (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp);
+ (tmp << 9) | (tmp << 6) | (tmp << 3) | tmp);
}
}
@@ -3992,7 +4142,7 @@ static void b43_nphy_spur_workaround(struct b43_wldev *dev)
if (nphy->gband_spurwar_en) {
/* TODO: N PHY Adjust Analog Pfbw (7) */
- if (channel == 11 && dev->phy.is_40mhz)
+ if (channel == 11 && b43_is_40mhz(dev))
; /* TODO: N PHY Adjust Min Noise Var(2, tone, noise)*/
else
; /* TODO: N PHY Adjust Min Noise Var(0, NULL, NULL)*/
@@ -4286,7 +4436,7 @@ static void b43_nphy_int_pa_set_tx_dig_filters(struct b43_wldev *dev)
b43_phy_write(dev, B43_PHY_N(offset[i] + j),
tbl_tx_filter_coef_rev4[i][j]);
- if (dev->phy.is_40mhz) {
+ if (b43_is_40mhz(dev)) {
for (j = 0; j < 15; j++)
b43_phy_write(dev, B43_PHY_N(offset[0] + j),
tbl_tx_filter_coef_rev4[3][j]);
@@ -4345,6 +4495,9 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev)
for (i = 0; i < 2; ++i) {
table = b43_nphy_get_tx_gain_table(dev);
+ if (!table)
+ break;
+
if (dev->phy.rev >= 3) {
target.ipa[i] = (table[index[i]] >> 16) & 0xF;
target.pad[i] = (table[index[i]] >> 20) & 0xF;
@@ -4500,8 +4653,9 @@ static void b43_nphy_save_cal(struct b43_wldev *dev)
txcal_radio_regs[2] = b43_radio_read(dev, 0x8D);
txcal_radio_regs[3] = b43_radio_read(dev, 0xBC);
}
- iqcal_chanspec->center_freq = dev->phy.channel_freq;
- iqcal_chanspec->channel_type = dev->phy.channel_type;
+ iqcal_chanspec->center_freq = dev->phy.chandef->chan->center_freq;
+ iqcal_chanspec->channel_type =
+ cfg80211_get_chandef_type(dev->phy.chandef);
b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 8, table);
if (nphy->hang_avoid)
@@ -4581,6 +4735,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
struct nphy_txgains target,
bool full, bool mphase)
{
+ struct b43_phy *phy = &dev->phy;
struct b43_phy_n *nphy = dev->phy.n;
int i;
int error = 0;
@@ -4621,7 +4776,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
(dev->phy.rev == 5 && nphy->ipa2g_on &&
b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ);
if (phy6or5x) {
- if (dev->phy.is_40mhz) {
+ if (b43_is_40mhz(dev)) {
b43_ntab_write_bulk(dev, B43_NTAB16(15, 0), 18,
tbl_tx_iqlo_cal_loft_ladder_40);
b43_ntab_write_bulk(dev, B43_NTAB16(15, 32), 18,
@@ -4636,16 +4791,16 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9);
- if (!dev->phy.is_40mhz)
+ if (!b43_is_40mhz(dev))
freq = 2500;
else
freq = 5000;
if (nphy->mphase_cal_phase_id > 2)
- b43_nphy_run_samples(dev, (dev->phy.is_40mhz ? 40 : 20) * 8,
- 0xFFFF, 0, true, false);
+ b43_nphy_run_samples(dev, (b43_is_40mhz(dev) ? 40 : 20) * 8,
+ 0xFFFF, 0, true, false, false);
else
- error = b43_nphy_tx_tone(dev, freq, 250, true, false);
+ error = b43_nphy_tx_tone(dev, freq, 250, true, false, false);
if (error == 0) {
if (nphy->mphase_cal_phase_id > 2) {
@@ -4773,9 +4928,9 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev,
nphy->txiqlocal_bestc);
nphy->txiqlocal_coeffsvalid = true;
nphy->txiqlocal_chanspec.center_freq =
- dev->phy.channel_freq;
+ phy->chandef->chan->center_freq;
nphy->txiqlocal_chanspec.channel_type =
- dev->phy.channel_type;
+ cfg80211_get_chandef_type(phy->chandef);
} else {
length = 11;
if (dev->phy.rev < 3)
@@ -4811,8 +4966,8 @@ static void b43_nphy_reapply_tx_cal_coeffs(struct b43_wldev *dev)
bool equal = true;
if (!nphy->txiqlocal_coeffsvalid ||
- nphy->txiqlocal_chanspec.center_freq != dev->phy.channel_freq ||
- nphy->txiqlocal_chanspec.channel_type != dev->phy.channel_type)
+ nphy->txiqlocal_chanspec.center_freq != dev->phy.chandef->chan->center_freq ||
+ nphy->txiqlocal_chanspec.channel_type != cfg80211_get_chandef_type(dev->phy.chandef))
return;
b43_ntab_read_bulk(dev, B43_NTAB16(15, 80), 7, buffer);
@@ -4968,11 +5123,11 @@ static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev,
if (playtone) {
ret = b43_nphy_tx_tone(dev, 4000,
(nphy->rxcalparams & 0xFFFF),
- false, false);
+ false, false, true);
playtone = false;
} else {
- b43_nphy_run_samples(dev, 160, 0xFFFF, 0,
- false, false);
+ b43_nphy_run_samples(dev, 160, 0xFFFF, 0, false,
+ false, true);
}
if (ret == 0) {
@@ -5344,7 +5499,7 @@ static int b43_phy_initn(struct b43_wldev *dev)
b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320);
if (phy->rev >= 3 && phy->rev <= 6)
b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0032);
- b43_nphy_tx_lp_fbw(dev);
+ b43_nphy_tx_lpf_bw(dev);
if (phy->rev >= 3)
b43_nphy_spur_workaround(dev);
@@ -5430,14 +5585,14 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev,
if (dev->phy.rev < 3)
b43_nphy_adjust_lna_gain_table(dev);
- b43_nphy_tx_lp_fbw(dev);
+ b43_nphy_tx_lpf_bw(dev);
if (dev->phy.rev >= 3 &&
dev->phy.n->spur_avoid != B43_SPUR_AVOID_DISABLE) {
bool avoid = false;
if (dev->phy.n->spur_avoid == B43_SPUR_AVOID_FORCE) {
avoid = true;
- } else if (!b43_channel_type_is_40mhz(phy->channel_type)) {
+ } else if (!b43_is_40mhz(dev)) {
if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14)
avoid = true;
} else { /* 40MHz */
@@ -5484,10 +5639,17 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
const struct b43_nphy_channeltab_entry_rev2 *tabent_r2 = NULL;
const struct b43_nphy_channeltab_entry_rev3 *tabent_r3 = NULL;
+ const struct b43_nphy_chantabent_rev7 *tabent_r7 = NULL;
+ const struct b43_nphy_chantabent_rev7_2g *tabent_r7_2g = NULL;
u8 tmp;
- if (dev->phy.rev >= 3) {
+ if (phy->rev >= 7) {
+ r2057_get_chantabent_rev7(dev, channel->center_freq,
+ &tabent_r7, &tabent_r7_2g);
+ if (!tabent_r7 && !tabent_r7_2g)
+ return -ESRCH;
+ } else if (phy->rev >= 3) {
tabent_r3 = b43_nphy_get_chantabent_rev3(dev,
channel->center_freq);
if (!tabent_r3)
@@ -5502,20 +5664,36 @@ static int b43_nphy_set_channel(struct b43_wldev *dev,
/* Channel is set later in common code, but we need to set it on our
own to let this function's subcalls work properly. */
phy->channel = channel->hw_value;
- phy->channel_freq = channel->center_freq;
+#if 0
if (b43_channel_type_is_40mhz(phy->channel_type) !=
b43_channel_type_is_40mhz(channel_type))
; /* TODO: BMAC BW Set (channel_type) */
+#endif
- if (channel_type == NL80211_CHAN_HT40PLUS)
- b43_phy_set(dev, B43_NPHY_RXCTL,
- B43_NPHY_RXCTL_BSELU20);
- else if (channel_type == NL80211_CHAN_HT40MINUS)
- b43_phy_mask(dev, B43_NPHY_RXCTL,
- ~B43_NPHY_RXCTL_BSELU20);
+ if (channel_type == NL80211_CHAN_HT40PLUS) {
+ b43_phy_set(dev, B43_NPHY_RXCTL, B43_NPHY_RXCTL_BSELU20);
+ if (phy->rev >= 7)
+ b43_phy_set(dev, 0x310, 0x8000);
+ } else if (channel_type == NL80211_CHAN_HT40MINUS) {
+ b43_phy_mask(dev, B43_NPHY_RXCTL, ~B43_NPHY_RXCTL_BSELU20);
+ if (phy->rev >= 7)
+ b43_phy_mask(dev, 0x310, (u16)~0x8000);
+ }
- if (dev->phy.rev >= 3) {
+ if (phy->rev >= 7) {
+ const struct b43_phy_n_sfo_cfg *phy_regs = tabent_r7 ?
+ &(tabent_r7->phy_regs) : &(tabent_r7_2g->phy_regs);
+
+ if (phy->radio_rev <= 4 || phy->radio_rev == 6) {
+ tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 2 : 0;
+ b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE0, ~2, tmp);
+ b43_radio_maskset(dev, R2057_TIA_CONFIG_CORE1, ~2, tmp);
+ }
+
+ b43_radio_2057_setup(dev, tabent_r7, tabent_r7_2g);
+ b43_nphy_channel_setup(dev, phy_regs, channel);
+ } else if (phy->rev >= 3) {
tmp = (channel->band == IEEE80211_BAND_5GHZ) ? 4 : 0;
b43_radio_maskset(dev, 0x08, 0xFFFB, tmp);
b43_radio_2056_setup(dev, tabent_r3);
diff --git a/drivers/net/wireless/b43/radio_2057.c b/drivers/net/wireless/b43/radio_2057.c
index d61d6830c5c7..df3574545819 100644
--- a/drivers/net/wireless/b43/radio_2057.c
+++ b/drivers/net/wireless/b43/radio_2057.c
@@ -26,7 +26,7 @@
#include "radio_2057.h"
#include "phy_common.h"
-static u16 r2057_rev4_init[42][2] = {
+static u16 r2057_rev4_init[][2] = {
{ 0x0E, 0x20 }, { 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 },
{ 0x35, 0x26 }, { 0x3C, 0xff }, { 0x3D, 0xff }, { 0x3E, 0xff },
{ 0x3F, 0xff }, { 0x62, 0x33 }, { 0x8A, 0xf0 }, { 0x8B, 0x10 },
@@ -40,7 +40,7 @@ static u16 r2057_rev4_init[42][2] = {
{ 0x1AB, 0x00 }, { 0x1AC, 0x00 },
};
-static u16 r2057_rev5_init[44][2] = {
+static u16 r2057_rev5_init[][2] = {
{ 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 },
{ 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 },
{ 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f },
@@ -54,7 +54,7 @@ static u16 r2057_rev5_init[44][2] = {
{ 0x1AC, 0x00 }, { 0x1B7, 0x0c }, { 0x1C1, 0x01 }, { 0x1C2, 0x80 },
};
-static u16 r2057_rev5a_init[45][2] = {
+static u16 r2057_rev5a_init[][2] = {
{ 0x00, 0x15 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x23, 0x6 },
{ 0x31, 0x00 }, { 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 },
{ 0x59, 0x88 }, { 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f },
@@ -69,7 +69,7 @@ static u16 r2057_rev5a_init[45][2] = {
{ 0x1C2, 0x80 },
};
-static u16 r2057_rev7_init[54][2] = {
+static u16 r2057_rev7_init[][2] = {
{ 0x00, 0x00 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 },
{ 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 },
{ 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x13 },
@@ -86,7 +86,8 @@ static u16 r2057_rev7_init[54][2] = {
{ 0x1B7, 0x05 }, { 0x1C2, 0xa0 },
};
-static u16 r2057_rev8_init[54][2] = {
+/* TODO: Which devices should use it?
+static u16 r2057_rev8_init[][2] = {
{ 0x00, 0x08 }, { 0x01, 0x57 }, { 0x02, 0x20 }, { 0x31, 0x00 },
{ 0x32, 0x00 }, { 0x33, 0x00 }, { 0x51, 0x70 }, { 0x59, 0x88 },
{ 0x5C, 0x20 }, { 0x62, 0x33 }, { 0x63, 0x0f }, { 0x64, 0x0f },
@@ -102,6 +103,47 @@ static u16 r2057_rev8_init[54][2] = {
{ 0x1A6, 0x00 }, { 0x1AA, 0x00 }, { 0x1AB, 0x00 }, { 0x1AC, 0x00 },
{ 0x1B7, 0x05 }, { 0x1C2, 0xa0 },
};
+*/
+
+#define RADIOREGS7(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \
+ r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \
+ r20, r21, r22, r23, r24, r25, r26, r27) \
+ .radio_vcocal_countval0 = r00, \
+ .radio_vcocal_countval1 = r01, \
+ .radio_rfpll_refmaster_sparextalsize = r02, \
+ .radio_rfpll_loopfilter_r1 = r03, \
+ .radio_rfpll_loopfilter_c2 = r04, \
+ .radio_rfpll_loopfilter_c1 = r05, \
+ .radio_cp_kpd_idac = r06, \
+ .radio_rfpll_mmd0 = r07, \
+ .radio_rfpll_mmd1 = r08, \
+ .radio_vcobuf_tune = r09, \
+ .radio_logen_mx2g_tune = r10, \
+ .radio_logen_mx5g_tune = r11, \
+ .radio_logen_indbuf2g_tune = r12, \
+ .radio_logen_indbuf5g_tune = r13, \
+ .radio_txmix2g_tune_boost_pu_core0 = r14, \
+ .radio_pad2g_tune_pus_core0 = r15, \
+ .radio_pga_boost_tune_core0 = r16, \
+ .radio_txmix5g_boost_tune_core0 = r17, \
+ .radio_pad5g_tune_misc_pus_core0 = r18, \
+ .radio_lna2g_tune_core0 = r19, \
+ .radio_lna5g_tune_core0 = r20, \
+ .radio_txmix2g_tune_boost_pu_core1 = r21, \
+ .radio_pad2g_tune_pus_core1 = r22, \
+ .radio_pga_boost_tune_core1 = r23, \
+ .radio_txmix5g_boost_tune_core1 = r24, \
+ .radio_pad5g_tune_misc_pus_core1 = r25, \
+ .radio_lna2g_tune_core1 = r26, \
+ .radio_lna5g_tune_core1 = r27
+
+#define PHYREGS(r0, r1, r2, r3, r4, r5) \
+ .phy_regs.phy_bw1a = r0, \
+ .phy_regs.phy_bw2 = r1, \
+ .phy_regs.phy_bw3 = r2, \
+ .phy_regs.phy_bw4 = r3, \
+ .phy_regs.phy_bw5 = r4, \
+ .phy_regs.phy_bw6 = r5
void r2057_upload_inittabs(struct b43_wldev *dev)
{
@@ -109,33 +151,69 @@ void r2057_upload_inittabs(struct b43_wldev *dev)
u16 *table = NULL;
u16 size, i;
- if (phy->rev == 7) {
+ switch (phy->rev) {
+ case 7:
table = r2057_rev4_init[0];
size = ARRAY_SIZE(r2057_rev4_init);
- } else if (phy->rev == 8 || phy->rev == 9) {
+ break;
+ case 8:
if (phy->radio_rev == 5) {
- if (phy->radio_rev == 8) {
- table = r2057_rev5_init[0];
- size = ARRAY_SIZE(r2057_rev5_init);
- } else {
- table = r2057_rev5a_init[0];
- size = ARRAY_SIZE(r2057_rev5a_init);
- }
+ table = r2057_rev5_init[0];
+ size = ARRAY_SIZE(r2057_rev5_init);
} else if (phy->radio_rev == 7) {
table = r2057_rev7_init[0];
size = ARRAY_SIZE(r2057_rev7_init);
- } else if (phy->radio_rev == 9) {
- table = r2057_rev8_init[0];
- size = ARRAY_SIZE(r2057_rev8_init);
}
+ break;
+ case 9:
+ if (phy->radio_rev == 5) {
+ table = r2057_rev5a_init[0];
+ size = ARRAY_SIZE(r2057_rev5a_init);
+ }
+ break;
}
+ B43_WARN_ON(!table);
+
if (table) {
- for (i = 0; i < 10; i++) {
- pr_info("radio_write 0x%X ", *table);
- table++;
- pr_info("0x%X\n", *table);
- table++;
+ for (i = 0; i < size; i++, table += 2)
+ b43_radio_write(dev, table[0], table[1]);
+ }
+}
+
+void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq,
+ const struct b43_nphy_chantabent_rev7 **tabent_r7,
+ const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g)
+{
+ struct b43_phy *phy = &dev->phy;
+ const struct b43_nphy_chantabent_rev7 *e_r7 = NULL;
+ const struct b43_nphy_chantabent_rev7_2g *e_r7_2g = NULL;
+ unsigned int len, i;
+
+ *tabent_r7 = NULL;
+ *tabent_r7_2g = NULL;
+
+ /* TODO */
+ switch (phy->rev) {
+ default:
+ break;
+ }
+
+ if (e_r7) {
+ for (i = 0; i < len; i++, e_r7++) {
+ if (e_r7->freq == freq) {
+ *tabent_r7 = e_r7;
+ return;
+ }
+ }
+ } else if (e_r7_2g) {
+ for (i = 0; i < len; i++, e_r7_2g++) {
+ if (e_r7_2g->freq == freq) {
+ *tabent_r7_2g = e_r7_2g;
+ return;
+ }
}
+ } else {
+ B43_WARN_ON(1);
}
}
diff --git a/drivers/net/wireless/b43/radio_2057.h b/drivers/net/wireless/b43/radio_2057.h
index eeebd8fbeb0d..675d1bb64429 100644
--- a/drivers/net/wireless/b43/radio_2057.h
+++ b/drivers/net/wireless/b43/radio_2057.h
@@ -425,6 +425,72 @@
#define R2057_VCM_MASK 0x7
+struct b43_nphy_chantabent_rev7 {
+ /* The channel frequency in MHz */
+ u16 freq;
+ /* Radio regs values on channelswitch */
+ u8 radio_vcocal_countval0;
+ u8 radio_vcocal_countval1;
+ u8 radio_rfpll_refmaster_sparextalsize;
+ u8 radio_rfpll_loopfilter_r1;
+ u8 radio_rfpll_loopfilter_c2;
+ u8 radio_rfpll_loopfilter_c1;
+ u8 radio_cp_kpd_idac;
+ u8 radio_rfpll_mmd0;
+ u8 radio_rfpll_mmd1;
+ u8 radio_vcobuf_tune;
+ u8 radio_logen_mx2g_tune;
+ u8 radio_logen_mx5g_tune;
+ u8 radio_logen_indbuf2g_tune;
+ u8 radio_logen_indbuf5g_tune;
+ u8 radio_txmix2g_tune_boost_pu_core0;
+ u8 radio_pad2g_tune_pus_core0;
+ u8 radio_pga_boost_tune_core0;
+ u8 radio_txmix5g_boost_tune_core0;
+ u8 radio_pad5g_tune_misc_pus_core0;
+ u8 radio_lna2g_tune_core0;
+ u8 radio_lna5g_tune_core0;
+ u8 radio_txmix2g_tune_boost_pu_core1;
+ u8 radio_pad2g_tune_pus_core1;
+ u8 radio_pga_boost_tune_core1;
+ u8 radio_txmix5g_boost_tune_core1;
+ u8 radio_pad5g_tune_misc_pus_core1;
+ u8 radio_lna2g_tune_core1;
+ u8 radio_lna5g_tune_core1;
+ /* PHY res values on channelswitch */
+ struct b43_phy_n_sfo_cfg phy_regs;
+};
+
+struct b43_nphy_chantabent_rev7_2g {
+ /* The channel frequency in MHz */
+ u16 freq;
+ /* Radio regs values on channelswitch */
+ u8 radio_vcocal_countval0;
+ u8 radio_vcocal_countval1;
+ u8 radio_rfpll_refmaster_sparextalsize;
+ u8 radio_rfpll_loopfilter_r1;
+ u8 radio_rfpll_loopfilter_c2;
+ u8 radio_rfpll_loopfilter_c1;
+ u8 radio_cp_kpd_idac;
+ u8 radio_rfpll_mmd0;
+ u8 radio_rfpll_mmd1;
+ u8 radio_vcobuf_tune;
+ u8 radio_logen_mx2g_tune;
+ u8 radio_logen_indbuf2g_tune;
+ u8 radio_txmix2g_tune_boost_pu_core0;
+ u8 radio_pad2g_tune_pus_core0;
+ u8 radio_lna2g_tune_core0;
+ u8 radio_txmix2g_tune_boost_pu_core1;
+ u8 radio_pad2g_tune_pus_core1;
+ u8 radio_lna2g_tune_core1;
+ /* PHY regs values on channelswitch */
+ struct b43_phy_n_sfo_cfg phy_regs;
+};
+
void r2057_upload_inittabs(struct b43_wldev *dev);
+void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq,
+ const struct b43_nphy_chantabent_rev7 **tabent_r7,
+ const struct b43_nphy_chantabent_rev7_2g **tabent_r7_2g);
+
#endif /* B43_RADIO_2057_H_ */
diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c
index 4047c05e3807..b28dce950e1f 100644
--- a/drivers/net/wireless/b43/tables_nphy.c
+++ b/drivers/net/wireless/b43/tables_nphy.c
@@ -2146,7 +2146,196 @@ static const u16 b43_ntab_antswctl_r3[4][32] = {
}
};
-/* TX gain tables */
+/* static tables, PHY revision >= 7 */
+
+/* Copied from brcmsmac (5.75.11) */
+static const u32 b43_ntab_tmap_r7[] = {
+ 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+ 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0xf1111110, 0x11111111, 0x11f11111, 0x00000111,
+ 0x11000000, 0x1111f111, 0x11111111, 0x111111f1,
+ 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888,
+ 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+ 0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+ 0xa2222220, 0x22222222, 0x22c22222, 0x00000222,
+ 0x22000000, 0x2222a222, 0x22222222, 0x222222a2,
+ 0xf1111110, 0x11111111, 0x11f11111, 0x00011111,
+ 0x11110000, 0x1111f111, 0x11111111, 0x111111f1,
+ 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa,
+ 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
+ 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88,
+ 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888,
+ 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808,
+ 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08,
+ 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080,
+ 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
+ 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
+ 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888,
+ 0x22000000, 0x2222b222, 0x22222222, 0x222222b2,
+ 0xb2222220, 0x22222222, 0x22d22222, 0x00000222,
+ 0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+ 0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+ 0x33000000, 0x3333b333, 0x33333333, 0x333333b3,
+ 0xb3333330, 0x33333333, 0x33d33333, 0x00000333,
+ 0x22000000, 0x2222a222, 0x22222222, 0x222222a2,
+ 0xa2222220, 0x22222222, 0x22c22222, 0x00000222,
+ 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
+ 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
+ 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888,
+ 0x22222200, 0x2222f222, 0x22222222, 0x222222f2,
+ 0x22222222, 0x22222222, 0x22f22222, 0x00000222,
+ 0x11000000, 0x1111f111, 0x11111111, 0x11111111,
+ 0xf1111111, 0x11111111, 0x11f11111, 0x01111111,
+ 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b,
+ 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb,
+ 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
+ 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa,
+ 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a,
+ 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa,
+ 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+ 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb,
+ 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999,
+ 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88,
+ 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a,
+ 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b,
+ 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909,
+ 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08,
+ 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a,
+ 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090,
+ 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090,
+ 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080,
+ 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0,
+ 0x22000000, 0x2222f222, 0x22222222, 0x222222f2,
+ 0xf2222220, 0x22222222, 0x22f22222, 0x00000222,
+ 0x11000000, 0x1111f111, 0x11111111, 0x111111f1,
+ 0xf1111110, 0x11111111, 0x11f11111, 0x00000111,
+ 0x33000000, 0x3333f333, 0x33333333, 0x333333f3,
+ 0xf3333330, 0x33333333, 0x33f33333, 0x00000333,
+ 0x22000000, 0x2222f222, 0x22222222, 0x222222f2,
+ 0xf2222220, 0x22222222, 0x22f22222, 0x00000222,
+ 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9,
+ 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999,
+ 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+ 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+ 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888,
+ 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888,
+ 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888,
+ 0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+ 0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+ 0x11000000, 0x1111a111, 0x11111111, 0x111111a1,
+ 0xa1111110, 0x11111111, 0x11c11111, 0x00000111,
+ 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+ 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8,
+ 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000,
+};
+
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const u32 b43_ntab_noisevar_r7[] = {
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+ 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d,
+};
+
+/**************************************************
+ * TX gain tables
+ **************************************************/
+
static const u32 b43_ntab_tx_gain_rev0_1_2[] = {
0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42,
0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44,
@@ -2182,7 +2371,9 @@ static const u32 b43_ntab_tx_gain_rev0_1_2[] = {
0x03801442, 0x03801344, 0x03801342, 0x00002b00,
};
-static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = {
+/* EPA 2 GHz */
+
+static const u32 b43_ntab_tx_gain_epa_rev3_2g[] = {
0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e,
0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037,
0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e,
@@ -2217,7 +2408,9 @@ static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = {
0x1041003c, 0x1041003b, 0x10410039, 0x10410037,
};
-static const u32 b43_ntab_tx_gain_rev3_5ghz[] = {
+/* EPA 5 GHz */
+
+static const u32 b43_ntab_tx_gain_epa_rev3_5g[] = {
0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e,
0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037,
0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e,
@@ -2252,7 +2445,7 @@ static const u32 b43_ntab_tx_gain_rev3_5ghz[] = {
0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037,
};
-static const u32 b43_ntab_tx_gain_rev4_5ghz[] = {
+static const u32 b43_ntab_tx_gain_epa_rev4_5g[] = {
0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e,
0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037,
0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e,
@@ -2287,7 +2480,7 @@ static const u32 b43_ntab_tx_gain_rev4_5ghz[] = {
0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034,
};
-static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = {
+static const u32 b43_ntab_tx_gain_epa_rev5_5g[] = {
0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044,
0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c,
0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e,
@@ -2322,7 +2515,9 @@ static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = {
0x0062003b, 0x00620039, 0x00620037, 0x00620035,
};
-static const u32 txpwrctrl_tx_gain_ipa[] = {
+/* IPA 2 GHz */
+
+static const u32 b43_ntab_tx_gain_ipa_rev3_2g[] = {
0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029,
0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025,
0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029,
@@ -2357,7 +2552,7 @@ static const u32 txpwrctrl_tx_gain_ipa[] = {
0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025,
};
-static const u32 txpwrctrl_tx_gain_ipa_rev5[] = {
+static const u32 b43_ntab_tx_gain_ipa_rev5_2g[] = {
0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029,
0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025,
0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029,
@@ -2392,7 +2587,7 @@ static const u32 txpwrctrl_tx_gain_ipa_rev5[] = {
0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025,
};
-static const u32 txpwrctrl_tx_gain_ipa_rev6[] = {
+static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = {
0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029,
0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025,
0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029,
@@ -2427,7 +2622,45 @@ static const u32 txpwrctrl_tx_gain_ipa_rev6[] = {
0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025,
};
-static const u32 txpwrctrl_tx_gain_ipa_5g[] = {
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = {
+ 0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029,
+ 0x60b70029, 0x60a70029, 0x609f002a, 0x6097002b,
+ 0x6087002e, 0x60770031, 0x606f0032, 0x60670034,
+ 0x60670031, 0x605f0033, 0x605f0031, 0x60570033,
+ 0x60570030, 0x6057002d, 0x6057002b, 0x604f002d,
+ 0x604f002b, 0x604f0029, 0x604f0026, 0x60470029,
+ 0x60470027, 0x603f0029, 0x603f0027, 0x603f0025,
+ 0x60370029, 0x60370027, 0x60370024, 0x602f002a,
+ 0x602f0028, 0x602f0026, 0x602f0024, 0x6027002a,
+ 0x60270028, 0x60270026, 0x60270024, 0x60270022,
+ 0x601f002b, 0x601f0029, 0x601f0027, 0x601f0024,
+ 0x601f0022, 0x601f0020, 0x601f001f, 0x601f001d,
+ 0x60170029, 0x60170027, 0x60170025, 0x60170023,
+ 0x60170021, 0x6017001f, 0x6017001d, 0x6017001c,
+ 0x6017001a, 0x60170018, 0x60170018, 0x60170016,
+ 0x60170015, 0x600f0029, 0x600f0027, 0x600f0025,
+ 0x600f0023, 0x600f0021, 0x600f001f, 0x600f001d,
+ 0x600f001c, 0x600f001a, 0x600f0019, 0x600f0018,
+ 0x600f0016, 0x600f0015, 0x600f0115, 0x600f0215,
+ 0x600f0315, 0x600f0415, 0x600f0515, 0x600f0615,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+ 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715,
+};
+
+/* IPA 2 5Hz */
+
+static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = {
0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031,
0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b,
0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027,
@@ -2462,6 +2695,42 @@ static const u32 txpwrctrl_tx_gain_ipa_5g[] = {
0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f,
};
+/* Extracted from MMIO dump of 6.30.223.141 */
+static const u32 b43_ntab_tx_gain_ipa_2057_rev9_5g[] = {
+ 0x7f7f0053, 0x7f7f004b, 0x7f7f0044, 0x7f7f003f,
+ 0x7f7f0039, 0x7f7f0035, 0x7f7f0032, 0x7f7f0030,
+ 0x7f7f002d, 0x7e7f0030, 0x7e7f002d, 0x7d7f0032,
+ 0x7d7f002f, 0x7d7f002c, 0x7c7f0032, 0x7c7f0030,
+ 0x7c7f002d, 0x7b7f0030, 0x7b7f002e, 0x7b7f002b,
+ 0x7a7f0032, 0x7a7f0030, 0x7a7f002d, 0x7a7f002b,
+ 0x797f0030, 0x797f002e, 0x797f002b, 0x797f0029,
+ 0x787f0030, 0x787f002d, 0x787f002b, 0x777f0032,
+ 0x777f0030, 0x777f002d, 0x777f002b, 0x767f0031,
+ 0x767f002f, 0x767f002c, 0x767f002a, 0x757f0031,
+ 0x757f002f, 0x757f002c, 0x757f002a, 0x747f0030,
+ 0x747f002d, 0x747f002b, 0x737f0032, 0x737f002f,
+ 0x737f002c, 0x737f002a, 0x727f0030, 0x727f002d,
+ 0x727f002b, 0x727f0029, 0x717f0030, 0x717f002d,
+ 0x717f002b, 0x707f0031, 0x707f002f, 0x707f002c,
+ 0x707f002a, 0x707f0027, 0x707f0025, 0x707f0023,
+ 0x707f0021, 0x707f001f, 0x707f001d, 0x707f001c,
+ 0x707f001a, 0x707f0019, 0x707f0017, 0x707f0016,
+ 0x707f0015, 0x707f0014, 0x707f0012, 0x707f0012,
+ 0x707f0011, 0x707f0010, 0x707f000f, 0x707f000e,
+ 0x707f000d, 0x707f000d, 0x707f000c, 0x707f000b,
+ 0x707f000a, 0x707f000a, 0x707f0009, 0x707f0008,
+ 0x707f0008, 0x707f0008, 0x707f0008, 0x707f0007,
+ 0x707f0007, 0x707f0006, 0x707f0006, 0x707f0006,
+ 0x707f0005, 0x707f0005, 0x707f0005, 0x707f0004,
+ 0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003,
+ 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003,
+ 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003,
+ 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002,
+ 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002,
+ 0x707f0002, 0x707f0001, 0x707f0001, 0x707f0001,
+ 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001,
+};
+
const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[] = {
-114, -108, -98, -91, -84, -78, -70, -62,
-54, -46, -39, -31, -23, -15, -8, 0
@@ -3031,6 +3300,91 @@ void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset,
b43_ntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \
} while (0)
+static void b43_nphy_tables_init_shared_lut(struct b43_wldev *dev)
+{
+ ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
+ ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
+ ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
+ ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
+ ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
+ ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
+ ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
+ ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
+ ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
+ ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
+}
+
+static void b43_nphy_tables_init_rev7_volatile(struct b43_wldev *dev)
+{
+ struct ssb_sprom *sprom = dev->dev->bus_sprom;
+ u8 antswlut;
+ int core, offset, i;
+
+ const int antswlut0_offsets[] = { 0, 4, 8, }; /* Offsets for values */
+ const u8 antswlut0_values[][3] = {
+ { 0x2, 0x12, 0x8 }, /* Core 0 */
+ { 0x2, 0x18, 0x2 }, /* Core 1 */
+ };
+
+ if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
+ antswlut = sprom->fem.ghz5.antswlut;
+ else
+ antswlut = sprom->fem.ghz2.antswlut;
+
+ switch (antswlut) {
+ case 0:
+ for (core = 0; core < 2; core++) {
+ for (i = 0; i < ARRAY_SIZE(antswlut0_values[0]); i++) {
+ offset = core ? 0x20 : 0x00;
+ offset += antswlut0_offsets[i];
+ b43_ntab_write(dev, B43_NTAB8(9, offset),
+ antswlut0_values[core][i]);
+ }
+ }
+ break;
+ default:
+ b43err(dev->wl, "Unsupported antswlut: %d\n", antswlut);
+ break;
+ }
+}
+
+static void b43_nphy_tables_init_rev16(struct b43_wldev *dev)
+{
+ /* Static tables */
+ if (dev->phy.do_full_init) {
+ ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7);
+ b43_nphy_tables_init_shared_lut(dev);
+ }
+
+ /* Volatile tables */
+ b43_nphy_tables_init_rev7_volatile(dev);
+}
+
+static void b43_nphy_tables_init_rev7(struct b43_wldev *dev)
+{
+ /* Static tables */
+ if (dev->phy.do_full_init) {
+ ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3);
+ ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3);
+ ntab_upload(dev, B43_NTAB_TMAP_R7, b43_ntab_tmap_r7);
+ ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3);
+ ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3);
+ ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7);
+ ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3);
+ ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3);
+ ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3);
+ ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3);
+ ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3);
+ ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
+ ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
+ ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
+ b43_nphy_tables_init_shared_lut(dev);
+ }
+
+ /* Volatile tables */
+ b43_nphy_tables_init_rev7_volatile(dev);
+}
+
static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
{
struct ssb_sprom *sprom = dev->dev->bus_sprom;
@@ -3057,16 +3411,7 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev)
ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3);
ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3);
ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3);
- ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3);
- ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3);
- ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3);
- ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3);
- ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3);
- ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3);
- ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3);
- ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3);
- ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3);
- ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3);
+ b43_nphy_tables_init_shared_lut(dev);
}
/* Volatile tables */
@@ -3115,7 +3460,11 @@ static void b43_nphy_tables_init_rev0(struct b43_wldev *dev)
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables */
void b43_nphy_tables_init(struct b43_wldev *dev)
{
- if (dev->phy.rev >= 3)
+ if (dev->phy.rev >= 16)
+ b43_nphy_tables_init_rev16(dev);
+ else if (dev->phy.rev >= 7)
+ b43_nphy_tables_init_rev7(dev);
+ else if (dev->phy.rev >= 3)
b43_nphy_tables_init_rev3(dev);
else
b43_nphy_tables_init_rev0(dev);
@@ -3124,23 +3473,45 @@ void b43_nphy_tables_init(struct b43_wldev *dev)
/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */
static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev)
{
+ struct b43_phy *phy = &dev->phy;
+
if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) {
- if (dev->phy.rev >= 6) {
- if (dev->dev->chip_id == 47162)
- return txpwrctrl_tx_gain_ipa_rev5;
- return txpwrctrl_tx_gain_ipa_rev6;
- } else if (dev->phy.rev >= 5) {
- return txpwrctrl_tx_gain_ipa_rev5;
- } else {
- return txpwrctrl_tx_gain_ipa;
+ switch (phy->rev) {
+ case 16:
+ if (phy->radio_rev == 9)
+ return b43_ntab_tx_gain_ipa_2057_rev9_2g;
+ case 6:
+ if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162)
+ return b43_ntab_tx_gain_ipa_rev5_2g;
+ return b43_ntab_tx_gain_ipa_rev6_2g;
+ case 5:
+ return b43_ntab_tx_gain_ipa_rev5_2g;
+ case 4:
+ case 3:
+ return b43_ntab_tx_gain_ipa_rev3_2g;
+ default:
+ b43err(dev->wl,
+ "No 2GHz IPA gain table available for this device\n");
+ return NULL;
}
} else {
- return txpwrctrl_tx_gain_ipa_5g;
+ switch (phy->rev) {
+ case 16:
+ if (phy->radio_rev == 9)
+ return b43_ntab_tx_gain_ipa_2057_rev9_5g;
+ case 3 ... 6:
+ return b43_ntab_tx_gain_ipa_rev3_5g;
+ default:
+ b43err(dev->wl,
+ "No 5GHz IPA gain table available for this device\n");
+ return NULL;
+ }
}
}
const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev)
{
+ struct b43_phy *phy = &dev->phy;
enum ieee80211_band band = b43_current_band(dev->wl);
struct ssb_sprom *sprom = dev->dev->bus_sprom;
@@ -3152,19 +3523,36 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev)
(dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)) {
return b43_nphy_get_ipa_gain_table(dev);
} else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) {
- if (dev->phy.rev == 3)
- return b43_ntab_tx_gain_rev3_5ghz;
- if (dev->phy.rev == 4)
+ switch (phy->rev) {
+ case 6:
+ case 5:
+ return b43_ntab_tx_gain_epa_rev5_5g;
+ case 4:
return sprom->fem.ghz5.extpa_gain == 3 ?
- b43_ntab_tx_gain_rev4_5ghz :
- b43_ntab_tx_gain_rev4_5ghz; /* FIXME */
- else
- return b43_ntab_tx_gain_rev5plus_5ghz;
+ b43_ntab_tx_gain_epa_rev4_5g :
+ b43_ntab_tx_gain_epa_rev4_5g; /* FIXME */
+ case 3:
+ return b43_ntab_tx_gain_epa_rev3_5g;
+ default:
+ b43err(dev->wl,
+ "No 5GHz EPA gain table available for this device\n");
+ return NULL;
+ }
} else {
- if (dev->phy.rev >= 5 && sprom->fem.ghz5.extpa_gain == 3)
- return b43_ntab_tx_gain_rev3plus_2ghz; /* FIXME */
- else
- return b43_ntab_tx_gain_rev3plus_2ghz;
+ switch (phy->rev) {
+ case 6:
+ case 5:
+ if (sprom->fem.ghz5.extpa_gain == 3)
+ return b43_ntab_tx_gain_epa_rev3_2g; /* FIXME */
+ /* fall through */
+ case 4:
+ case 3:
+ return b43_ntab_tx_gain_epa_rev3_2g;
+ default:
+ b43err(dev->wl,
+ "No 2GHz EPA gain table available for this device\n");
+ return NULL;
+ }
}
}
@@ -3191,7 +3579,7 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
/* Some workarounds to the workarounds... */
if (ghz5 && dev->phy.rev >= 6) {
if (dev->phy.radio_rev == 11 &&
- !b43_channel_type_is_40mhz(dev->phy.channel_type))
+ !b43_is_40mhz(dev))
e->cliplo_gain = 0x2d;
} else if (!ghz5 && dev->phy.rev >= 5) {
static const int gain_data[] = {0x0062, 0x0064, 0x006a, 0x106a,
diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/b43/tables_nphy.h
index 3a58aee4c4cf..3ce2e6f3a278 100644
--- a/drivers/net/wireless/b43/tables_nphy.h
+++ b/drivers/net/wireless/b43/tables_nphy.h
@@ -165,6 +165,10 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent(
#define B43_NTAB_C1_LOFEEDTH_R3 B43_NTAB16(27, 448) /* Local Oscillator Feed Through lookup 1 */
#define B43_NTAB_C1_PAPD_COMP_R3 B43_NTAB16(27, 576)
+/* Static N-PHY tables, PHY revision >= 7 */
+#define B43_NTAB_TMAP_R7 B43_NTAB32(12, 0) /* TM AP */
+#define B43_NTAB_NOISEVAR_R7 B43_NTAB32(16, 0) /* noise variance */
+
#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE 18
#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE 18
#define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE 18
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
index 98e67c18f276..4cffb2ee3673 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile
+++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
@@ -34,7 +34,8 @@ brcmfmac-objs += \
dhd_common.o \
dhd_linux.o \
firmware.o \
- btcoex.o
+ btcoex.o \
+ vendor.o
brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
dhd_sdio.o \
bcmsdh.o
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
index 0cb591b050b3..a29ac4977b3a 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/btcoex.c
@@ -157,7 +157,7 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
*/
/* save current */
- brcmf_dbg(TRACE, "new SCO/eSCO coex algo {save & override}\n");
+ brcmf_dbg(INFO, "new SCO/eSCO coex algo {save & override}\n");
brcmf_btcoex_params_read(ifp, 50, &btci->reg50);
brcmf_btcoex_params_read(ifp, 51, &btci->reg51);
brcmf_btcoex_params_read(ifp, 64, &btci->reg64);
@@ -165,7 +165,7 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
brcmf_btcoex_params_read(ifp, 71, &btci->reg71);
btci->saved_regs_part2 = true;
- brcmf_dbg(TRACE,
+ brcmf_dbg(INFO,
"saved bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
btci->reg50, btci->reg51, btci->reg64,
btci->reg65, btci->reg71);
@@ -179,21 +179,21 @@ static void brcmf_btcoex_boost_wifi(struct brcmf_btcoex_info *btci,
} else if (btci->saved_regs_part2) {
/* restore previously saved bt params */
- brcmf_dbg(TRACE, "Do new SCO/eSCO coex algo {restore}\n");
+ brcmf_dbg(INFO, "Do new SCO/eSCO coex algo {restore}\n");
brcmf_btcoex_params_write(ifp, 50, btci->reg50);
brcmf_btcoex_params_write(ifp, 51, btci->reg51);
brcmf_btcoex_params_write(ifp, 64, btci->reg64);
brcmf_btcoex_params_write(ifp, 65, btci->reg65);
brcmf_btcoex_params_write(ifp, 71, btci->reg71);
- brcmf_dbg(TRACE,
+ brcmf_dbg(INFO,
"restored bt_params[50,51,64,65,71]: 0x%x 0x%x 0x%x 0x%x 0x%x\n",
btci->reg50, btci->reg51, btci->reg64,
btci->reg65, btci->reg71);
btci->saved_regs_part2 = false;
} else {
- brcmf_err("attempted to restore not saved BTCOEX params\n");
+ brcmf_dbg(INFO, "attempted to restore not saved BTCOEX params\n");
}
}
@@ -219,14 +219,14 @@ static bool brcmf_btcoex_is_sco_active(struct brcmf_if *ifp)
break;
}
- brcmf_dbg(TRACE, "sample[%d], btc_params 27:%x\n", i, param27);
+ brcmf_dbg(INFO, "sample[%d], btc_params 27:%x\n", i, param27);
if ((param27 & 0x6) == 2) { /* count both sco & esco */
sco_id_cnt++;
}
if (sco_id_cnt > 2) {
- brcmf_dbg(TRACE,
+ brcmf_dbg(INFO,
"sco/esco detected, pkt id_cnt:%d samples:%d\n",
sco_id_cnt, i);
res = true;
@@ -250,7 +250,7 @@ static void btcmf_btcoex_save_part1(struct brcmf_btcoex_info *btci)
brcmf_btcoex_params_read(ifp, 41, &btci->reg41);
brcmf_btcoex_params_read(ifp, 68, &btci->reg68);
btci->saved_regs_part1 = true;
- brcmf_dbg(TRACE,
+ brcmf_dbg(INFO,
"saved btc_params regs (66,41,68) 0x%x 0x%x 0x%x\n",
btci->reg66, btci->reg41,
btci->reg68);
@@ -270,7 +270,7 @@ static void brcmf_btcoex_restore_part1(struct brcmf_btcoex_info *btci)
brcmf_btcoex_params_write(ifp, 66, btci->reg66);
brcmf_btcoex_params_write(ifp, 41, btci->reg41);
brcmf_btcoex_params_write(ifp, 68, btci->reg68);
- brcmf_dbg(TRACE,
+ brcmf_dbg(INFO,
"restored btc_params regs {66,41,68} 0x%x 0x%x 0x%x\n",
btci->reg66, btci->reg41,
btci->reg68);
@@ -307,7 +307,7 @@ static void brcmf_btcoex_handler(struct work_struct *work)
/* DHCP started provide OPPORTUNITY window
to get DHCP address
*/
- brcmf_dbg(TRACE, "DHCP started\n");
+ brcmf_dbg(INFO, "DHCP started\n");
btci->bt_state = BRCMF_BT_DHCP_OPPR_WIN;
if (btci->timeout < BRCMF_BTCOEX_OPPR_WIN_TIME) {
mod_timer(&btci->timer, btci->timer.expires);
@@ -322,12 +322,12 @@ static void brcmf_btcoex_handler(struct work_struct *work)
case BRCMF_BT_DHCP_OPPR_WIN:
if (btci->dhcp_done) {
- brcmf_dbg(TRACE, "DHCP done before T1 expiration\n");
+ brcmf_dbg(INFO, "DHCP done before T1 expiration\n");
goto idle;
}
/* DHCP is not over yet, start lowering BT priority */
- brcmf_dbg(TRACE, "DHCP T1:%d expired\n",
+ brcmf_dbg(INFO, "DHCP T1:%d expired\n",
BRCMF_BTCOEX_OPPR_WIN_TIME);
brcmf_btcoex_boost_wifi(btci, true);
@@ -339,9 +339,9 @@ static void brcmf_btcoex_handler(struct work_struct *work)
case BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT:
if (btci->dhcp_done)
- brcmf_dbg(TRACE, "DHCP done before T2 expiration\n");
+ brcmf_dbg(INFO, "DHCP done before T2 expiration\n");
else
- brcmf_dbg(TRACE, "DHCP T2:%d expired\n",
+ brcmf_dbg(INFO, "DHCP T2:%d expired\n",
BRCMF_BT_DHCP_FLAG_FORCE_TIMEOUT);
goto idle;
@@ -440,13 +440,13 @@ static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci)
/* Stop any bt timer because DHCP session is done */
btci->dhcp_done = true;
if (btci->timer_on) {
- brcmf_dbg(TRACE, "disable BT DHCP Timer\n");
+ brcmf_dbg(INFO, "disable BT DHCP Timer\n");
btci->timer_on = false;
del_timer_sync(&btci->timer);
/* schedule worker if transition to IDLE is needed */
if (btci->bt_state != BRCMF_BT_DHCP_IDLE) {
- brcmf_dbg(TRACE, "bt_state:%d\n",
+ brcmf_dbg(INFO, "bt_state:%d\n",
btci->bt_state);
schedule_work(&btci->work);
}
@@ -472,7 +472,7 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
switch (mode) {
case BRCMF_BTCOEX_DISABLED:
- brcmf_dbg(TRACE, "DHCP session starts\n");
+ brcmf_dbg(INFO, "DHCP session starts\n");
if (btci->bt_state != BRCMF_BT_DHCP_IDLE)
return -EBUSY;
/* Start BT timer only for SCO connection */
@@ -484,14 +484,14 @@ int brcmf_btcoex_set_mode(struct brcmf_cfg80211_vif *vif,
break;
case BRCMF_BTCOEX_ENABLED:
- brcmf_dbg(TRACE, "DHCP session ends\n");
+ brcmf_dbg(INFO, "DHCP session ends\n");
if (btci->bt_state != BRCMF_BT_DHCP_IDLE &&
vif == btci->vif) {
brcmf_btcoex_dhcp_end(btci);
}
break;
default:
- brcmf_dbg(TRACE, "Unknown mode, ignored\n");
+ brcmf_dbg(INFO, "Unknown mode, ignored\n");
}
return 0;
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
index 16f9ab2568a8..a8998eb60d22 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -49,16 +49,6 @@
*/
#define BRCMF_DRIVER_FIRMWARE_VERSION_LEN 32
-/* Bus independent dongle command */
-struct brcmf_dcmd {
- uint cmd; /* common dongle cmd definition */
- void *buf; /* pointer to user buffer */
- uint len; /* length of user buffer */
- u8 set; /* get or set request (optional) */
- uint used; /* bytes read or written (optional) */
- uint needed; /* bytes needed (optional) */
-};
-
/**
* struct brcmf_ampdu_rx_reorder - AMPDU receive reorder info
*
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
index ed3e32ce8c23..d991f8e3d9ec 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
@@ -282,6 +282,13 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
ptr = strrchr(buf, ' ') + 1;
strlcpy(ifp->drvr->fwver, ptr, sizeof(ifp->drvr->fwver));
+ /* set mpc */
+ err = brcmf_fil_iovar_int_set(ifp, "mpc", 1);
+ if (err) {
+ brcmf_err("failed setting mpc\n");
+ goto done;
+ }
+
/*
* Setup timeout if Beacons are lost and roam is off to report
* link down
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
index 59a5af5bf994..ded328f80cd1 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c
@@ -54,7 +54,7 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set)
if (err >= 0)
err = 0;
else
- brcmf_err("Failed err=%d\n", err);
+ brcmf_dbg(FIL, "Failed err=%d\n", err);
return err;
}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
index f3445ac627e4..588fdbdb3255 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
@@ -708,7 +708,7 @@ static s32 brcmf_p2p_escan(struct brcmf_p2p_info *p2p, u32 num_chans,
active = P2PAPI_SCAN_SOCIAL_DWELL_TIME_MS;
else if (num_chans == AF_PEER_SEARCH_CNT)
active = P2PAPI_SCAN_AF_SEARCH_DWELL_TIME_MS;
- else if (wl_get_vif_state_all(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
+ else if (brcmf_get_vif_state_any(p2p->cfg, BRCMF_VIF_STATUS_CONNECTED))
active = -1;
else
active = P2PAPI_SCAN_DWELL_TIME_MS;
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
index 6db51a666f61..839bcda9465a 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
@@ -29,32 +29,24 @@
#include "usb_rdl.h"
#include "usb.h"
-#define IOCTL_RESP_TIMEOUT 2000
+#define IOCTL_RESP_TIMEOUT 2000
#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */
#define BRCMF_USB_RESET_GETVER_LOOP_CNT 10
#define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle
has boot up */
-#define BRCMF_USB_NRXQ 50
-#define BRCMF_USB_NTXQ 50
+#define BRCMF_USB_NRXQ 50
+#define BRCMF_USB_NTXQ 50
-#define CONFIGDESC(usb) (&((usb)->actconfig)->desc)
-#define IFPTR(usb, idx) ((usb)->actconfig->interface[(idx)])
-#define IFALTS(usb, idx) (IFPTR((usb), (idx))->altsetting[0])
-#define IFDESC(usb, idx) IFALTS((usb), (idx)).desc
-#define IFEPDESC(usb, idx, ep) (IFALTS((usb), (idx)).endpoint[(ep)]).desc
+#define BRCMF_USB_CBCTL_WRITE 0
+#define BRCMF_USB_CBCTL_READ 1
+#define BRCMF_USB_MAX_PKT_SIZE 1600
-#define CONTROL_IF 0
-#define BULK_IF 0
-
-#define BRCMF_USB_CBCTL_WRITE 0
-#define BRCMF_USB_CBCTL_READ 1
-#define BRCMF_USB_MAX_PKT_SIZE 1600
-
-#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin"
-#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin"
-#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin"
+#define BRCMF_USB_43143_FW_NAME "brcm/brcmfmac43143.bin"
+#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin"
+#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin"
+#define BRCMF_USB_43569_FW_NAME "brcm/brcmfmac43569.bin"
struct brcmf_usb_image {
struct list_head list;
@@ -70,7 +62,7 @@ struct brcmf_usbdev_info {
struct list_head rx_postq;
struct list_head tx_freeq;
struct list_head tx_postq;
- uint rx_pipe, tx_pipe, rx_pipe2;
+ uint rx_pipe, tx_pipe;
int rx_low_watermark;
int tx_low_watermark;
@@ -97,6 +89,7 @@ struct brcmf_usbdev_info {
int ctl_completed;
wait_queue_head_t ioctl_resp_wait;
ulong ctl_op;
+ u8 ifnum;
struct urb *bulk_urb; /* used for FW download */
};
@@ -576,7 +569,6 @@ fail:
static int brcmf_usb_up(struct device *dev)
{
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
- u16 ifnum;
brcmf_dbg(USB, "Enter\n");
if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
@@ -589,21 +581,19 @@ static int brcmf_usb_up(struct device *dev)
devinfo->ctl_in_pipe = usb_rcvctrlpipe(devinfo->usbdev, 0);
devinfo->ctl_out_pipe = usb_sndctrlpipe(devinfo->usbdev, 0);
- ifnum = IFDESC(devinfo->usbdev, CONTROL_IF).bInterfaceNumber;
-
/* CTL Write */
devinfo->ctl_write.bRequestType =
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
devinfo->ctl_write.bRequest = 0;
devinfo->ctl_write.wValue = cpu_to_le16(0);
- devinfo->ctl_write.wIndex = cpu_to_le16p(&ifnum);
+ devinfo->ctl_write.wIndex = cpu_to_le16(devinfo->ifnum);
/* CTL Read */
devinfo->ctl_read.bRequestType =
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
devinfo->ctl_read.bRequest = 1;
devinfo->ctl_read.wValue = cpu_to_le16(0);
- devinfo->ctl_read.wIndex = cpu_to_le16p(&ifnum);
+ devinfo->ctl_read.wIndex = cpu_to_le16(devinfo->ifnum);
}
brcmf_usb_rx_fill_all(devinfo);
return 0;
@@ -642,19 +632,19 @@ brcmf_usb_sync_complete(struct urb *urb)
brcmf_usb_ioctl_resp_wake(devinfo);
}
-static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
- void *buffer, int buflen)
+static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
+ void *buffer, int buflen)
{
- int ret = 0;
+ int ret;
char *tmpbuf;
u16 size;
if ((!devinfo) || (devinfo->ctl_urb == NULL))
- return false;
+ return -EINVAL;
tmpbuf = kmalloc(buflen, GFP_ATOMIC);
if (!tmpbuf)
- return false;
+ return -ENOMEM;
size = buflen;
devinfo->ctl_urb->transfer_buffer_length = size;
@@ -675,14 +665,16 @@ static bool brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd,
ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC);
if (ret < 0) {
brcmf_err("usb_submit_urb failed %d\n", ret);
- kfree(tmpbuf);
- return false;
+ goto finalize;
}
- ret = brcmf_usb_ioctl_resp_wait(devinfo);
- memcpy(buffer, tmpbuf, buflen);
- kfree(tmpbuf);
+ if (!brcmf_usb_ioctl_resp_wait(devinfo))
+ ret = -ETIMEDOUT;
+ else
+ memcpy(buffer, tmpbuf, buflen);
+finalize:
+ kfree(tmpbuf);
return ret;
}
@@ -724,6 +716,7 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
{
struct bootrom_id_le id;
u32 loop_cnt;
+ int err;
brcmf_dbg(USB, "Enter\n");
@@ -732,7 +725,9 @@ brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo)
mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT);
loop_cnt++;
id.chip = cpu_to_le32(0xDEAD); /* Get the ID */
- brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
+ err = brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id));
+ if ((err) && (err != -ETIMEDOUT))
+ return err;
if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID))
break;
} while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT);
@@ -794,8 +789,7 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
}
/* 1) Prepare USB boot loader for runtime image */
- brcmf_usb_dl_cmd(devinfo, DL_START, &state,
- sizeof(struct rdl_state_le));
+ brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state));
rdlstate = le32_to_cpu(state.state);
rdlbytes = le32_to_cpu(state.bytes);
@@ -839,10 +833,10 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen)
dlpos += sendlen;
sent += sendlen;
}
- if (!brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
- sizeof(struct rdl_state_le))) {
- brcmf_err("DL_GETSTATE Failed xxxx\n");
- err = -EINVAL;
+ err = brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
+ sizeof(state));
+ if (err) {
+ brcmf_err("DL_GETSTATE Failed\n");
goto fail;
}
@@ -898,13 +892,12 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo)
return -EINVAL;
/* Check we are runnable */
- brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state,
- sizeof(struct rdl_state_le));
+ state.state = 0;
+ brcmf_usb_dl_cmd(devinfo, DL_GETSTATE, &state, sizeof(state));
/* Start the image */
if (state.state == cpu_to_le32(DL_RUNNABLE)) {
- if (!brcmf_usb_dl_cmd(devinfo, DL_GO, &state,
- sizeof(struct rdl_state_le)))
+ if (brcmf_usb_dl_cmd(devinfo, DL_GO, &state, sizeof(state)))
return -ENODEV;
if (brcmf_usb_resetcfg(devinfo))
return -ENODEV;
@@ -928,6 +921,9 @@ static bool brcmf_usb_chip_support(int chipid, int chiprev)
return (chiprev == 3);
case 43242:
return true;
+ case 43566:
+ case 43569:
+ return true;
default:
break;
}
@@ -1028,6 +1024,9 @@ static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo)
return BRCMF_USB_43236_FW_NAME;
case 43242:
return BRCMF_USB_43242_FW_NAME;
+ case 43566:
+ case 43569:
+ return BRCMF_USB_43569_FW_NAME;
default:
return NULL;
}
@@ -1221,15 +1220,15 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo)
static int
brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
- int ep;
- struct usb_endpoint_descriptor *endpoint;
- int ret = 0;
struct usb_device *usb = interface_to_usbdev(intf);
- int num_of_eps;
- u8 endpoint_num;
struct brcmf_usbdev_info *devinfo;
+ struct usb_interface_descriptor *desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int ret = 0;
+ u32 num_of_eps;
+ u8 endpoint_num, ep;
- brcmf_dbg(USB, "Enter\n");
+ brcmf_dbg(USB, "Enter 0x%04x:0x%04x\n", id->idVendor, id->idProduct);
devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC);
if (devinfo == NULL)
@@ -1237,92 +1236,71 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
devinfo->usbdev = usb;
devinfo->dev = &usb->dev;
-
usb_set_intfdata(intf, devinfo);
/* Check that the device supports only one configuration */
if (usb->descriptor.bNumConfigurations != 1) {
- ret = -1;
- goto fail;
- }
-
- if (usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) {
- ret = -1;
+ brcmf_err("Number of configurations: %d not supported\n",
+ usb->descriptor.bNumConfigurations);
+ ret = -ENODEV;
goto fail;
}
- /*
- * Only the BDC interface configuration is supported:
- * Device class: USB_CLASS_VENDOR_SPEC
- * if0 class: USB_CLASS_VENDOR_SPEC
- * if0/ep0: control
- * if0/ep1: bulk in
- * if0/ep2: bulk out (ok if swapped with bulk in)
- */
- if (CONFIGDESC(usb)->bNumInterfaces != 1) {
- ret = -1;
+ if ((usb->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) &&
+ (usb->descriptor.bDeviceClass != USB_CLASS_MISC) &&
+ (usb->descriptor.bDeviceClass != USB_CLASS_WIRELESS_CONTROLLER)) {
+ brcmf_err("Device class: 0x%x not supported\n",
+ usb->descriptor.bDeviceClass);
+ ret = -ENODEV;
goto fail;
}
- /* Check interface */
- if (IFDESC(usb, CONTROL_IF).bInterfaceClass != USB_CLASS_VENDOR_SPEC ||
- IFDESC(usb, CONTROL_IF).bInterfaceSubClass != 2 ||
- IFDESC(usb, CONTROL_IF).bInterfaceProtocol != 0xff) {
- brcmf_err("invalid control interface: class %d, subclass %d, proto %d\n",
- IFDESC(usb, CONTROL_IF).bInterfaceClass,
- IFDESC(usb, CONTROL_IF).bInterfaceSubClass,
- IFDESC(usb, CONTROL_IF).bInterfaceProtocol);
- ret = -1;
+ desc = &intf->altsetting[0].desc;
+ if ((desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
+ (desc->bInterfaceSubClass != 2) ||
+ (desc->bInterfaceProtocol != 0xff)) {
+ brcmf_err("non WLAN interface %d: 0x%x:0x%x:0x%x\n",
+ desc->bInterfaceNumber, desc->bInterfaceClass,
+ desc->bInterfaceSubClass, desc->bInterfaceProtocol);
+ ret = -ENODEV;
goto fail;
}
- /* Check control endpoint */
- endpoint = &IFEPDESC(usb, CONTROL_IF, 0);
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- != USB_ENDPOINT_XFER_INT) {
- brcmf_err("invalid control endpoint %d\n",
- endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
- ret = -1;
- goto fail;
- }
-
- devinfo->rx_pipe = 0;
- devinfo->rx_pipe2 = 0;
- devinfo->tx_pipe = 0;
- num_of_eps = IFDESC(usb, BULK_IF).bNumEndpoints - 1;
-
- /* Check data endpoints and get pipes */
- for (ep = 1; ep <= num_of_eps; ep++) {
- endpoint = &IFEPDESC(usb, BULK_IF, ep);
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
- USB_ENDPOINT_XFER_BULK) {
- brcmf_err("invalid data endpoint %d\n", ep);
- ret = -1;
- goto fail;
- }
-
- endpoint_num = endpoint->bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_IN) {
- if (!devinfo->rx_pipe) {
+ num_of_eps = desc->bNumEndpoints;
+ for (ep = 0; ep < num_of_eps; ep++) {
+ endpoint = &intf->altsetting[0].endpoint[ep].desc;
+ endpoint_num = usb_endpoint_num(endpoint);
+ if (!usb_endpoint_xfer_bulk(endpoint))
+ continue;
+ if (usb_endpoint_dir_in(endpoint)) {
+ if (!devinfo->rx_pipe)
devinfo->rx_pipe =
usb_rcvbulkpipe(usb, endpoint_num);
- } else {
- devinfo->rx_pipe2 =
- usb_rcvbulkpipe(usb, endpoint_num);
- }
} else {
- devinfo->tx_pipe = usb_sndbulkpipe(usb, endpoint_num);
+ if (!devinfo->tx_pipe)
+ devinfo->tx_pipe =
+ usb_sndbulkpipe(usb, endpoint_num);
}
}
+ if (devinfo->rx_pipe == 0) {
+ brcmf_err("No RX (in) Bulk EP found\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+ if (devinfo->tx_pipe == 0) {
+ brcmf_err("No TX (out) Bulk EP found\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ devinfo->ifnum = desc->bInterfaceNumber;
if (usb->speed == USB_SPEED_SUPER)
- brcmf_dbg(USB, "Broadcom super speed USB wireless device detected\n");
+ brcmf_dbg(USB, "Broadcom super speed USB WLAN interface detected\n");
else if (usb->speed == USB_SPEED_HIGH)
- brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n");
+ brcmf_dbg(USB, "Broadcom high speed USB WLAN interface detected\n");
else
- brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n");
+ brcmf_dbg(USB, "Broadcom full speed USB WLAN interface detected\n");
ret = brcmf_usb_probe_cb(devinfo);
if (ret)
@@ -1332,11 +1310,9 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
return 0;
fail:
- brcmf_err("failed with errno %d\n", ret);
kfree(devinfo);
usb_set_intfdata(intf, NULL);
return ret;
-
}
static void
@@ -1381,6 +1357,7 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
{
struct usb_device *usb = interface_to_usbdev(intf);
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
+
brcmf_dbg(USB, "Enter\n");
return brcmf_fw_get_firmwares(&usb->dev, 0,
@@ -1392,12 +1369,14 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf)
#define BRCMF_USB_DEVICE_ID_43143 0xbd1e
#define BRCMF_USB_DEVICE_ID_43236 0xbd17
#define BRCMF_USB_DEVICE_ID_43242 0xbd1f
+#define BRCMF_USB_DEVICE_ID_43569 0xbd27
#define BRCMF_USB_DEVICE_ID_BCMFW 0x0bdc
static struct usb_device_id brcmf_usb_devid_table[] = {
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) },
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) },
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) },
+ { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43569) },
/* special entry for device with firmware loaded and running */
{ USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) },
{ }
@@ -1407,6 +1386,7 @@ MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table);
MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME);
MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME);
MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME);
+MODULE_FIRMWARE(BRCMF_USB_43569_FW_NAME);
static struct usb_driver brcmf_usbdrvr = {
.name = KBUILD_MODNAME,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
new file mode 100644
index 000000000000..5960d827508c
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2014 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/vmalloc.h>
+#include <net/cfg80211.h>
+#include <net/netlink.h>
+
+#include <brcmu_wifi.h>
+#include "fwil_types.h"
+#include "dhd.h"
+#include "p2p.h"
+#include "dhd_dbg.h"
+#include "wl_cfg80211.h"
+#include "vendor.h"
+#include "fwil.h"
+
+static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy,
+ struct wireless_dev *wdev,
+ const void *data, int len)
+{
+ struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
+ struct net_device *ndev = cfg_to_ndev(cfg);
+ const struct brcmf_vndr_dcmd_hdr *cmdhdr = data;
+ struct sk_buff *reply;
+ int ret, payload, ret_len;
+ void *dcmd_buf = NULL, *wr_pointer;
+ u16 msglen, maxmsglen = PAGE_SIZE - 0x100;
+
+ brcmf_dbg(TRACE, "cmd %x set %d len %d\n", cmdhdr->cmd, cmdhdr->set,
+ cmdhdr->len);
+
+ len -= sizeof(struct brcmf_vndr_dcmd_hdr);
+ ret_len = cmdhdr->len;
+ if (ret_len > 0 || len > 0) {
+ if (len > BRCMF_DCMD_MAXLEN) {
+ brcmf_err("oversize input buffer %d\n", len);
+ len = BRCMF_DCMD_MAXLEN;
+ }
+ if (ret_len > BRCMF_DCMD_MAXLEN) {
+ brcmf_err("oversize return buffer %d\n", ret_len);
+ ret_len = BRCMF_DCMD_MAXLEN;
+ }
+ payload = max(ret_len, len) + 1;
+ dcmd_buf = vzalloc(payload);
+ if (NULL == dcmd_buf)
+ return -ENOMEM;
+
+ memcpy(dcmd_buf, (void *)cmdhdr + cmdhdr->offset, len);
+ *(char *)(dcmd_buf + len) = '\0';
+ }
+
+ if (cmdhdr->set)
+ ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), cmdhdr->cmd,
+ dcmd_buf, ret_len);
+ else
+ ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), cmdhdr->cmd,
+ dcmd_buf, ret_len);
+ if (ret != 0)
+ goto exit;
+
+ wr_pointer = dcmd_buf;
+ while (ret_len > 0) {
+ msglen = ret_len > maxmsglen ? maxmsglen : ret_len;
+ ret_len -= msglen;
+ payload = msglen + sizeof(msglen);
+ reply = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, payload);
+ if (NULL == reply) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ if (nla_put(reply, BRCMF_NLATTR_DATA, msglen, wr_pointer) ||
+ nla_put_u16(reply, BRCMF_NLATTR_LEN, msglen)) {
+ kfree_skb(reply);
+ ret = -ENOBUFS;
+ break;
+ }
+
+ ret = cfg80211_vendor_cmd_reply(reply);
+ if (ret)
+ break;
+
+ wr_pointer += msglen;
+ }
+
+exit:
+ vfree(dcmd_buf);
+
+ return ret;
+}
+
+const struct wiphy_vendor_command brcmf_vendor_cmds[] = {
+ {
+ {
+ .vendor_id = BROADCOM_OUI,
+ .subcmd = BRCMF_VNDR_CMDS_DCMD
+ },
+ .flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+ WIPHY_VENDOR_CMD_NEED_NETDEV,
+ .doit = brcmf_cfg80211_vndr_cmds_dcmd_handler
+ },
+};
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/brcm80211/brcmfmac/vendor.h
new file mode 100644
index 000000000000..061b7bfa2e1c
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/vendor.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+#ifndef _vendor_h_
+#define _vendor_h_
+
+#define BROADCOM_OUI 0x001018
+
+enum brcmf_vndr_cmds {
+ BRCMF_VNDR_CMDS_UNSPEC,
+ BRCMF_VNDR_CMDS_DCMD,
+ BRCMF_VNDR_CMDS_LAST
+};
+
+/**
+ * enum brcmf_nlattrs - nl80211 message attributes
+ *
+ * @BRCMF_NLATTR_LEN: message body length
+ * @BRCMF_NLATTR_DATA: message body
+ */
+enum brcmf_nlattrs {
+ BRCMF_NLATTR_UNSPEC,
+
+ BRCMF_NLATTR_LEN,
+ BRCMF_NLATTR_DATA,
+
+ __BRCMF_NLATTR_AFTER_LAST,
+ BRCMF_NLATTR_MAX = __BRCMF_NLATTR_AFTER_LAST - 1
+};
+
+/**
+ * struct brcmf_vndr_dcmd_hdr - message header for cfg80211 vendor command dcmd
+ * support
+ *
+ * @cmd: common dongle cmd definition
+ * @len: length of expecting return buffer
+ * @offset: offset of data buffer
+ * @set: get or set request(optional)
+ * @magic: magic number for verification
+ */
+struct brcmf_vndr_dcmd_hdr {
+ uint cmd;
+ int len;
+ uint offset;
+ uint set;
+ uint magic;
+};
+
+extern const struct wiphy_vendor_command brcmf_vendor_cmds[];
+
+#endif /* _vendor_h_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index d8fa276e368b..9682cf213ec4 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/etherdevice.h>
#include <linux/module.h>
+#include <linux/vmalloc.h>
#include <net/cfg80211.h>
#include <net/netlink.h>
@@ -33,6 +34,7 @@
#include "btcoex.h"
#include "wl_cfg80211.h"
#include "fwil.h"
+#include "vendor.h"
#define BRCMF_SCAN_IE_LEN_MAX 2048
#define BRCMF_PNO_VERSION 2
@@ -588,6 +590,12 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
}
}
+static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
+{
+ if ((brcmf_get_chip_info(ifp) >> 4) == 0x4329)
+ brcmf_set_mpc(ifp, mpc);
+}
+
void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
{
s32 err = 0;
@@ -641,7 +649,7 @@ s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
brcmf_err("Scan abort failed\n");
}
- brcmf_set_mpc(ifp, 1);
+ brcmf_scan_config_mpc(ifp, 1);
/*
* e-scan can be initiated by scheduled scan
@@ -920,7 +928,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
brcmf_err("error (%d)\n", err);
return err;
}
- brcmf_set_mpc(ifp, 0);
+ brcmf_scan_config_mpc(ifp, 0);
results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
results->version = 0;
results->count = 0;
@@ -928,7 +936,7 @@ brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
if (err)
- brcmf_set_mpc(ifp, 1);
+ brcmf_scan_config_mpc(ifp, 1);
return err;
}
@@ -1019,7 +1027,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
goto scan_out;
}
- brcmf_set_mpc(ifp, 0);
+ brcmf_scan_config_mpc(ifp, 0);
err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
&sr->ssid_le, sizeof(sr->ssid_le));
if (err) {
@@ -1029,7 +1037,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
else
brcmf_err("WLC_SCAN error (%d)\n", err);
- brcmf_set_mpc(ifp, 1);
+ brcmf_scan_config_mpc(ifp, 1);
goto scan_out;
}
}
@@ -1331,7 +1339,6 @@ static s32
brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
{
struct brcmf_if *ifp = netdev_priv(ndev);
- s32 err = 0;
brcmf_dbg(TRACE, "Enter\n");
if (!check_vif_up(ifp->vif))
@@ -1341,7 +1348,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
brcmf_dbg(TRACE, "Exit\n");
- return err;
+ return 0;
}
static s32 brcmf_set_wpa_version(struct net_device *ndev,
@@ -2388,7 +2395,6 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
struct cfg80211_bss *bss;
struct ieee80211_supported_band *band;
struct brcmu_chan ch;
- s32 err = 0;
u16 channel;
u32 freq;
u16 notify_capability;
@@ -2438,7 +2444,7 @@ static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
cfg80211_put_bss(wiphy, bss);
- return err;
+ return 0;
}
static struct brcmf_bss_info_le *
@@ -2690,7 +2696,6 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
{
struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
s32 status;
- s32 err = 0;
struct brcmf_escan_result_le *escan_result_le;
struct brcmf_bss_info_le *bss_info_le;
struct brcmf_bss_info_le *bss = NULL;
@@ -2781,7 +2786,7 @@ brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
status);
}
exit:
- return err;
+ return 0;
}
static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
@@ -3260,35 +3265,6 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
return 0;
}
-#ifdef CONFIG_NL80211_TESTMODE
-static int brcmf_cfg80211_testmode(struct wiphy *wiphy,
- struct wireless_dev *wdev,
- void *data, int len)
-{
- struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
- struct net_device *ndev = cfg_to_ndev(cfg);
- struct brcmf_dcmd *dcmd = data;
- struct sk_buff *reply;
- int ret;
-
- brcmf_dbg(TRACE, "cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set,
- dcmd->buf, dcmd->len);
-
- if (dcmd->set)
- ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd,
- dcmd->buf, dcmd->len);
- else
- ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd,
- dcmd->buf, dcmd->len);
- if (ret == 0) {
- reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
- nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
- ret = cfg80211_testmode_reply(reply);
- }
- return ret;
-}
-#endif
-
static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
{
s32 err;
@@ -3507,7 +3483,6 @@ static s32
brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
struct parsed_vndr_ies *vndr_ies)
{
- s32 err = 0;
struct brcmf_vs_tlv *vndrie;
struct brcmf_tlv *ie;
struct parsed_vndr_ie_info *parsed_info;
@@ -3560,7 +3535,7 @@ next:
ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
TLV_HDR_LEN);
}
- return err;
+ return 0;
}
static u32
@@ -4307,7 +4282,6 @@ static struct cfg80211_ops wl_cfg80211_ops = {
.crit_proto_start = brcmf_cfg80211_crit_proto_start,
.crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
.tdls_oper = brcmf_cfg80211_tdls_oper,
- CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode)
};
static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
@@ -4412,6 +4386,11 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
brcmf_dbg(INFO, "Registering custom regulatory\n");
wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
+
+ /* vendor commands/events support */
+ wiphy->vendor_commands = brcmf_vendor_cmds;
+ wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
+
err = wiphy_register(wiphy);
if (err < 0) {
brcmf_err("Could not register wiphy device (%d)\n", err);
@@ -4650,7 +4629,6 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
struct brcmf_if *ifp = netdev_priv(ndev);
struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
- s32 err = 0;
brcmf_dbg(TRACE, "Enter\n");
@@ -4676,7 +4654,7 @@ brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
completed ? "succeeded" : "failed");
}
brcmf_dbg(TRACE, "Exit\n");
- return err;
+ return 0;
}
static s32
@@ -4768,7 +4746,6 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp,
const struct brcmf_event_msg *e, void *data)
{
struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
- s32 err = 0;
u32 event = e->event_code;
u32 status = e->status;
@@ -4779,7 +4756,7 @@ brcmf_notify_roaming_status(struct brcmf_if *ifp,
brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
}
- return err;
+ return 0;
}
static s32
@@ -5057,6 +5034,9 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
BRCMF_OBSS_COEX_AUTO);
}
+ /* clear for now and rely on update later */
+ wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false;
+ wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0;
err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
if (err) {
@@ -5625,16 +5605,15 @@ enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
return wdev->iftype;
}
-u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state)
+bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state)
{
struct brcmf_cfg80211_vif *vif;
- bool result = 0;
list_for_each_entry(vif, &cfg->vif_list, list) {
if (test_bit(state, &vif->sme_state))
- result++;
+ return true;
}
- return result;
+ return false;
}
static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
index 283c525a44f7..f9fb10998e79 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
@@ -477,7 +477,7 @@ const struct brcmf_tlv *
brcmf_parse_tlvs(const void *buf, int buflen, uint key);
u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
struct ieee80211_channel *ch);
-u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state);
+bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, unsigned long state);
void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
struct brcmf_cfg80211_vif *vif);
bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
index b0fd807f2b2b..57ecc05802e9 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
@@ -1538,11 +1538,7 @@ static s8
wlc_user_txpwr_antport_to_rfport(struct brcms_phy *pi, uint chan, u32 band,
u8 rate)
{
- s8 offset = 0;
-
- if (!pi->user_txpwr_at_rfport)
- return offset;
- return offset;
+ return 0;
}
void wlc_phy_txpower_recalc_target(struct brcms_phy *pi)
diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c
index 9afcd4ce3368..b2fb6c632092 100644
--- a/drivers/net/wireless/cw1200/scan.c
+++ b/drivers/net/wireless/cw1200/scan.c
@@ -53,9 +53,10 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan)
int cw1200_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req)
+ struct ieee80211_scan_request *hw_req)
{
struct cw1200_common *priv = hw->priv;
+ struct cfg80211_scan_request *req = &hw_req->req;
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
};
diff --git a/drivers/net/wireless/cw1200/scan.h b/drivers/net/wireless/cw1200/scan.h
index 5a8296ccfa82..cc75459e5784 100644
--- a/drivers/net/wireless/cw1200/scan.h
+++ b/drivers/net/wireless/cw1200/scan.h
@@ -41,7 +41,7 @@ struct cw1200_scan {
int cw1200_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req);
+ struct ieee80211_scan_request *hw_req);
void cw1200_scan_work(struct work_struct *work);
void cw1200_scan_timeout(struct work_struct *work);
void cw1200_clear_recent_scan_work(struct work_struct *work);
diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c
index cd0cad7f7759..5b84664db13b 100644
--- a/drivers/net/wireless/cw1200/sta.c
+++ b/drivers/net/wireless/cw1200/sta.c
@@ -2289,7 +2289,6 @@ static int cw1200_upload_null(struct cw1200_common *priv)
static int cw1200_upload_qosnull(struct cw1200_common *priv)
{
- int ret = 0;
/* TODO: This needs to be implemented
struct wsm_template_frame frame = {
@@ -2306,7 +2305,7 @@ static int cw1200_upload_qosnull(struct cw1200_common *priv)
dev_kfree_skb(frame.skb);
*/
- return ret;
+ return 0;
}
static int cw1200_enable_beaconing(struct cw1200_common *priv,
diff --git a/drivers/net/wireless/ipw2x00/libipw_module.c b/drivers/net/wireless/ipw2x00/libipw_module.c
index 3adb24021a28..5f31b72a4921 100644
--- a/drivers/net/wireless/ipw2x00/libipw_module.c
+++ b/drivers/net/wireless/ipw2x00/libipw_module.c
@@ -100,8 +100,7 @@ static inline void libipw_networks_free(struct libipw_device *ieee)
int i;
for (i = 0; i < MAX_NETWORK_COUNT; i++) {
- if (ieee->networks[i]->ibss_dfs)
- kfree(ieee->networks[i]->ibss_dfs);
+ kfree(ieee->networks[i]->ibss_dfs);
kfree(ieee->networks[i]);
}
}
diff --git a/drivers/net/wireless/iwlegacy/common.c b/drivers/net/wireless/iwlegacy/common.c
index ecc674627e6e..03de7467aecf 100644
--- a/drivers/net/wireless/iwlegacy/common.c
+++ b/drivers/net/wireless/iwlegacy/common.c
@@ -1572,8 +1572,9 @@ il_scan_initiate(struct il_priv *il, struct ieee80211_vif *vif)
int
il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req)
+ struct ieee80211_scan_request *hw_req)
{
+ struct cfg80211_scan_request *req = &hw_req->req;
struct il_priv *il = hw->priv;
int ret;
diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h
index ea5c0f863c4e..5b972798bdff 100644
--- a/drivers/net/wireless/iwlegacy/common.h
+++ b/drivers/net/wireless/iwlegacy/common.h
@@ -1787,7 +1787,7 @@ int il_scan_cancel(struct il_priv *il);
int il_scan_cancel_timeout(struct il_priv *il, unsigned long ms);
void il_force_scan_end(struct il_priv *il);
int il_mac_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req);
+ struct ieee80211_scan_request *hw_req);
void il_internal_short_hw_scan(struct il_priv *il);
int il_force_reset(struct il_priv *il, bool external);
u16 il_fill_probe_req(struct il_priv *il, struct ieee80211_mgmt *frame,
diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
index 29af7b51e370..afb98f4fdaf3 100644
--- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c
@@ -1495,9 +1495,10 @@ static int iwlagn_mac_change_interface(struct ieee80211_hw *hw,
static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req)
+ struct ieee80211_scan_request *hw_req)
{
struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
+ struct cfg80211_scan_request *req = &hw_req->req;
int ret;
IWL_DEBUG_MAC80211(priv, "enter\n");
diff --git a/drivers/net/wireless/iwlwifi/dvm/power.c b/drivers/net/wireless/iwlwifi/dvm/power.c
index f2c1439566b5..760c45c34ef3 100644
--- a/drivers/net/wireless/iwlwifi/dvm/power.c
+++ b/drivers/net/wireless/iwlwifi/dvm/power.c
@@ -40,6 +40,10 @@
#include "commands.h"
#include "power.h"
+static bool force_cam;
+module_param(force_cam, bool, 0644);
+MODULE_PARM_DESC(force_cam, "force continuously aware mode (no power saving at all)");
+
/*
* Setting power level allows the card to go to sleep when not busy.
*
@@ -288,6 +292,11 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
bool enabled = priv->hw->conf.flags & IEEE80211_CONF_PS;
int dtimper;
+ if (force_cam) {
+ iwl_power_sleep_cam_cmd(priv, cmd);
+ return;
+ }
+
dtimper = priv->hw->conf.ps_dtim_period ?: 1;
if (priv->wowlan)
diff --git a/drivers/net/wireless/iwlwifi/dvm/rxon.c b/drivers/net/wireless/iwlwifi/dvm/rxon.c
index ed50de6362ed..6dc5dd3ced44 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rxon.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rxon.c
@@ -1068,13 +1068,6 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
/* recalculate basic rates */
iwl_calc_basic_rates(priv, ctx);
- /*
- * force CTS-to-self frames protection if RTS-CTS is not preferred
- * one aggregation protection method
- */
- if (!priv->hw_params.use_rts_for_aggregation)
- ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
-
if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) ||
!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK))
ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK;
@@ -1480,11 +1473,6 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
else
ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK;
- if (bss_conf->use_cts_prot)
- ctx->staging.flags |= RXON_FLG_SELF_CTS_EN;
- else
- ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN;
-
memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN);
if (vif->type == NL80211_IFTYPE_AP ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c
index 51c41531d81d..51486cc9d943 100644
--- a/drivers/net/wireless/iwlwifi/iwl-8000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-8000.c
@@ -67,7 +67,7 @@
#include "iwl-agn-hw.h"
/* Highest firmware API version supported */
-#define IWL8000_UCODE_API_MAX 8
+#define IWL8000_UCODE_API_MAX 9
/* Oldest version we won't warn about */
#define IWL8000_UCODE_API_OK 8
@@ -119,10 +119,9 @@ const struct iwl_cfg iwl8260_2ac_cfg = {
.ht_params = &iwl8000_ht_params,
.nvm_ver = IWL8000_NVM_VERSION,
.nvm_calib_ver = IWL8000_TX_POWER_VERSION,
- .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000,
};
-const struct iwl_cfg iwl8260_n_cfg = {
+const struct iwl_cfg iwl8260_2ac_sdio_cfg = {
.name = "Intel(R) Dual Band Wireless-AC 8260",
.fw_name_pre = IWL8000_FW_PRE,
IWL_DEVICE_8000,
diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h
index b7047905f41a..034c2fc4b69f 100644
--- a/drivers/net/wireless/iwlwifi/iwl-config.h
+++ b/drivers/net/wireless/iwlwifi/iwl-config.h
@@ -337,7 +337,7 @@ extern const struct iwl_cfg iwl7265_2ac_cfg;
extern const struct iwl_cfg iwl7265_2n_cfg;
extern const struct iwl_cfg iwl7265_n_cfg;
extern const struct iwl_cfg iwl8260_2ac_cfg;
-extern const struct iwl_cfg iwl8260_n_cfg;
+extern const struct iwl_cfg iwl8260_2ac_sdio_cfg;
#endif /* CONFIG_IWLMVM */
#endif /* __IWL_CONFIG_H__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index f2a5c12269a3..77e3178040b2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -155,6 +155,8 @@ static struct iwlwifi_opmode_table {
[MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL },
};
+#define IWL_DEFAULT_SCAN_CHANNELS 40
+
/*
* struct fw_sec: Just for the image parsing proccess.
* For the fw storage we are using struct fw_desc.
@@ -565,6 +567,8 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
}
drv->fw.ucode_ver = le32_to_cpu(ucode->ver);
+ memcpy(drv->fw.human_readable, ucode->human_readable,
+ sizeof(drv->fw.human_readable));
build = le32_to_cpu(ucode->build);
if (build)
@@ -819,6 +823,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
if (iwl_store_cscheme(&drv->fw, tlv_data, tlv_len))
goto invalid_tlv_len;
break;
+ case IWL_UCODE_TLV_N_SCAN_CHANNELS:
+ if (tlv_len != sizeof(u32))
+ goto invalid_tlv_len;
+ capa->n_scan_channels =
+ le32_to_cpup((__le32 *)tlv_data);
+ break;
default:
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
break;
@@ -973,6 +983,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH;
fw->ucode_capa.standard_phy_calibration_size =
IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE;
+ fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS;
if (!api_ok)
api_ok = api_max;
@@ -1394,3 +1405,7 @@ module_param_named(power_level, iwlwifi_mod_params.power_level,
int, S_IRUGO);
MODULE_PARM_DESC(power_level,
"default power save level (range from 1 - 5, default: 1)");
+
+module_param_named(fw_monitor, iwlwifi_mod_params.fw_monitor, bool, S_IRUGO);
+MODULE_PARM_DESC(fw_monitor,
+ "firmware monitor - to debug FW (default: false - needs lots of memory)");
diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
index c44cf1149648..07ff7e0028ee 100644
--- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c
@@ -779,7 +779,6 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg,
if (cfg->ht_params->ht40_bands & BIT(band)) {
ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
ht_info->cap |= IEEE80211_HT_CAP_SGI_40;
- ht_info->mcs.rx_mask[4] = 0x01;
max_bit_rate = MAX_BIT_RATE_40_MHZ;
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
index 2953ffceda38..c39a0b899e83 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
@@ -74,12 +74,17 @@
* @IWL_FW_ERROR_DUMP_RXF:
* @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as
* &struct iwl_fw_error_dump_txcmd packets
+ * @IWL_FW_ERROR_DUMP_DEV_FW_INFO: struct %iwl_fw_error_dump_info
+ * info on the device / firmware.
+ * @IWL_FW_ERROR_DUMP_FW_MONITOR: firmware monitor
*/
enum iwl_fw_error_dump_type {
IWL_FW_ERROR_DUMP_SRAM = 0,
IWL_FW_ERROR_DUMP_REG = 1,
IWL_FW_ERROR_DUMP_RXF = 2,
IWL_FW_ERROR_DUMP_TXCMD = 3,
+ IWL_FW_ERROR_DUMP_DEV_FW_INFO = 4,
+ IWL_FW_ERROR_DUMP_FW_MONITOR = 5,
IWL_FW_ERROR_DUMP_MAX,
};
@@ -87,8 +92,8 @@ enum iwl_fw_error_dump_type {
/**
* struct iwl_fw_error_dump_data - data for one type
* @type: %enum iwl_fw_error_dump_type
- * @len: the length starting from %data - must be a multiplier of 4.
- * @data: the data itself padded to be a multiplier of 4.
+ * @len: the length starting from %data
+ * @data: the data itself
*/
struct iwl_fw_error_dump_data {
__le32 type;
@@ -120,13 +125,50 @@ struct iwl_fw_error_dump_txcmd {
u8 data[];
} __packed;
+enum iwl_fw_error_dump_family {
+ IWL_FW_ERROR_DUMP_FAMILY_7 = 7,
+ IWL_FW_ERROR_DUMP_FAMILY_8 = 8,
+};
+
+/**
+ * struct iwl_fw_error_dump_info - info on the device / firmware
+ * @device_family: the family of the device (7 / 8)
+ * @hw_step: the step of the device
+ * @fw_human_readable: human readable FW version
+ * @dev_human_readable: name of the device
+ * @bus_human_readable: name of the bus used
+ */
+struct iwl_fw_error_dump_info {
+ __le32 device_family;
+ __le32 hw_step;
+ u8 fw_human_readable[FW_VER_HUMAN_READABLE_SZ];
+ u8 dev_human_readable[64];
+ u8 bus_human_readable[8];
+} __packed;
+
+/**
+ * struct iwl_fw_error_dump_fw_mon - FW monitor data
+ * @fw_mon_wr_ptr: the position of the write pointer in the cyclic buffer
+ * @fw_mon_base_ptr: base pointer of the data
+ * @fw_mon_cycle_cnt: number of wrap arounds
+ * @reserved: for future use
+ * @data: captured data
+ */
+struct iwl_fw_error_dump_fw_mon {
+ __le32 fw_mon_wr_ptr;
+ __le32 fw_mon_base_ptr;
+ __le32 fw_mon_cycle_cnt;
+ __le32 reserved[3];
+ u8 data[];
+} __packed;
+
/**
- * iwl_mvm_fw_error_next_data - advance fw error dump data pointer
+ * iwl_fw_error_next_data - advance fw error dump data pointer
* @data: previous data block
* Returns: next data block
*/
static inline struct iwl_fw_error_dump_data *
-iwl_mvm_fw_error_next_data(struct iwl_fw_error_dump_data *data)
+iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
{
return (void *)(data->data + le32_to_cpu(data->len));
}
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
index b45e576a4b57..929a8063354c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
@@ -128,6 +128,7 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_CSCHEME = 28,
IWL_UCODE_TLV_API_CHANGES_SET = 29,
IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30,
+ IWL_UCODE_TLV_N_SCAN_CHANNELS = 31,
};
struct iwl_ucode_tlv {
@@ -136,7 +137,8 @@ struct iwl_ucode_tlv {
u8 data[0];
};
-#define IWL_TLV_UCODE_MAGIC 0x0a4c5749
+#define IWL_TLV_UCODE_MAGIC 0x0a4c5749
+#define FW_VER_HUMAN_READABLE_SZ 64
struct iwl_tlv_ucode_header {
/*
@@ -147,7 +149,7 @@ struct iwl_tlv_ucode_header {
*/
__le32 zero;
__le32 magic;
- u8 human_readable[64];
+ u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
__le32 ver; /* major/minor/API/serial */
__le32 build;
__le64 ignore;
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index 0aa7c0085c9f..1bb5193c5b1b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -65,6 +65,8 @@
#include <linux/types.h>
#include <net/mac80211.h>
+#include "iwl-fw-file.h"
+
/**
* enum iwl_ucode_tlv_flag - ucode API flags
* @IWL_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously
@@ -88,6 +90,7 @@
* P2P client interfaces simultaneously if they are in different bindings.
* @IWL_UCODE_TLV_FLAGS_P2P_BSS_PS_SCM: support power save on BSS station and
* P2P client interfaces simultaneously if they are in same bindings.
+ * @IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD
* @IWL_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save
* @IWL_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering.
* @IWL_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients
@@ -117,11 +120,19 @@ enum iwl_ucode_tlv_flag {
/**
* enum iwl_ucode_tlv_api - ucode api
* @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
+ * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification
+ * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
* @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA.
+ * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
+ * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
*/
enum iwl_ucode_tlv_api {
IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0),
+ IWL_UCODE_TLV_CAPA_EXTENDED_BEACON = BIT(1),
+ IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3),
IWL_UCODE_TLV_API_CSA_FLOW = BIT(4),
+ IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5),
+ IWL_UCODE_TLV_API_LMAC_SCAN = BIT(6),
};
/**
@@ -178,6 +189,7 @@ enum iwl_ucode_sec {
struct iwl_ucode_capabilities {
u32 max_probe_length;
+ u32 n_scan_channels;
u32 standard_phy_calibration_size;
u32 flags;
u32 api[IWL_API_ARRAY_SIZE];
@@ -311,6 +323,7 @@ struct iwl_fw {
bool mvm_fw;
struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS];
+ u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
};
#endif /* __iwl_fw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h
index d051857729ab..f2d39cb011fc 100644
--- a/drivers/net/wireless/iwlwifi/iwl-modparams.h
+++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h
@@ -103,6 +103,7 @@ enum iwl_disable_11n {
* @power_level: power level, default = 1
* @debug_level: levels are IWL_DL_*
* @ant_coupling: antenna coupling in dB, default = 0
+ * @fw_monitor: allow to use firmware monitor
*/
struct iwl_mod_params {
int sw_crypto;
@@ -120,6 +121,7 @@ struct iwl_mod_params {
int ant_coupling;
char *nvm_file;
bool uapsd_disable;
+ bool fw_monitor;
};
#endif /* #__iwl_modparams_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
index 85eee79c495c..018af2957d3b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
+++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c
@@ -63,6 +63,7 @@
#include <linux/slab.h>
#include <linux/export.h>
#include <linux/etherdevice.h>
+#include <linux/pci.h>
#include "iwl-drv.h"
#include "iwl-modparams.h"
#include "iwl-nvm-parse.h"
@@ -87,8 +88,10 @@ enum wkp_nvm_offsets {
enum family_8000_nvm_offsets {
/* NVM HW-Section offset (in words) definitions */
- HW_ADDR0_FAMILY_8000 = 0x12,
- HW_ADDR1_FAMILY_8000 = 0x16,
+ HW_ADDR0_WFPM_FAMILY_8000 = 0x12,
+ HW_ADDR1_WFPM_FAMILY_8000 = 0x16,
+ HW_ADDR0_PCIE_FAMILY_8000 = 0x8A,
+ HW_ADDR1_PCIE_FAMILY_8000 = 0x8E,
MAC_ADDRESS_OVERRIDE_FAMILY_8000 = 1,
/* NVM SW-Section offset (in words) definitions */
@@ -174,7 +177,9 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = {
* @NVM_CHANNEL_IBSS: usable as an IBSS channel
* @NVM_CHANNEL_ACTIVE: active scanning allowed
* @NVM_CHANNEL_RADAR: radar detection required
- * @NVM_CHANNEL_DFS: dynamic freq selection candidate
+ * @NVM_CHANNEL_INDOOR_ONLY: only indoor use is allowed
+ * @NVM_CHANNEL_GO_CONCURRENT: GO operation is allowed when connected to BSS
+ * on same channel on 2.4 or same UNII band on 5.2
* @NVM_CHANNEL_WIDE: 20 MHz channel okay (?)
* @NVM_CHANNEL_40MHZ: 40 MHz channel okay (?)
* @NVM_CHANNEL_80MHZ: 80 MHz channel okay (?)
@@ -185,7 +190,8 @@ enum iwl_nvm_channel_flags {
NVM_CHANNEL_IBSS = BIT(1),
NVM_CHANNEL_ACTIVE = BIT(3),
NVM_CHANNEL_RADAR = BIT(4),
- NVM_CHANNEL_DFS = BIT(7),
+ NVM_CHANNEL_INDOOR_ONLY = BIT(5),
+ NVM_CHANNEL_GO_CONCURRENT = BIT(6),
NVM_CHANNEL_WIDE = BIT(8),
NVM_CHANNEL_40MHZ = BIT(9),
NVM_CHANNEL_80MHZ = BIT(10),
@@ -273,6 +279,16 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
if (ch_flags & NVM_CHANNEL_RADAR)
channel->flags |= IEEE80211_CHAN_RADAR;
+ if (ch_flags & NVM_CHANNEL_INDOOR_ONLY)
+ channel->flags |= IEEE80211_CHAN_INDOOR_ONLY;
+
+ /* Set the GO concurrent flag only in case that NO_IR is set.
+ * Otherwise it is meaningless
+ */
+ if ((ch_flags & NVM_CHANNEL_GO_CONCURRENT) &&
+ (channel->flags & IEEE80211_CHAN_NO_IR))
+ channel->flags |= IEEE80211_CHAN_GO_CONCURRENT;
+
/* Initialize regulatory-based run-time data */
/*
@@ -282,7 +298,7 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
channel->max_power = DEFAULT_MAX_TX_POWER;
is_5ghz = channel->band == IEEE80211_BAND_5GHZ;
IWL_DEBUG_EEPROM(dev,
- "Ch. %d [%sGHz] %s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
+ "Ch. %d [%sGHz] %s%s%s%s%s%s%s(0x%02x %ddBm): Ad-Hoc %ssupported\n",
channel->hw_value,
is_5ghz ? "5.2" : "2.4",
CHECK_AND_PRINT_I(VALID),
@@ -290,7 +306,8 @@ static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg,
CHECK_AND_PRINT_I(ACTIVE),
CHECK_AND_PRINT_I(RADAR),
CHECK_AND_PRINT_I(WIDE),
- CHECK_AND_PRINT_I(DFS),
+ CHECK_AND_PRINT_I(INDOOR_ONLY),
+ CHECK_AND_PRINT_I(GO_CONCURRENT),
ch_flags,
channel->max_power,
((ch_flags & NVM_CHANNEL_IBSS) &&
@@ -462,7 +479,8 @@ static void iwl_set_hw_address(const struct iwl_cfg *cfg,
data->hw_addr[5] = hw_addr[4];
}
-static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg,
+static void iwl_set_hw_address_family_8000(struct device *dev,
+ const struct iwl_cfg *cfg,
struct iwl_nvm_data *data,
const __le16 *mac_override,
const __le16 *nvm_hw)
@@ -481,20 +499,64 @@ static void iwl_set_hw_address_family_8000(const struct iwl_cfg *cfg,
data->hw_addr[4] = hw_addr[5];
data->hw_addr[5] = hw_addr[4];
- if (is_valid_ether_addr(hw_addr))
+ if (is_valid_ether_addr(data->hw_addr))
return;
+
+ IWL_ERR_DEV(dev,
+ "mac address from nvm override section is not valid\n");
}
- /* take the MAC address from the OTP */
- hw_addr = (const u8 *)(nvm_hw + HW_ADDR0_FAMILY_8000);
- data->hw_addr[0] = hw_addr[3];
- data->hw_addr[1] = hw_addr[2];
- data->hw_addr[2] = hw_addr[1];
- data->hw_addr[3] = hw_addr[0];
+ if (nvm_hw) {
+ /* read the MAC address from OTP */
+ if (!dev_is_pci(dev) || (data->nvm_version < 0xE08)) {
+ /* read the mac address from the WFPM location */
+ hw_addr = (const u8 *)(nvm_hw +
+ HW_ADDR0_WFPM_FAMILY_8000);
+ data->hw_addr[0] = hw_addr[3];
+ data->hw_addr[1] = hw_addr[2];
+ data->hw_addr[2] = hw_addr[1];
+ data->hw_addr[3] = hw_addr[0];
+
+ hw_addr = (const u8 *)(nvm_hw +
+ HW_ADDR1_WFPM_FAMILY_8000);
+ data->hw_addr[4] = hw_addr[1];
+ data->hw_addr[5] = hw_addr[0];
+ } else if ((data->nvm_version >= 0xE08) &&
+ (data->nvm_version < 0xE0B)) {
+ /* read "reverse order" from the PCIe location */
+ hw_addr = (const u8 *)(nvm_hw +
+ HW_ADDR0_PCIE_FAMILY_8000);
+ data->hw_addr[5] = hw_addr[2];
+ data->hw_addr[4] = hw_addr[1];
+ data->hw_addr[3] = hw_addr[0];
+
+ hw_addr = (const u8 *)(nvm_hw +
+ HW_ADDR1_PCIE_FAMILY_8000);
+ data->hw_addr[2] = hw_addr[3];
+ data->hw_addr[1] = hw_addr[2];
+ data->hw_addr[0] = hw_addr[1];
+ } else {
+ /* read from the PCIe location */
+ hw_addr = (const u8 *)(nvm_hw +
+ HW_ADDR0_PCIE_FAMILY_8000);
+ data->hw_addr[5] = hw_addr[0];
+ data->hw_addr[4] = hw_addr[1];
+ data->hw_addr[3] = hw_addr[2];
+
+ hw_addr = (const u8 *)(nvm_hw +
+ HW_ADDR1_PCIE_FAMILY_8000);
+ data->hw_addr[2] = hw_addr[1];
+ data->hw_addr[1] = hw_addr[2];
+ data->hw_addr[0] = hw_addr[3];
+ }
+ if (!is_valid_ether_addr(data->hw_addr))
+ IWL_ERR_DEV(dev,
+ "mac address from hw section is not valid\n");
+
+ return;
+ }
- hw_addr = (const u8 *)(nvm_hw + HW_ADDR1_FAMILY_8000);
- data->hw_addr[4] = hw_addr[1];
- data->hw_addr[5] = hw_addr[0];
+ IWL_ERR_DEV(dev, "mac address is not found\n");
}
struct iwl_nvm_data *
@@ -556,7 +618,8 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg,
rx_chains);
} else {
/* MAC address in family 8000 */
- iwl_set_hw_address_family_8000(cfg, data, mac_override, nvm_hw);
+ iwl_set_hw_address_family_8000(dev, cfg, data, mac_override,
+ nvm_hw);
iwl_init_sbands(dev, cfg, data, regulatory,
sku & NVM_SKU_CAP_11AC_ENABLE, tx_chains,
diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h
index 4997e27672b3..47033a35a402 100644
--- a/drivers/net/wireless/iwlwifi/iwl-prph.h
+++ b/drivers/net/wireless/iwlwifi/iwl-prph.h
@@ -359,4 +359,10 @@ enum secure_load_status_reg {
#define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10)
#define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c)
+/* FW monitor */
+#define MON_BUFF_BASE_ADDR (0xa03c3c)
+#define MON_BUFF_END_ADDR (0xa03c40)
+#define MON_BUFF_WRPTR (0xa03c44)
+#define MON_BUFF_CYCLE_CNT (0xa03c48)
+
#endif /* __iwl_prph_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile
index c30d7f64ec1e..a28235913c2c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/Makefile
+++ b/drivers/net/wireless/iwlwifi/mvm/Makefile
@@ -2,7 +2,7 @@ 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 sf.o
iwlmvm-y += scan.o time-event.o rs.o
-iwlmvm-y += power.o coex.o
+iwlmvm-y += power.o coex.o coex_legacy.o
iwlmvm-y += tt.o offloading.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
iwlmvm-$(CONFIG_IWLWIFI_LEDS) += led.o
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c
index c8c3b38228f0..8110fe00bf55 100644
--- a/drivers/net/wireless/iwlwifi/mvm/coex.c
+++ b/drivers/net/wireless/iwlwifi/mvm/coex.c
@@ -70,47 +70,8 @@
#include "mvm.h"
#include "iwl-debug.h"
-#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \
- [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \
- ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
-
-static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
- BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
- BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
- BT_COEX_PRIO_TBL_PRIO_LOW, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
- BT_COEX_PRIO_TBL_PRIO_LOW, 1),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
- BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
- BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
- BT_COEX_PRIO_TBL_DISABLED, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
- BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
- BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
- EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
- BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
- 0, 0, 0, 0, 0, 0,
-};
-
-#undef EVENT_PRIO_ANT
-
-#define BT_ENABLE_REDUCED_TXPOWER_THRESHOLD (-62)
-#define BT_DISABLE_REDUCED_TXPOWER_THRESHOLD (-65)
#define BT_ANTENNA_COUPLING_THRESHOLD (30)
-static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
-{
- return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
- sizeof(struct iwl_bt_coex_prio_tbl_cmd),
- &iwl_bt_prio_tbl);
-}
-
const u32 iwl_bt_ack_kill_msk[BT_KILL_MSK_MAX] = {
[BT_KILL_MSK_DEFAULT] = 0xffff0000,
[BT_KILL_MSK_SCO_HID_A2DP] = 0xffffffff,
@@ -519,6 +480,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
struct ieee80211_chanctx_conf *chanctx_conf;
enum iwl_bt_coex_lut_type ret;
u16 phy_ctx_id;
+ u32 primary_ch_phy_id, secondary_ch_phy_id;
/*
* Checking that we hold mvm->mutex is a good idea, but the rate
@@ -535,7 +497,7 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
if (!chanctx_conf ||
chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
rcu_read_unlock();
- return BT_COEX_LOOSE_LUT;
+ return BT_COEX_INVALID_LUT;
}
ret = BT_COEX_TX_DIS_LUT;
@@ -546,10 +508,13 @@ iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
}
phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
+ primary_ch_phy_id = le32_to_cpu(mvm->last_bt_ci_cmd.primary_ch_phy_id);
+ secondary_ch_phy_id =
+ le32_to_cpu(mvm->last_bt_ci_cmd.secondary_ch_phy_id);
- if (mvm->last_bt_ci_cmd.primary_ch_phy_id == phy_ctx_id)
+ if (primary_ch_phy_id == phy_ctx_id)
ret = le32_to_cpu(mvm->last_bt_notif.primary_ch_lut);
- else if (mvm->last_bt_ci_cmd.secondary_ch_phy_id == phy_ctx_id)
+ else if (secondary_ch_phy_id == phy_ctx_id)
ret = le32_to_cpu(mvm->last_bt_notif.secondary_ch_lut);
/* else - default = TX TX disallowed */
@@ -567,59 +532,63 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
.dataflags = { IWL_HCMD_DFL_NOCOPY, },
};
int ret;
- u32 flags;
+ u32 mode;
- ret = iwl_send_bt_prio_tbl(mvm);
- if (ret)
- return ret;
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_send_bt_init_conf_old(mvm);
bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
if (!bt_cmd)
return -ENOMEM;
cmd.data[0] = bt_cmd;
- bt_cmd->max_kill = 5;
- bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD;
- bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
- bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
- bt_cmd->bt4_tx_rx_max_freq0 = 15;
- bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
- bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
-
- flags = iwlwifi_mod_params.bt_coex_active ?
- BT_COEX_NW : BT_COEX_DISABLE;
- bt_cmd->flags = cpu_to_le32(flags);
-
- bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
- BT_VALID_BT_PRIO_BOOST |
- BT_VALID_MAX_KILL |
- BT_VALID_3W_TMRS |
- BT_VALID_KILL_ACK |
- BT_VALID_KILL_CTS |
- BT_VALID_REDUCED_TX_POWER |
- BT_VALID_LUT |
- BT_VALID_WIFI_RX_SW_PRIO_BOOST |
- BT_VALID_WIFI_TX_SW_PRIO_BOOST |
- BT_VALID_ANT_ISOLATION |
- BT_VALID_ANT_ISOLATION_THRS |
- BT_VALID_TXTX_DELTA_FREQ_THRS |
- BT_VALID_TXRX_MAX_FREQ_0 |
- BT_VALID_SYNC_TO_SCO);
+ lockdep_assert_held(&mvm->mutex);
- if (IWL_MVM_BT_COEX_SYNC2SCO)
- bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
+ u32 mode;
- if (IWL_MVM_BT_COEX_CORUNNING) {
- bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
- BT_VALID_CORUN_LUT_40);
- bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
+ switch (mvm->bt_force_ant_mode) {
+ case BT_FORCE_ANT_BT:
+ mode = BT_COEX_BT;
+ break;
+ case BT_FORCE_ANT_WIFI:
+ mode = BT_COEX_WIFI;
+ break;
+ default:
+ WARN_ON(1);
+ mode = 0;
+ }
+
+ bt_cmd->mode = cpu_to_le32(mode);
+ goto send_cmd;
}
+ bt_cmd->max_kill = cpu_to_le32(5);
+ bt_cmd->bt4_antenna_isolation_thr =
+ cpu_to_le32(BT_ANTENNA_COUPLING_THRESHOLD);
+ bt_cmd->bt4_tx_tx_delta_freq_thr = cpu_to_le32(15);
+ bt_cmd->bt4_tx_rx_max_freq0 = cpu_to_le32(15);
+ bt_cmd->override_primary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
+ bt_cmd->override_secondary_lut = cpu_to_le32(BT_COEX_INVALID_LUT);
+
+ mode = iwlwifi_mod_params.bt_coex_active ? BT_COEX_NW : BT_COEX_DISABLE;
+ bt_cmd->mode = cpu_to_le32(mode);
+
+ if (IWL_MVM_BT_COEX_SYNC2SCO)
+ bt_cmd->enabled_modules |=
+ cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED);
+
+ if (IWL_MVM_BT_COEX_CORUNNING)
+ bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED);
+
if (IWL_MVM_BT_COEX_MPLUT) {
- bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
- bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
+ bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_MPLUT_ENABLED);
+ bt_cmd->enabled_modules |=
+ cpu_to_le32(BT_COEX_MPLUT_BOOST_ENABLED);
}
+ bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_HIGH_BAND_RET);
+
if (mvm->cfg->bt_shared_single_ant)
memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
sizeof(iwl_single_shared_ant));
@@ -627,21 +596,12 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
sizeof(iwl_combined_lookup));
- /* Take first Co-running block LUT to get started */
- memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
- sizeof(bt_cmd->bt4_corun_lut20));
- memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
- sizeof(bt_cmd->bt4_corun_lut40));
-
- memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
+ memcpy(&bt_cmd->mplut_prio_boost, iwl_bt_prio_boost,
sizeof(iwl_bt_prio_boost));
- memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
+ memcpy(&bt_cmd->multiprio_lut, iwl_bt_mprio_lut,
sizeof(iwl_bt_mprio_lut));
- bt_cmd->kill_ack_msk =
- cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
- bt_cmd->kill_cts_msk =
- cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
+send_cmd:
memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
memset(&mvm->last_bt_ci_cmd, 0, sizeof(mvm->last_bt_ci_cmd));
@@ -651,19 +611,12 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
return ret;
}
-static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
- bool reduced_tx_power)
+static int iwl_mvm_bt_udpate_sw_boost(struct iwl_mvm *mvm,
+ bool reduced_tx_power)
{
enum iwl_bt_kill_msk bt_kill_msk;
- struct iwl_bt_coex_cmd *bt_cmd;
+ struct iwl_bt_coex_sw_boost_update_cmd cmd = {};
struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
- struct iwl_host_cmd cmd = {
- .id = BT_CONFIG,
- .data[0] = &bt_cmd,
- .len = { sizeof(*bt_cmd), },
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
- };
- int ret = 0;
lockdep_assert_held(&mvm->mutex);
@@ -693,40 +646,30 @@ static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
mvm->bt_kill_msk = bt_kill_msk;
- bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
- if (!bt_cmd)
- return -ENOMEM;
- cmd.data[0] = bt_cmd;
- bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
+ cmd.boost_values[0].kill_ack_msk =
+ cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
+ cmd.boost_values[0].kill_cts_msk =
+ cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
- bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
- bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
- bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
- BT_VALID_KILL_ACK |
- BT_VALID_KILL_CTS);
+ cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk;
+ cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk;
+ cmd.boost_values[1].kill_ack_msk = cmd.boost_values[0].kill_ack_msk;
+ cmd.boost_values[2].kill_cts_msk = cmd.boost_values[0].kill_cts_msk;
IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
iwl_bt_ack_kill_msk[bt_kill_msk],
iwl_bt_cts_kill_msk[bt_kill_msk]);
- ret = iwl_mvm_send_cmd(mvm, &cmd);
-
- kfree(bt_cmd);
- return ret;
+ return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_SW_BOOST, 0,
+ sizeof(cmd), &cmd);
}
static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
bool enable)
{
- struct iwl_bt_coex_cmd *bt_cmd;
- /* Send ASYNC since this can be sent from an atomic context */
- struct iwl_host_cmd cmd = {
- .id = BT_CONFIG,
- .len = { sizeof(*bt_cmd), },
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
- .flags = CMD_ASYNC,
- };
+ struct iwl_bt_coex_reduced_txp_update_cmd cmd = {};
struct iwl_mvm_sta *mvmsta;
+ u32 value;
int ret;
mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
@@ -737,27 +680,20 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
if (mvmsta->bt_reduced_txpower == enable)
return 0;
- bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
- if (!bt_cmd)
- return -ENOMEM;
- cmd.data[0] = bt_cmd;
- bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
-
- bt_cmd->valid_bit_msk =
- cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
- bt_cmd->bt_reduced_tx_power = sta_id;
+ value = mvmsta->sta_id;
if (enable)
- bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
+ value |= BT_REDUCED_TX_POWER_BIT;
IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
enable ? "en" : "dis", sta_id);
+ cmd.reduced_txp = cpu_to_le32(value);
mvmsta->bt_reduced_txpower = enable;
- ret = iwl_mvm_send_cmd(mvm, &cmd);
+ ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC,
+ sizeof(cmd), &cmd);
- kfree(bt_cmd);
return ret;
}
@@ -780,9 +716,9 @@ void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
mvmvif->bf_data.last_bt_coex_event = rssi;
mvmvif->bf_data.bt_coex_max_thold =
- enable ? BT_ENABLE_REDUCED_TXPOWER_THRESHOLD : 0;
+ enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
mvmvif->bf_data.bt_coex_min_thold =
- enable ? BT_DISABLE_REDUCED_TXPOWER_THRESHOLD : 0;
+ enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
}
/* must be called under rcu_read_lock */
@@ -851,10 +787,13 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
if (!vif->bss_conf.assoc)
smps_mode = IEEE80211_SMPS_AUTOMATIC;
+ if (IWL_COEX_IS_RRC_ON(mvm->last_bt_notif.ttc_rrc_status,
+ mvmvif->phy_ctxt->id))
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
IWL_DEBUG_COEX(data->mvm,
- "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
- mvmvif->id, data->notif->bt_status, bt_activity_grading,
- smps_mode);
+ "mac %d: bt_activity_grading %d smps_req %d\n",
+ mvmvif->id, bt_activity_grading, smps_mode);
iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
@@ -906,7 +845,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
*/
if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
- !data->notif->bt_status) {
+ le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF) {
data->reduced_tx_power = false;
iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
@@ -919,7 +858,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
/* if the RSSI isn't valid, fake it is very low */
if (!ave_rssi)
ave_rssi = -100;
- if (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) {
+ if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
@@ -930,7 +869,7 @@ static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
* the iteration, if one interface's rssi isn't good enough,
* bt_kill_msk will be set to default values.
*/
- } else if (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) {
+ } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
@@ -955,6 +894,10 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
struct iwl_bt_coex_ci_cmd cmd = {};
u8 ci_bw_idx;
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return;
+
rcu_read_lock();
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
@@ -969,9 +912,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
if (chan->def.width < NL80211_CHAN_WIDTH_40) {
ci_bw_idx = 0;
- cmd.co_run_bw_primary = 0;
} else {
- cmd.co_run_bw_primary = 1;
if (chan->def.center_freq1 >
chan->def.chan->center_freq)
ci_bw_idx = 2;
@@ -981,7 +922,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
cmd.bt_primary_ci =
iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
- cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+ cmd.primary_ch_phy_id =
+ cpu_to_le32(*((u16 *)data.primary->drv_priv));
}
if (data.secondary) {
@@ -993,9 +935,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
if (chan->def.width < NL80211_CHAN_WIDTH_40) {
ci_bw_idx = 0;
- cmd.co_run_bw_secondary = 0;
} else {
- cmd.co_run_bw_secondary = 1;
if (chan->def.center_freq1 >
chan->def.chan->center_freq)
ci_bw_idx = 2;
@@ -1005,7 +945,8 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
cmd.bt_secondary_ci =
iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
- cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
+ cmd.secondary_ch_phy_id =
+ cpu_to_le32(*((u16 *)data.secondary->drv_priv));
}
rcu_read_unlock();
@@ -1025,7 +966,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
*/
data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
- if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+ if (iwl_mvm_bt_udpate_sw_boost(mvm, data.reduced_tx_power))
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
}
@@ -1036,11 +977,10 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_bt_coex_profile_notif *notif = (void *)pkt->data;
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_rx_bt_coex_notif_old(mvm, rxb, dev_cmd);
IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
- IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
- notif->bt_status ? "ON" : "OFF");
- IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
le32_to_cpu(notif->primary_ch_lut));
@@ -1048,8 +988,6 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm,
le32_to_cpu(notif->secondary_ch_lut));
IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
le32_to_cpu(notif->bt_activity_grading));
- IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
- notif->bt_agg_traffic_load);
/* remember this notification for future use: rssi fluctuations */
memcpy(&mvm->last_bt_notif, notif, sizeof(mvm->last_bt_notif));
@@ -1119,8 +1057,17 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
};
int ret;
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+ iwl_mvm_bt_rssi_event_old(mvm, vif, rssi_event);
+ return;
+ }
+
lockdep_assert_held(&mvm->mutex);
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return;
+
/*
* Rssi update while not associated - can happen since the statistics
* are handled asynchronously
@@ -1129,7 +1076,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
return;
/* No BT - reports should be disabled */
- if (!mvm->last_bt_notif.bt_status)
+ if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF)
return;
IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
@@ -1160,7 +1107,7 @@ void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
*/
data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
- if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+ if (iwl_mvm_bt_udpate_sw_boost(mvm, data.reduced_tx_power))
IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
}
@@ -1171,15 +1118,23 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
struct ieee80211_sta *sta)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+ struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
enum iwl_bt_coex_lut_type lut_type;
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_coex_agg_time_limit_old(mvm, sta);
+
+ if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
+ return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
BT_HIGH_TRAFFIC)
return LINK_QUAL_AGG_TIME_LIMIT_DEF;
lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
- if (lut_type == BT_COEX_LOOSE_LUT)
+ if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
return LINK_QUAL_AGG_TIME_LIMIT_DEF;
/* tight coex, high bt traffic, reduce AGG time limit */
@@ -1190,18 +1145,37 @@ bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
struct ieee80211_sta *sta)
{
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(mvmsta->vif);
+ struct iwl_mvm_phy_ctxt *phy_ctxt = mvmvif->phy_ctxt;
+ enum iwl_bt_coex_lut_type lut_type;
+
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_coex_agg_time_limit_old(mvm, sta);
+
+ if (IWL_COEX_IS_TTC_ON(mvm->last_bt_notif.ttc_rrc_status, phy_ctxt->id))
+ return true;
if (le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) <
BT_HIGH_TRAFFIC)
return true;
/*
- * In Tight, BT can't Rx while we Tx, so use both antennas since BT is
- * already killed.
- * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while we
- * Tx.
+ * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
+ * since BT is already killed.
+ * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
+ * we Tx.
+ * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
*/
- return iwl_get_coex_type(mvm, mvmsta->vif) == BT_COEX_TIGHT_LUT;
+ lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+ return lut_type != BT_COEX_LOOSE_LUT;
+}
+
+bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm)
+{
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_bt_coex_is_shared_ant_avail_old(mvm);
+
+ return le32_to_cpu(mvm->last_bt_notif.bt_activity_grading) == BT_OFF;
}
bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
@@ -1209,6 +1183,9 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
{
u32 bt_activity = le32_to_cpu(mvm->last_bt_notif.bt_activity_grading);
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_bt_coex_is_tpc_allowed_old(mvm, band);
+
if (band != IEEE80211_BAND_2GHZ)
return false;
@@ -1249,6 +1226,11 @@ u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
void iwl_mvm_bt_coex_vif_change(struct iwl_mvm *mvm)
{
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+ iwl_mvm_bt_coex_vif_change_old(mvm);
+ return;
+ }
+
iwl_mvm_bt_coex_notif_handle(mvm);
}
@@ -1258,22 +1240,22 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
u32 ant_isolation = le32_to_cpup((void *)pkt->data);
+ struct iwl_bt_coex_corun_lut_update_cmd cmd = {};
u8 __maybe_unused lower_bound, upper_bound;
- int ret;
u8 lut;
- struct iwl_bt_coex_cmd *bt_cmd;
- struct iwl_host_cmd cmd = {
- .id = BT_CONFIG,
- .len = { sizeof(*bt_cmd), },
- .dataflags = { IWL_HCMD_DFL_NOCOPY, },
- };
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
+ return iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb, dev_cmd);
if (!IWL_MVM_BT_COEX_CORUNNING)
return 0;
lockdep_assert_held(&mvm->mutex);
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return 0;
+
if (ant_isolation == mvm->last_ant_isol)
return 0;
@@ -1298,25 +1280,13 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
mvm->last_corun_lut = lut;
- bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
- if (!bt_cmd)
- return 0;
- cmd.data[0] = bt_cmd;
-
- bt_cmd->flags = cpu_to_le32(BT_COEX_NW);
- bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
- BT_VALID_CORUN_LUT_20 |
- BT_VALID_CORUN_LUT_40);
-
/* For the moment, use the same LUT for 20GHz and 40GHz */
- memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
- sizeof(bt_cmd->bt4_corun_lut20));
-
- memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
- sizeof(bt_cmd->bt4_corun_lut40));
+ memcpy(&cmd.corun_lut20, antenna_coupling_ranges[lut].lut20,
+ sizeof(cmd.corun_lut20));
- ret = iwl_mvm_send_cmd(mvm, &cmd);
+ memcpy(&cmd.corun_lut40, antenna_coupling_ranges[lut].lut20,
+ sizeof(cmd.corun_lut40));
- kfree(bt_cmd);
- return ret;
+ return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_CORUN_LUT, 0,
+ sizeof(cmd), &cmd);
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
new file mode 100644
index 000000000000..ce50363d314b
--- /dev/null
+++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c
@@ -0,0 +1,1332 @@
+/******************************************************************************
+ *
+ * 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 - 2014 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 - 2014 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *****************************************************************************/
+
+#include <linux/ieee80211.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+
+#include "fw-api-coex.h"
+#include "iwl-modparams.h"
+#include "mvm.h"
+#include "iwl-debug.h"
+
+#define EVENT_PRIO_ANT(_evt, _prio, _shrd_ant) \
+ [(_evt)] = (((_prio) << BT_COEX_PRIO_TBL_PRIO_POS) | \
+ ((_shrd_ant) << BT_COEX_PRIO_TBL_SHRD_ANT_POS))
+
+static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = {
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB1,
+ BT_COEX_PRIO_TBL_PRIO_BYPASS, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_INIT_CALIB2,
+ BT_COEX_PRIO_TBL_PRIO_BYPASS, 1),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW1,
+ BT_COEX_PRIO_TBL_PRIO_LOW, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_LOW2,
+ BT_COEX_PRIO_TBL_PRIO_LOW, 1),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH1,
+ BT_COEX_PRIO_TBL_PRIO_HIGH, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_PERIODIC_CALIB_HIGH2,
+ BT_COEX_PRIO_TBL_PRIO_HIGH, 1),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_DTIM,
+ BT_COEX_PRIO_TBL_DISABLED, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN52,
+ BT_COEX_PRIO_TBL_PRIO_COEX_OFF, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_SCAN24,
+ BT_COEX_PRIO_TBL_PRIO_COEX_ON, 0),
+ EVENT_PRIO_ANT(BT_COEX_PRIO_TBL_EVT_IDLE,
+ BT_COEX_PRIO_TBL_PRIO_COEX_IDLE, 0),
+ 0, 0, 0, 0, 0, 0,
+};
+
+#undef EVENT_PRIO_ANT
+
+#define BT_ANTENNA_COUPLING_THRESHOLD (30)
+
+static int iwl_send_bt_prio_tbl(struct iwl_mvm *mvm)
+{
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return 0;
+
+ return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_PRIO_TABLE, 0,
+ sizeof(struct iwl_bt_coex_prio_tbl_cmd),
+ &iwl_bt_prio_tbl);
+}
+
+static const __le32 iwl_bt_prio_boost[BT_COEX_BOOST_SIZE] = {
+ cpu_to_le32(0xf0f0f0f0), /* 50% */
+ cpu_to_le32(0xc0c0c0c0), /* 25% */
+ cpu_to_le32(0xfcfcfcfc), /* 75% */
+ cpu_to_le32(0xfefefefe), /* 87.5% */
+};
+
+static const __le32 iwl_single_shared_ant[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+ {
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ },
+ {
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ },
+ {
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x40000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x44000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ },
+};
+
+static const __le32 iwl_combined_lookup[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE] = {
+ {
+ /* Tight */
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaeaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xcc00ff28),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xcc00aaaa),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0x00004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xf0005000),
+ },
+ {
+ /* Loose */
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xcc00ff28),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xcc00aaaa),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xf0005000),
+ },
+ {
+ /* Tx Tx disabled */
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xeeaaaaaa),
+ cpu_to_le32(0xaaaaaaaa),
+ cpu_to_le32(0xcc00ff28),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xcc00aaaa),
+ cpu_to_le32(0x0000aaaa),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xc0004000),
+ cpu_to_le32(0xf0005000),
+ cpu_to_le32(0xf0005000),
+ },
+};
+
+/* 20MHz / 40MHz below / 40Mhz above*/
+static const __le64 iwl_ci_mask[][3] = {
+ /* dummy entry for channel 0 */
+ {cpu_to_le64(0), cpu_to_le64(0), cpu_to_le64(0)},
+ {
+ cpu_to_le64(0x0000001FFFULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x00007FFFFFULL),
+ },
+ {
+ cpu_to_le64(0x000000FFFFULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x0003FFFFFFULL),
+ },
+ {
+ cpu_to_le64(0x000003FFFCULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x000FFFFFFCULL),
+ },
+ {
+ cpu_to_le64(0x00001FFFE0ULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x007FFFFFE0ULL),
+ },
+ {
+ cpu_to_le64(0x00007FFF80ULL),
+ cpu_to_le64(0x00007FFFFFULL),
+ cpu_to_le64(0x01FFFFFF80ULL),
+ },
+ {
+ cpu_to_le64(0x0003FFFC00ULL),
+ cpu_to_le64(0x0003FFFFFFULL),
+ cpu_to_le64(0x0FFFFFFC00ULL),
+ },
+ {
+ cpu_to_le64(0x000FFFF000ULL),
+ cpu_to_le64(0x000FFFFFFCULL),
+ cpu_to_le64(0x3FFFFFF000ULL),
+ },
+ {
+ cpu_to_le64(0x007FFF8000ULL),
+ cpu_to_le64(0x007FFFFFE0ULL),
+ cpu_to_le64(0xFFFFFF8000ULL),
+ },
+ {
+ cpu_to_le64(0x01FFFE0000ULL),
+ cpu_to_le64(0x01FFFFFF80ULL),
+ cpu_to_le64(0xFFFFFE0000ULL),
+ },
+ {
+ cpu_to_le64(0x0FFFF00000ULL),
+ cpu_to_le64(0x0FFFFFFC00ULL),
+ cpu_to_le64(0x0ULL),
+ },
+ {
+ cpu_to_le64(0x3FFFC00000ULL),
+ cpu_to_le64(0x3FFFFFF000ULL),
+ cpu_to_le64(0x0)
+ },
+ {
+ cpu_to_le64(0xFFFE000000ULL),
+ cpu_to_le64(0xFFFFFF8000ULL),
+ cpu_to_le64(0x0)
+ },
+ {
+ cpu_to_le64(0xFFF8000000ULL),
+ cpu_to_le64(0xFFFFFE0000ULL),
+ cpu_to_le64(0x0)
+ },
+ {
+ cpu_to_le64(0xFFC0000000ULL),
+ cpu_to_le64(0x0ULL),
+ cpu_to_le64(0x0ULL)
+ },
+};
+
+static const __le32 iwl_bt_mprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE] = {
+ cpu_to_le32(0x28412201),
+ cpu_to_le32(0x11118451),
+};
+
+struct corunning_block_luts {
+ u8 range;
+ __le32 lut20[BT_COEX_CORUN_LUT_SIZE];
+};
+
+/*
+ * Ranges for the antenna coupling calibration / co-running block LUT:
+ * LUT0: [ 0, 12[
+ * LUT1: [12, 20[
+ * LUT2: [20, 21[
+ * LUT3: [21, 23[
+ * LUT4: [23, 27[
+ * LUT5: [27, 30[
+ * LUT6: [30, 32[
+ * LUT7: [32, 33[
+ * LUT8: [33, - [
+ */
+static const struct corunning_block_luts antenna_coupling_ranges[] = {
+ {
+ .range = 0,
+ .lut20 = {
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 12,
+ .lut20 = {
+ cpu_to_le32(0x00000001), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 20,
+ .lut20 = {
+ cpu_to_le32(0x00000002), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 21,
+ .lut20 = {
+ cpu_to_le32(0x00000003), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 23,
+ .lut20 = {
+ cpu_to_le32(0x00000004), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 27,
+ .lut20 = {
+ cpu_to_le32(0x00000005), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 30,
+ .lut20 = {
+ cpu_to_le32(0x00000006), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 32,
+ .lut20 = {
+ cpu_to_le32(0x00000007), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+ {
+ .range = 33,
+ .lut20 = {
+ cpu_to_le32(0x00000008), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ cpu_to_le32(0x00000000), cpu_to_le32(0x00000000),
+ },
+ },
+};
+
+static enum iwl_bt_coex_lut_type
+iwl_get_coex_type(struct iwl_mvm *mvm, const struct ieee80211_vif *vif)
+{
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum iwl_bt_coex_lut_type ret;
+ u16 phy_ctx_id;
+
+ /*
+ * Checking that we hold mvm->mutex is a good idea, but the rate
+ * control can't acquire the mutex since it runs in Tx path.
+ * So this is racy in that case, but in the worst case, the AMPDU
+ * size limit will be wrong for a short time which is not a big
+ * issue.
+ */
+
+ rcu_read_lock();
+
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+ if (!chanctx_conf ||
+ chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+ rcu_read_unlock();
+ return BT_COEX_INVALID_LUT;
+ }
+
+ ret = BT_COEX_TX_DIS_LUT;
+
+ if (mvm->cfg->bt_shared_single_ant) {
+ rcu_read_unlock();
+ return ret;
+ }
+
+ phy_ctx_id = *((u16 *)chanctx_conf->drv_priv);
+
+ if (mvm->last_bt_ci_cmd_old.primary_ch_phy_id == phy_ctx_id)
+ ret = le32_to_cpu(mvm->last_bt_notif_old.primary_ch_lut);
+ else if (mvm->last_bt_ci_cmd_old.secondary_ch_phy_id == phy_ctx_id)
+ ret = le32_to_cpu(mvm->last_bt_notif_old.secondary_ch_lut);
+ /* else - default = TX TX disallowed */
+
+ rcu_read_unlock();
+
+ return ret;
+}
+
+int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm)
+{
+ struct iwl_bt_coex_cmd_old *bt_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = BT_CONFIG,
+ .len = { sizeof(*bt_cmd), },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ int ret;
+ u32 flags;
+
+ ret = iwl_send_bt_prio_tbl(mvm);
+ if (ret)
+ return ret;
+
+ bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+ if (!bt_cmd)
+ return -ENOMEM;
+ cmd.data[0] = bt_cmd;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS)) {
+ switch (mvm->bt_force_ant_mode) {
+ case BT_FORCE_ANT_AUTO:
+ flags = BT_COEX_AUTO_OLD;
+ break;
+ case BT_FORCE_ANT_BT:
+ flags = BT_COEX_BT_OLD;
+ break;
+ case BT_FORCE_ANT_WIFI:
+ flags = BT_COEX_WIFI_OLD;
+ break;
+ default:
+ WARN_ON(1);
+ flags = 0;
+ }
+
+ bt_cmd->flags = cpu_to_le32(flags);
+ bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE);
+ goto send_cmd;
+ }
+
+ bt_cmd->max_kill = 5;
+ bt_cmd->bt4_antenna_isolation_thr = BT_ANTENNA_COUPLING_THRESHOLD;
+ bt_cmd->bt4_antenna_isolation = iwlwifi_mod_params.ant_coupling;
+ bt_cmd->bt4_tx_tx_delta_freq_thr = 15;
+ bt_cmd->bt4_tx_rx_max_freq0 = 15;
+ bt_cmd->override_primary_lut = BT_COEX_INVALID_LUT;
+ bt_cmd->override_secondary_lut = BT_COEX_INVALID_LUT;
+
+ flags = iwlwifi_mod_params.bt_coex_active ?
+ BT_COEX_NW_OLD : BT_COEX_DISABLE_OLD;
+ bt_cmd->flags = cpu_to_le32(flags);
+
+ bt_cmd->valid_bit_msk = cpu_to_le32(BT_VALID_ENABLE |
+ BT_VALID_BT_PRIO_BOOST |
+ BT_VALID_MAX_KILL |
+ BT_VALID_3W_TMRS |
+ BT_VALID_KILL_ACK |
+ BT_VALID_KILL_CTS |
+ BT_VALID_REDUCED_TX_POWER |
+ BT_VALID_LUT |
+ BT_VALID_WIFI_RX_SW_PRIO_BOOST |
+ BT_VALID_WIFI_TX_SW_PRIO_BOOST |
+ BT_VALID_ANT_ISOLATION |
+ BT_VALID_ANT_ISOLATION_THRS |
+ BT_VALID_TXTX_DELTA_FREQ_THRS |
+ BT_VALID_TXRX_MAX_FREQ_0 |
+ BT_VALID_SYNC_TO_SCO);
+
+ if (IWL_MVM_BT_COEX_SYNC2SCO)
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
+
+ if (IWL_MVM_BT_COEX_CORUNNING) {
+ bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
+ BT_VALID_CORUN_LUT_40);
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
+ }
+
+ if (IWL_MVM_BT_COEX_MPLUT) {
+ bt_cmd->flags |= cpu_to_le32(BT_COEX_MPLUT);
+ bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_MULTI_PRIO_LUT);
+ }
+
+ if (mvm->cfg->bt_shared_single_ant)
+ memcpy(&bt_cmd->decision_lut, iwl_single_shared_ant,
+ sizeof(iwl_single_shared_ant));
+ else
+ memcpy(&bt_cmd->decision_lut, iwl_combined_lookup,
+ sizeof(iwl_combined_lookup));
+
+ /* Take first Co-running block LUT to get started */
+ memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[0].lut20,
+ sizeof(bt_cmd->bt4_corun_lut20));
+ memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[0].lut20,
+ sizeof(bt_cmd->bt4_corun_lut40));
+
+ memcpy(&bt_cmd->bt_prio_boost, iwl_bt_prio_boost,
+ sizeof(iwl_bt_prio_boost));
+ memcpy(&bt_cmd->bt4_multiprio_lut, iwl_bt_mprio_lut,
+ sizeof(iwl_bt_mprio_lut));
+ bt_cmd->kill_ack_msk =
+ cpu_to_le32(iwl_bt_ack_kill_msk[BT_KILL_MSK_DEFAULT]);
+ bt_cmd->kill_cts_msk =
+ cpu_to_le32(iwl_bt_cts_kill_msk[BT_KILL_MSK_DEFAULT]);
+
+send_cmd:
+ memset(&mvm->last_bt_notif_old, 0, sizeof(mvm->last_bt_notif_old));
+ memset(&mvm->last_bt_ci_cmd_old, 0, sizeof(mvm->last_bt_ci_cmd_old));
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+ kfree(bt_cmd);
+ return ret;
+}
+
+static int iwl_mvm_bt_udpate_ctrl_kill_msk(struct iwl_mvm *mvm,
+ bool reduced_tx_power)
+{
+ enum iwl_bt_kill_msk bt_kill_msk;
+ struct iwl_bt_coex_cmd_old *bt_cmd;
+ struct iwl_bt_coex_profile_notif_old *notif = &mvm->last_bt_notif_old;
+ struct iwl_host_cmd cmd = {
+ .id = BT_CONFIG,
+ .data[0] = &bt_cmd,
+ .len = { sizeof(*bt_cmd), },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ int ret = 0;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (reduced_tx_power) {
+ /* Reduced Tx power has precedence on the type of the profile */
+ bt_kill_msk = BT_KILL_MSK_REDUCED_TXPOW;
+ } else {
+ /* Low latency BT profile is active: give higher prio to BT */
+ if (BT_MBOX_MSG(notif, 3, SCO_STATE) ||
+ BT_MBOX_MSG(notif, 3, A2DP_STATE) ||
+ BT_MBOX_MSG(notif, 3, SNIFF_STATE))
+ bt_kill_msk = BT_KILL_MSK_SCO_HID_A2DP;
+ else
+ bt_kill_msk = BT_KILL_MSK_DEFAULT;
+ }
+
+ IWL_DEBUG_COEX(mvm,
+ "Update kill_msk: %d - SCO %sactive A2DP %sactive SNIFF %sactive\n",
+ bt_kill_msk,
+ BT_MBOX_MSG(notif, 3, SCO_STATE) ? "" : "in",
+ BT_MBOX_MSG(notif, 3, A2DP_STATE) ? "" : "in",
+ BT_MBOX_MSG(notif, 3, SNIFF_STATE) ? "" : "in");
+
+ /* Don't send HCMD if there is no update */
+ if (bt_kill_msk == mvm->bt_kill_msk)
+ return 0;
+
+ mvm->bt_kill_msk = bt_kill_msk;
+
+ bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+ if (!bt_cmd)
+ return -ENOMEM;
+ cmd.data[0] = bt_cmd;
+ bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+
+ bt_cmd->kill_ack_msk = cpu_to_le32(iwl_bt_ack_kill_msk[bt_kill_msk]);
+ bt_cmd->kill_cts_msk = cpu_to_le32(iwl_bt_cts_kill_msk[bt_kill_msk]);
+ bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
+ BT_VALID_KILL_ACK |
+ BT_VALID_KILL_CTS);
+
+ IWL_DEBUG_COEX(mvm, "ACK Kill msk = 0x%08x, CTS Kill msk = 0x%08x\n",
+ iwl_bt_ack_kill_msk[bt_kill_msk],
+ iwl_bt_cts_kill_msk[bt_kill_msk]);
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+ kfree(bt_cmd);
+ return ret;
+}
+
+static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id,
+ bool enable)
+{
+ struct iwl_bt_coex_cmd_old *bt_cmd;
+ /* Send ASYNC since this can be sent from an atomic context */
+ struct iwl_host_cmd cmd = {
+ .id = BT_CONFIG,
+ .len = { sizeof(*bt_cmd), },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ .flags = CMD_ASYNC,
+ };
+ struct iwl_mvm_sta *mvmsta;
+ int ret;
+
+ mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id);
+ if (!mvmsta)
+ return 0;
+
+ /* nothing to do */
+ if (mvmsta->bt_reduced_txpower == enable)
+ return 0;
+
+ bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_ATOMIC);
+ if (!bt_cmd)
+ return -ENOMEM;
+ cmd.data[0] = bt_cmd;
+ bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+
+ bt_cmd->valid_bit_msk =
+ cpu_to_le32(BT_VALID_ENABLE | BT_VALID_REDUCED_TX_POWER);
+ bt_cmd->bt_reduced_tx_power = sta_id;
+
+ if (enable)
+ bt_cmd->bt_reduced_tx_power |= BT_REDUCED_TX_POWER_BIT;
+
+ IWL_DEBUG_COEX(mvm, "%sable reduced Tx Power for sta %d\n",
+ enable ? "en" : "dis", sta_id);
+
+ mvmsta->bt_reduced_txpower = enable;
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+ kfree(bt_cmd);
+ return ret;
+}
+
+struct iwl_bt_iterator_data {
+ struct iwl_bt_coex_profile_notif_old *notif;
+ struct iwl_mvm *mvm;
+ u32 num_bss_ifaces;
+ bool reduced_tx_power;
+ struct ieee80211_chanctx_conf *primary;
+ struct ieee80211_chanctx_conf *secondary;
+ bool primary_ll;
+};
+
+static inline
+void iwl_mvm_bt_coex_enable_rssi_event(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool enable, int rssi)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ mvmvif->bf_data.last_bt_coex_event = rssi;
+ mvmvif->bf_data.bt_coex_max_thold =
+ enable ? -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH : 0;
+ mvmvif->bf_data.bt_coex_min_thold =
+ enable ? -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH : 0;
+}
+
+/* must be called under rcu_read_lock */
+static void iwl_mvm_bt_notif_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_bt_iterator_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ enum ieee80211_smps_mode smps_mode;
+ u32 bt_activity_grading;
+ int ave_rssi;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ /* Count BSSes vifs */
+ data->num_bss_ifaces++;
+ /* default smps_mode for BSS / P2P client is AUTOMATIC */
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+ break;
+ case NL80211_IFTYPE_AP:
+ /* default smps_mode for AP / GO is OFF */
+ smps_mode = IEEE80211_SMPS_OFF;
+ if (!mvmvif->ap_ibss_active) {
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+ smps_mode);
+ return;
+ }
+
+ /* the Ack / Cts kill mask must be default if AP / GO */
+ data->reduced_tx_power = false;
+ break;
+ default:
+ return;
+ }
+
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+
+ /* If channel context is invalid or not on 2.4GHz .. */
+ if ((!chanctx_conf ||
+ chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ)) {
+ /* ... relax constraints and disable rssi events */
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX,
+ smps_mode);
+ data->reduced_tx_power = false;
+ if (vif->type == NL80211_IFTYPE_STATION) {
+ iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+ false);
+ iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+ }
+ return;
+ }
+
+ bt_activity_grading = le32_to_cpu(data->notif->bt_activity_grading);
+ if (bt_activity_grading >= BT_HIGH_TRAFFIC)
+ smps_mode = IEEE80211_SMPS_STATIC;
+ else if (bt_activity_grading >= BT_LOW_TRAFFIC)
+ smps_mode = vif->type == NL80211_IFTYPE_AP ?
+ IEEE80211_SMPS_OFF :
+ IEEE80211_SMPS_DYNAMIC;
+
+ /* relax SMPS contraints for next association */
+ if (!vif->bss_conf.assoc)
+ smps_mode = IEEE80211_SMPS_AUTOMATIC;
+
+ IWL_DEBUG_COEX(data->mvm,
+ "mac %d: bt_status %d bt_activity_grading %d smps_req %d\n",
+ mvmvif->id, data->notif->bt_status, bt_activity_grading,
+ smps_mode);
+
+ iwl_mvm_update_smps(mvm, vif, IWL_MVM_SMPS_REQ_BT_COEX, smps_mode);
+
+ /* low latency is always primary */
+ if (iwl_mvm_vif_low_latency(mvmvif)) {
+ data->primary_ll = true;
+
+ data->secondary = data->primary;
+ data->primary = chanctx_conf;
+ }
+
+ if (vif->type == NL80211_IFTYPE_AP) {
+ if (!mvmvif->ap_ibss_active)
+ return;
+
+ if (chanctx_conf == data->primary)
+ return;
+
+ if (!data->primary_ll) {
+ /*
+ * downgrade the current primary no matter what its
+ * type is.
+ */
+ data->secondary = data->primary;
+ data->primary = chanctx_conf;
+ } else {
+ /* there is low latency vif - we will be secondary */
+ data->secondary = chanctx_conf;
+ }
+ return;
+ }
+
+ /*
+ * STA / P2P Client, try to be primary if first vif. If we are in low
+ * latency mode, we are already in primary and just don't do much
+ */
+ if (!data->primary || data->primary == chanctx_conf)
+ data->primary = chanctx_conf;
+ else if (!data->secondary)
+ /* if secondary is not NULL, it might be a GO */
+ data->secondary = chanctx_conf;
+
+ /*
+ * don't reduce the Tx power if one of these is true:
+ * we are in LOOSE
+ * single share antenna product
+ * BT is active
+ * we are associated
+ */
+ if (iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT ||
+ mvm->cfg->bt_shared_single_ant || !vif->bss_conf.assoc ||
+ !data->notif->bt_status) {
+ data->reduced_tx_power = false;
+ iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false);
+ iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, false, 0);
+ return;
+ }
+
+ /* try to get the avg rssi from fw */
+ ave_rssi = mvmvif->bf_data.ave_beacon_signal;
+
+ /* if the RSSI isn't valid, fake it is very low */
+ if (!ave_rssi)
+ ave_rssi = -100;
+ if (ave_rssi > -IWL_MVM_BT_COEX_EN_RED_TXP_THRESH) {
+ if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true))
+ IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+
+ /*
+ * bt_kill_msk can be BT_KILL_MSK_REDUCED_TXPOW only if all the
+ * BSS / P2P clients have rssi above threshold.
+ * We set the bt_kill_msk to BT_KILL_MSK_REDUCED_TXPOW before
+ * the iteration, if one interface's rssi isn't good enough,
+ * bt_kill_msk will be set to default values.
+ */
+ } else if (ave_rssi < -IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH) {
+ if (iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, false))
+ IWL_ERR(mvm, "Couldn't send BT_CONFIG cmd\n");
+
+ /*
+ * One interface hasn't rssi above threshold, bt_kill_msk must
+ * be set to default values.
+ */
+ data->reduced_tx_power = false;
+ }
+
+ /* Begin to monitor the RSSI: it may influence the reduced Tx power */
+ iwl_mvm_bt_coex_enable_rssi_event(mvm, vif, true, ave_rssi);
+}
+
+static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm)
+{
+ struct iwl_bt_iterator_data data = {
+ .mvm = mvm,
+ .notif = &mvm->last_bt_notif_old,
+ .reduced_tx_power = true,
+ };
+ struct iwl_bt_coex_ci_cmd_old cmd = {};
+ u8 ci_bw_idx;
+
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return;
+
+ rcu_read_lock();
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bt_notif_iterator, &data);
+
+ if (data.primary) {
+ struct ieee80211_chanctx_conf *chan = data.primary;
+
+ if (WARN_ON(!chan->def.chan)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+ ci_bw_idx = 0;
+ cmd.co_run_bw_primary = 0;
+ } else {
+ cmd.co_run_bw_primary = 1;
+ if (chan->def.center_freq1 >
+ chan->def.chan->center_freq)
+ ci_bw_idx = 2;
+ else
+ ci_bw_idx = 1;
+ }
+
+ cmd.bt_primary_ci =
+ iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+ cmd.primary_ch_phy_id = *((u16 *)data.primary->drv_priv);
+ }
+
+ if (data.secondary) {
+ struct ieee80211_chanctx_conf *chan = data.secondary;
+
+ if (WARN_ON(!data.secondary->def.chan)) {
+ rcu_read_unlock();
+ return;
+ }
+
+ if (chan->def.width < NL80211_CHAN_WIDTH_40) {
+ ci_bw_idx = 0;
+ cmd.co_run_bw_secondary = 0;
+ } else {
+ cmd.co_run_bw_secondary = 1;
+ if (chan->def.center_freq1 >
+ chan->def.chan->center_freq)
+ ci_bw_idx = 2;
+ else
+ ci_bw_idx = 1;
+ }
+
+ cmd.bt_secondary_ci =
+ iwl_ci_mask[chan->def.chan->hw_value][ci_bw_idx];
+ cmd.secondary_ch_phy_id = *((u16 *)data.secondary->drv_priv);
+ }
+
+ rcu_read_unlock();
+
+ /* Don't spam the fw with the same command over and over */
+ if (memcmp(&cmd, &mvm->last_bt_ci_cmd_old, sizeof(cmd))) {
+ if (iwl_mvm_send_cmd_pdu(mvm, BT_COEX_CI, 0,
+ sizeof(cmd), &cmd))
+ IWL_ERR(mvm, "Failed to send BT_CI cmd\n");
+ memcpy(&mvm->last_bt_ci_cmd_old, &cmd, sizeof(cmd));
+ }
+
+ /*
+ * If there are no BSS / P2P client interfaces, reduced Tx Power is
+ * irrelevant since it is based on the RSSI coming from the beacon.
+ * Use BT_KILL_MSK_DEFAULT in that case.
+ */
+ data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
+
+ if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+ IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
+
+int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *dev_cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ struct iwl_bt_coex_profile_notif_old *notif = (void *)pkt->data;
+
+ IWL_DEBUG_COEX(mvm, "BT Coex Notification received\n");
+ IWL_DEBUG_COEX(mvm, "\tBT status: %s\n",
+ notif->bt_status ? "ON" : "OFF");
+ IWL_DEBUG_COEX(mvm, "\tBT open conn %d\n", notif->bt_open_conn);
+ IWL_DEBUG_COEX(mvm, "\tBT ci compliance %d\n", notif->bt_ci_compliance);
+ IWL_DEBUG_COEX(mvm, "\tBT primary_ch_lut %d\n",
+ le32_to_cpu(notif->primary_ch_lut));
+ IWL_DEBUG_COEX(mvm, "\tBT secondary_ch_lut %d\n",
+ le32_to_cpu(notif->secondary_ch_lut));
+ IWL_DEBUG_COEX(mvm, "\tBT activity grading %d\n",
+ le32_to_cpu(notif->bt_activity_grading));
+ IWL_DEBUG_COEX(mvm, "\tBT agg traffic load %d\n",
+ notif->bt_agg_traffic_load);
+
+ /* remember this notification for future use: rssi fluctuations */
+ memcpy(&mvm->last_bt_notif_old, notif, sizeof(mvm->last_bt_notif_old));
+
+ iwl_mvm_bt_coex_notif_handle(mvm);
+
+ /*
+ * This is an async handler for a notification, returning anything other
+ * than 0 doesn't make sense even if HCMD failed.
+ */
+ return 0;
+}
+
+static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+ struct iwl_bt_iterator_data *data = _data;
+ struct iwl_mvm *mvm = data->mvm;
+
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+
+ struct ieee80211_chanctx_conf *chanctx_conf;
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(vif->chanctx_conf);
+ /* If channel context is invalid or not on 2.4GHz - don't count it */
+ if (!chanctx_conf ||
+ chanctx_conf->def.chan->band != IEEE80211_BAND_2GHZ) {
+ rcu_read_unlock();
+ return;
+ }
+ rcu_read_unlock();
+
+ if (vif->type != NL80211_IFTYPE_STATION ||
+ mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+ return;
+
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[mvmvif->ap_sta_id],
+ lockdep_is_held(&mvm->mutex));
+
+ /* This can happen if the station has been removed right now */
+ if (IS_ERR_OR_NULL(sta))
+ return;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+
+ data->num_bss_ifaces++;
+
+ /*
+ * This interface doesn't support reduced Tx power (because of low
+ * RSSI probably), then set bt_kill_msk to default values.
+ */
+ if (!mvmsta->bt_reduced_txpower)
+ data->reduced_tx_power = false;
+ /* else - possibly leave it to BT_KILL_MSK_REDUCED_TXPOW */
+}
+
+void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum ieee80211_rssi_event rssi_event)
+{
+ struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv;
+ struct iwl_bt_iterator_data data = {
+ .mvm = mvm,
+ .reduced_tx_power = true,
+ };
+ int ret;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return;
+
+ /*
+ * Rssi update while not associated - can happen since the statistics
+ * are handled asynchronously
+ */
+ if (mvmvif->ap_sta_id == IWL_MVM_STATION_COUNT)
+ return;
+
+ /* No BT - reports should be disabled */
+ if (!mvm->last_bt_notif_old.bt_status)
+ return;
+
+ IWL_DEBUG_COEX(mvm, "RSSI for %pM is now %s\n", vif->bss_conf.bssid,
+ rssi_event == RSSI_EVENT_HIGH ? "HIGH" : "LOW");
+
+ /*
+ * Check if rssi is good enough for reduced Tx power, but not in loose
+ * scheme.
+ */
+ if (rssi_event == RSSI_EVENT_LOW || mvm->cfg->bt_shared_single_ant ||
+ iwl_get_coex_type(mvm, vif) == BT_COEX_LOOSE_LUT)
+ ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id,
+ false);
+ else
+ ret = iwl_mvm_bt_coex_reduced_txp(mvm, mvmvif->ap_sta_id, true);
+
+ if (ret)
+ IWL_ERR(mvm, "couldn't send BT_CONFIG HCMD upon RSSI event\n");
+
+ ieee80211_iterate_active_interfaces_atomic(
+ mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_bt_rssi_iterator, &data);
+
+ /*
+ * If there are no BSS / P2P client interfaces, reduced Tx Power is
+ * irrelevant since it is based on the RSSI coming from the beacon.
+ * Use BT_KILL_MSK_DEFAULT in that case.
+ */
+ data.reduced_tx_power = data.reduced_tx_power && data.num_bss_ifaces;
+
+ if (iwl_mvm_bt_udpate_ctrl_kill_msk(mvm, data.reduced_tx_power))
+ IWL_ERR(mvm, "Failed to update the ctrl_kill_msk\n");
+}
+
+#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000)
+#define LINK_QUAL_AGG_TIME_LIMIT_BT_ACT (1200)
+
+u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ enum iwl_bt_coex_lut_type lut_type;
+
+ if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) <
+ BT_HIGH_TRAFFIC)
+ return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+ if (mvm->last_bt_notif_old.ttc_enabled)
+ return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+ lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+
+ if (lut_type == BT_COEX_LOOSE_LUT || lut_type == BT_COEX_INVALID_LUT)
+ return LINK_QUAL_AGG_TIME_LIMIT_DEF;
+
+ /* tight coex, high bt traffic, reduce AGG time limit */
+ return LINK_QUAL_AGG_TIME_LIMIT_BT_ACT;
+}
+
+bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta)
+{
+ struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ enum iwl_bt_coex_lut_type lut_type;
+
+ if (mvm->last_bt_notif_old.ttc_enabled)
+ return true;
+
+ if (le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading) <
+ BT_HIGH_TRAFFIC)
+ return true;
+
+ /*
+ * In Tight / TxTxDis, BT can't Rx while we Tx, so use both antennas
+ * since BT is already killed.
+ * In Loose, BT can Rx while we Tx, so forbid MIMO to let BT Rx while
+ * we Tx.
+ * When we are in 5GHz, we'll get BT_COEX_INVALID_LUT allowing MIMO.
+ */
+ lut_type = iwl_get_coex_type(mvm, mvmsta->vif);
+ return lut_type != BT_COEX_LOOSE_LUT;
+}
+
+bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm)
+{
+ u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+ return ag == BT_OFF;
+}
+
+bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
+ enum ieee80211_band band)
+{
+ u32 bt_activity =
+ le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
+
+ if (band != IEEE80211_BAND_2GHZ)
+ return false;
+
+ return bt_activity >= BT_LOW_TRAFFIC;
+}
+
+void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm)
+{
+ iwl_mvm_bt_coex_notif_handle(mvm);
+}
+
+int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *dev_cmd)
+{
+ struct iwl_rx_packet *pkt = rxb_addr(rxb);
+ u32 ant_isolation = le32_to_cpup((void *)pkt->data);
+ u8 __maybe_unused lower_bound, upper_bound;
+ int ret;
+ u8 lut;
+
+ struct iwl_bt_coex_cmd_old *bt_cmd;
+ struct iwl_host_cmd cmd = {
+ .id = BT_CONFIG,
+ .len = { sizeof(*bt_cmd), },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+
+ if (!IWL_MVM_BT_COEX_CORUNNING)
+ return 0;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Ignore updates if we are in force mode */
+ if (unlikely(mvm->bt_force_ant_mode != BT_FORCE_ANT_DIS))
+ return 0;
+
+ if (ant_isolation == mvm->last_ant_isol)
+ return 0;
+
+ for (lut = 0; lut < ARRAY_SIZE(antenna_coupling_ranges) - 1; lut++)
+ if (ant_isolation < antenna_coupling_ranges[lut + 1].range)
+ break;
+
+ lower_bound = antenna_coupling_ranges[lut].range;
+
+ if (lut < ARRAY_SIZE(antenna_coupling_ranges) - 1)
+ upper_bound = antenna_coupling_ranges[lut + 1].range;
+ else
+ upper_bound = antenna_coupling_ranges[lut].range;
+
+ IWL_DEBUG_COEX(mvm, "Antenna isolation=%d in range [%d,%d[, lut=%d\n",
+ ant_isolation, lower_bound, upper_bound, lut);
+
+ mvm->last_ant_isol = ant_isolation;
+
+ if (mvm->last_corun_lut == lut)
+ return 0;
+
+ mvm->last_corun_lut = lut;
+
+ bt_cmd = kzalloc(sizeof(*bt_cmd), GFP_KERNEL);
+ if (!bt_cmd)
+ return 0;
+ cmd.data[0] = bt_cmd;
+
+ bt_cmd->flags = cpu_to_le32(BT_COEX_NW_OLD);
+ bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_ENABLE |
+ BT_VALID_CORUN_LUT_20 |
+ BT_VALID_CORUN_LUT_40);
+
+ /* For the moment, use the same LUT for 20GHz and 40GHz */
+ memcpy(bt_cmd->bt4_corun_lut20, antenna_coupling_ranges[lut].lut20,
+ sizeof(bt_cmd->bt4_corun_lut20));
+
+ memcpy(bt_cmd->bt4_corun_lut40, antenna_coupling_ranges[lut].lut20,
+ sizeof(bt_cmd->bt4_corun_lut40));
+
+ ret = iwl_mvm_send_cmd(mvm, &cmd);
+
+ kfree(bt_cmd);
+ return ret;
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h
index 51685693af2e..ca79f7160573 100644
--- a/drivers/net/wireless/iwlwifi/mvm/constants.h
+++ b/drivers/net/wireless/iwlwifi/mvm/constants.h
@@ -79,6 +79,8 @@
#define IWL_MVM_PS_SNOOZE_WINDOW 50
#define IWL_MVM_WOWLAN_PS_SNOOZE_WINDOW 25
#define IWL_MVM_LOWLAT_QUOTA_MIN_PERCENT 64
+#define IWL_MVM_BT_COEX_EN_RED_TXP_THRESH 62
+#define IWL_MVM_BT_COEX_DIS_RED_TXP_THRESH 65
#define IWL_MVM_BT_COEX_SYNC2SCO 1
#define IWL_MVM_BT_COEX_CORUNNING 1
#define IWL_MVM_BT_COEX_MPLUT 1
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
index 29ca72695eaa..f131ef0ec5b3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -312,20 +312,69 @@ static ssize_t iwl_dbgfs_disable_power_off_write(struct iwl_mvm *mvm, char *buf,
BT_MBOX_MSG(notif, _num, _field), \
true ? "\n" : ", ");
-static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
+static
+int iwl_mvm_coex_dump_mbox(struct iwl_bt_coex_profile_notif *notif, char *buf,
+ int pos, int bufsz)
{
- struct iwl_mvm *mvm = file->private_data;
- struct iwl_bt_coex_profile_notif *notif = &mvm->last_bt_notif;
- char *buf;
- int ret, pos = 0, bufsz = sizeof(char) * 1024;
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
- buf = kmalloc(bufsz, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
+ BT_MBOX_PRINT(0, LE_PROF1, false);
+ BT_MBOX_PRINT(0, LE_PROF2, false);
+ BT_MBOX_PRINT(0, LE_PROF_OTHER, false);
+ BT_MBOX_PRINT(0, CHL_SEQ_N, false);
+ BT_MBOX_PRINT(0, INBAND_S, false);
+ BT_MBOX_PRINT(0, LE_MIN_RSSI, false);
+ BT_MBOX_PRINT(0, LE_SCAN, false);
+ BT_MBOX_PRINT(0, LE_ADV, false);
+ BT_MBOX_PRINT(0, LE_MAX_TX_POWER, false);
+ BT_MBOX_PRINT(0, OPEN_CON_1, true);
- mutex_lock(&mvm->mutex);
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw1:\n");
+
+ BT_MBOX_PRINT(1, BR_MAX_TX_POWER, false);
+ BT_MBOX_PRINT(1, IP_SR, false);
+ BT_MBOX_PRINT(1, LE_MSTR, false);
+ BT_MBOX_PRINT(1, AGGR_TRFC_LD, false);
+ BT_MBOX_PRINT(1, MSG_TYPE, false);
+ BT_MBOX_PRINT(1, SSN, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw2:\n");
+
+ BT_MBOX_PRINT(2, SNIFF_ACT, false);
+ BT_MBOX_PRINT(2, PAG, false);
+ BT_MBOX_PRINT(2, INQUIRY, false);
+ BT_MBOX_PRINT(2, CONN, false);
+ BT_MBOX_PRINT(2, SNIFF_INTERVAL, false);
+ BT_MBOX_PRINT(2, DISC, false);
+ BT_MBOX_PRINT(2, SCO_TX_ACT, false);
+ BT_MBOX_PRINT(2, SCO_RX_ACT, false);
+ BT_MBOX_PRINT(2, ESCO_RE_TX, false);
+ BT_MBOX_PRINT(2, SCO_DURATION, true);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw3:\n");
+
+ BT_MBOX_PRINT(3, SCO_STATE, false);
+ BT_MBOX_PRINT(3, SNIFF_STATE, false);
+ BT_MBOX_PRINT(3, A2DP_STATE, false);
+ BT_MBOX_PRINT(3, ACL_STATE, false);
+ BT_MBOX_PRINT(3, MSTR_STATE, false);
+ BT_MBOX_PRINT(3, OBX_STATE, false);
+ BT_MBOX_PRINT(3, OPEN_CON_2, false);
+ BT_MBOX_PRINT(3, TRAFFIC_LOAD, false);
+ BT_MBOX_PRINT(3, CHL_SEQN_LSB, false);
+ BT_MBOX_PRINT(3, INBAND_P, false);
+ BT_MBOX_PRINT(3, MSG_TYPE_2, false);
+ BT_MBOX_PRINT(3, SSN_2, false);
+ BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
+
+ return pos;
+}
+static
+int iwl_mvm_coex_dump_mbox_old(struct iwl_bt_coex_profile_notif_old *notif,
+ char *buf, int pos, int bufsz)
+{
pos += scnprintf(buf+pos, bufsz-pos, "MBOX dw0:\n");
BT_MBOX_PRINT(0, LE_SLAVE_LAT, false);
@@ -378,25 +427,59 @@ static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
BT_MBOX_PRINT(3, SSN_2, false);
BT_MBOX_PRINT(3, UPDATE_REQUEST, true);
- pos += scnprintf(buf+pos, bufsz-pos, "bt_status = %d\n",
- notif->bt_status);
- pos += scnprintf(buf+pos, bufsz-pos, "bt_open_conn = %d\n",
- notif->bt_open_conn);
- pos += scnprintf(buf+pos, bufsz-pos, "bt_traffic_load = %d\n",
- notif->bt_traffic_load);
- pos += scnprintf(buf+pos, bufsz-pos, "bt_agg_traffic_load = %d\n",
- notif->bt_agg_traffic_load);
- pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
- notif->bt_ci_compliance);
- pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
- le32_to_cpu(notif->primary_ch_lut));
- pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
- le32_to_cpu(notif->secondary_ch_lut));
- pos += scnprintf(buf+pos, bufsz-pos, "bt_activity_grading = %d\n",
- le32_to_cpu(notif->bt_activity_grading));
- pos += scnprintf(buf+pos, bufsz-pos,
- "antenna isolation = %d CORUN LUT index = %d\n",
- mvm->last_ant_isol, mvm->last_corun_lut);
+ return pos;
+}
+
+static ssize_t iwl_dbgfs_bt_notif_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct iwl_mvm *mvm = file->private_data;
+ char *buf;
+ int ret, pos = 0, bufsz = sizeof(char) * 1024;
+
+ buf = kmalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ mutex_lock(&mvm->mutex);
+
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+ struct iwl_bt_coex_profile_notif_old *notif =
+ &mvm->last_bt_notif_old;
+
+ pos += iwl_mvm_coex_dump_mbox_old(notif, buf, pos, bufsz);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
+ notif->bt_ci_compliance);
+ pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
+ le32_to_cpu(notif->primary_ch_lut));
+ pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
+ le32_to_cpu(notif->secondary_ch_lut));
+ pos += scnprintf(buf+pos,
+ bufsz-pos, "bt_activity_grading = %d\n",
+ le32_to_cpu(notif->bt_activity_grading));
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "antenna isolation = %d CORUN LUT index = %d\n",
+ mvm->last_ant_isol, mvm->last_corun_lut);
+ } else {
+ struct iwl_bt_coex_profile_notif *notif =
+ &mvm->last_bt_notif;
+
+ pos += iwl_mvm_coex_dump_mbox(notif, buf, pos, bufsz);
+
+ pos += scnprintf(buf+pos, bufsz-pos, "bt_ci_compliance = %d\n",
+ notif->bt_ci_compliance);
+ pos += scnprintf(buf+pos, bufsz-pos, "primary_ch_lut = %d\n",
+ le32_to_cpu(notif->primary_ch_lut));
+ pos += scnprintf(buf+pos, bufsz-pos, "secondary_ch_lut = %d\n",
+ le32_to_cpu(notif->secondary_ch_lut));
+ pos += scnprintf(buf+pos,
+ bufsz-pos, "bt_activity_grading = %d\n",
+ le32_to_cpu(notif->bt_activity_grading));
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "antenna isolation = %d CORUN LUT index = %d\n",
+ mvm->last_ant_isol, mvm->last_corun_lut);
+ }
mutex_unlock(&mvm->mutex);
@@ -411,28 +494,48 @@ static ssize_t iwl_dbgfs_bt_cmd_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm *mvm = file->private_data;
- struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
char buf[256];
int bufsz = sizeof(buf);
int pos = 0;
mutex_lock(&mvm->mutex);
- pos += scnprintf(buf+pos, bufsz-pos, "Channel inhibition CMD\n");
- pos += scnprintf(buf+pos, bufsz-pos,
- "\tPrimary Channel Bitmap 0x%016llx Fat: %d\n",
- le64_to_cpu(cmd->bt_primary_ci),
- !!cmd->co_run_bw_primary);
- pos += scnprintf(buf+pos, bufsz-pos,
- "\tSecondary Channel Bitmap 0x%016llx Fat: %d\n",
- le64_to_cpu(cmd->bt_secondary_ci),
- !!cmd->co_run_bw_secondary);
-
- pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
- pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
- iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
- pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
- iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) {
+ struct iwl_bt_coex_ci_cmd_old *cmd = &mvm->last_bt_ci_cmd_old;
+
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "Channel inhibition CMD\n");
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "\tPrimary Channel Bitmap 0x%016llx\n",
+ le64_to_cpu(cmd->bt_primary_ci));
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "\tSecondary Channel Bitmap 0x%016llx\n",
+ le64_to_cpu(cmd->bt_secondary_ci));
+
+ pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
+ pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
+ iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
+ pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
+ iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
+
+ } else {
+ struct iwl_bt_coex_ci_cmd *cmd = &mvm->last_bt_ci_cmd;
+
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "Channel inhibition CMD\n");
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "\tPrimary Channel Bitmap 0x%016llx\n",
+ le64_to_cpu(cmd->bt_primary_ci));
+ pos += scnprintf(buf+pos, bufsz-pos,
+ "\tSecondary Channel Bitmap 0x%016llx\n",
+ le64_to_cpu(cmd->bt_secondary_ci));
+
+ pos += scnprintf(buf+pos, bufsz-pos, "BT Configuration CMD\n");
+ pos += scnprintf(buf+pos, bufsz-pos, "\tACK Kill Mask 0x%08x\n",
+ iwl_bt_ack_kill_msk[mvm->bt_kill_msk]);
+ pos += scnprintf(buf+pos, bufsz-pos, "\tCTS Kill Mask 0x%08x\n",
+ iwl_bt_cts_kill_msk[mvm->bt_kill_msk]);
+ }
mutex_unlock(&mvm->mutex);
@@ -455,6 +558,43 @@ iwl_dbgfs_bt_tx_prio_write(struct iwl_mvm *mvm, char *buf,
return count;
}
+static ssize_t
+iwl_dbgfs_bt_force_ant_write(struct iwl_mvm *mvm, char *buf,
+ size_t count, loff_t *ppos)
+{
+ static const char * const modes_str[BT_FORCE_ANT_MAX] = {
+ [BT_FORCE_ANT_DIS] = "dis",
+ [BT_FORCE_ANT_AUTO] = "auto",
+ [BT_FORCE_ANT_BT] = "bt",
+ [BT_FORCE_ANT_WIFI] = "wifi",
+ };
+ int ret, bt_force_ant_mode;
+
+ for (bt_force_ant_mode = 0;
+ bt_force_ant_mode < ARRAY_SIZE(modes_str);
+ bt_force_ant_mode++) {
+ if (!strcmp(buf, modes_str[bt_force_ant_mode]))
+ break;
+ }
+
+ if (bt_force_ant_mode >= ARRAY_SIZE(modes_str))
+ return -EINVAL;
+
+ ret = 0;
+ mutex_lock(&mvm->mutex);
+ if (mvm->bt_force_ant_mode == bt_force_ant_mode)
+ goto out;
+
+ mvm->bt_force_ant_mode = bt_force_ant_mode;
+ IWL_DEBUG_COEX(mvm, "Force mode: %s\n",
+ modes_str[mvm->bt_force_ant_mode]);
+ ret = iwl_send_bt_init_conf(mvm);
+
+out:
+ mutex_unlock(&mvm->mutex);
+ return ret ?: count;
+}
+
#define PRINT_STATS_LE32(_str, _val) \
pos += scnprintf(buf + pos, bufsz - pos, \
fmt_table, _str, \
@@ -1101,6 +1241,7 @@ MVM_DEBUGFS_READ_FILE_OPS(drv_rx_stats);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_restart, 10);
MVM_DEBUGFS_WRITE_FILE_OPS(fw_nmi, 10);
MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10);
+MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8);
@@ -1142,6 +1283,7 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir)
MVM_DEBUGFS_ADD_FILE(fw_restart, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(fw_nmi, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(bt_tx_prio, mvm->debugfs_dir, S_IWUSR);
+ MVM_DEBUGFS_ADD_FILE(bt_force_ant, mvm->debugfs_dir, S_IWUSR);
MVM_DEBUGFS_ADD_FILE(scan_ant_rxchain, mvm->debugfs_dir,
S_IWUSR | S_IRUSR);
MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR);
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
index 5fe82c29c8ad..ab12aaa43034 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-coex.h
@@ -72,10 +72,13 @@
* enum iwl_bt_coex_flags - flags for BT_COEX command
* @BT_COEX_MODE_POS:
* @BT_COEX_MODE_MSK:
- * @BT_COEX_DISABLE:
- * @BT_COEX_2W:
- * @BT_COEX_3W:
- * @BT_COEX_NW:
+ * @BT_COEX_DISABLE_OLD:
+ * @BT_COEX_2W_OLD:
+ * @BT_COEX_3W_OLD:
+ * @BT_COEX_NW_OLD:
+ * @BT_COEX_AUTO_OLD:
+ * @BT_COEX_BT_OLD: Antenna is for BT (manufacuring tests)
+ * @BT_COEX_WIFI_OLD: Antenna is for BT (manufacuring tests)
* @BT_COEX_SYNC2SCO:
* @BT_COEX_CORUNNING:
* @BT_COEX_MPLUT:
@@ -85,10 +88,13 @@
enum iwl_bt_coex_flags {
BT_COEX_MODE_POS = 3,
BT_COEX_MODE_MSK = BITS(3) << BT_COEX_MODE_POS,
- BT_COEX_DISABLE = 0x0 << BT_COEX_MODE_POS,
- BT_COEX_2W = 0x1 << BT_COEX_MODE_POS,
- BT_COEX_3W = 0x2 << BT_COEX_MODE_POS,
- BT_COEX_NW = 0x3 << BT_COEX_MODE_POS,
+ BT_COEX_DISABLE_OLD = 0x0 << BT_COEX_MODE_POS,
+ BT_COEX_2W_OLD = 0x1 << BT_COEX_MODE_POS,
+ BT_COEX_3W_OLD = 0x2 << BT_COEX_MODE_POS,
+ BT_COEX_NW_OLD = 0x3 << BT_COEX_MODE_POS,
+ BT_COEX_AUTO_OLD = 0x5 << BT_COEX_MODE_POS,
+ BT_COEX_BT_OLD = 0x6 << BT_COEX_MODE_POS,
+ BT_COEX_WIFI_OLD = 0x7 << BT_COEX_MODE_POS,
BT_COEX_SYNC2SCO = BIT(7),
BT_COEX_CORUNNING = BIT(8),
BT_COEX_MPLUT = BIT(9),
@@ -151,7 +157,7 @@ enum iwl_bt_coex_lut_type {
#define BT_REDUCED_TX_POWER_BIT BIT(7)
/**
- * struct iwl_bt_coex_cmd - bt coex configuration command
+ * struct iwl_bt_coex_cmd_old - bt coex configuration command
* @flags:&enum iwl_bt_coex_flags
* @max_kill:
* @bt_reduced_tx_power: enum %iwl_bt_reduced_tx_power
@@ -176,7 +182,7 @@ enum iwl_bt_coex_lut_type {
*
* The structure is used for the BT_COEX command.
*/
-struct iwl_bt_coex_cmd {
+struct iwl_bt_coex_cmd_old {
__le32 flags;
u8 max_kill;
u8 bt_reduced_tx_power;
@@ -202,26 +208,117 @@ struct iwl_bt_coex_cmd {
__le32 valid_bit_msk;
} __packed; /* BT_COEX_CMD_API_S_VER_5 */
+enum iwl_bt_coex_mode {
+ BT_COEX_DISABLE = 0x0,
+ BT_COEX_NW = 0x1,
+ BT_COEX_BT = 0x2,
+ BT_COEX_WIFI = 0x3,
+}; /* BT_COEX_MODES_E */
+
+enum iwl_bt_coex_enabled_modules {
+ BT_COEX_MPLUT_ENABLED = BIT(0),
+ BT_COEX_MPLUT_BOOST_ENABLED = BIT(1),
+ BT_COEX_SYNC2SCO_ENABLED = BIT(2),
+ BT_COEX_CORUN_ENABLED = BIT(3),
+ BT_COEX_HIGH_BAND_RET = BIT(4),
+}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */
+
+/**
+ * struct iwl_bt_coex_cmd - bt coex configuration command
+ * @mode: enum %iwl_bt_coex_mode
+ * @enabled_modules: enum %iwl_bt_coex_enabled_modules
+ * @max_kill: max count of Tx retries due to kill from PTA
+ * @override_primary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ * should be set by default
+ * @override_secondary_lut: enum %iwl_bt_coex_lut_type: BT_COEX_INVALID_LUT
+ * should be set by default
+ * @bt4_antenna_isolation_thr: antenna threshold value
+ * @bt4_tx_tx_delta_freq_thr: TxTx delta frequency
+ * @bt4_tx_rx_max_freq0: TxRx max frequency
+ * @multiprio_lut: multi priority LUT configuration
+ * @mplut_prio_boost: BT priority boost registers
+ * @decision_lut: PTA decision LUT, per Prio-Ch
+ *
+ * The structure is used for the BT_COEX command.
+ */
+struct iwl_bt_coex_cmd {
+ __le32 mode;
+ __le32 enabled_modules;
+
+ __le32 max_kill;
+ __le32 override_primary_lut;
+ __le32 override_secondary_lut;
+ __le32 bt4_antenna_isolation_thr;
+
+ __le32 bt4_tx_tx_delta_freq_thr;
+ __le32 bt4_tx_rx_max_freq0;
+
+ __le32 multiprio_lut[BT_COEX_MULTI_PRIO_LUT_SIZE];
+ __le32 mplut_prio_boost[BT_COEX_BOOST_SIZE];
+
+ __le32 decision_lut[BT_COEX_MAX_LUT][BT_COEX_LUT_SIZE];
+} __packed; /* BT_COEX_CMD_API_S_VER_6 */
+
+/**
+ * struct iwl_bt_coex_corun_lut_update - bt coex update the corun lut
+ * @corun_lut20: co-running 20 MHz LUT configuration
+ * @corun_lut40: co-running 40 MHz LUT configuration
+ *
+ * The structure is used for the BT_COEX_UPDATE_CORUN_LUT command.
+ */
+struct iwl_bt_coex_corun_lut_update_cmd {
+ __le32 corun_lut20[BT_COEX_CORUN_LUT_SIZE];
+ __le32 corun_lut40[BT_COEX_CORUN_LUT_SIZE];
+} __packed; /* BT_COEX_UPDATE_CORUN_LUT_API_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_sw_boost - SW boost values
+ * @wifi_tx_prio_boost: SW boost of wifi tx priority
+ * @wifi_rx_prio_boost: SW boost of wifi rx priority
+ * @kill_ack_msk: kill ACK mask. 1 - Tx ACK, 0 - kill Tx of ACK.
+ * @kill_cts_msk: kill CTS mask. 1 - Tx CTS, 0 - kill Tx of CTS.
+ */
+struct iwl_bt_coex_sw_boost {
+ __le32 wifi_tx_prio_boost;
+ __le32 wifi_rx_prio_boost;
+ __le32 kill_ack_msk;
+ __le32 kill_cts_msk;
+};
+
+/**
+ * struct iwl_bt_coex_sw_boost_update_cmd - command to update the SW boost
+ * @boost_values: check struct %iwl_bt_coex_sw_boost - one for each channel
+ * primary / secondary / low priority
+ */
+struct iwl_bt_coex_sw_boost_update_cmd {
+ struct iwl_bt_coex_sw_boost boost_values[3];
+} __packed; /* BT_COEX_UPDATE_SW_BOOST_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_reduced_txp_update_cmd
+ * @reduced_txp: bit BT_REDUCED_TX_POWER_BIT to enable / disable, rest of the
+ * bits are the sta_id (value)
+ */
+struct iwl_bt_coex_reduced_txp_update_cmd {
+ __le32 reduced_txp;
+} __packed; /* BT_COEX_UPDATE_REDUCED_TX_POWER_API_S_VER_1 */
+
/**
* struct iwl_bt_coex_ci_cmd - bt coex channel inhibition command
* @bt_primary_ci:
- * @bt_secondary_ci:
- * @co_run_bw_primary:
- * @co_run_bw_secondary:
* @primary_ch_phy_id:
+ * @bt_secondary_ci:
* @secondary_ch_phy_id:
*
* Used for BT_COEX_CI command
*/
struct iwl_bt_coex_ci_cmd {
__le64 bt_primary_ci;
- __le64 bt_secondary_ci;
+ __le32 primary_ch_phy_id;
- u8 co_run_bw_primary;
- u8 co_run_bw_secondary;
- u8 primary_ch_phy_id;
- u8 secondary_ch_phy_id;
-} __packed; /* BT_CI_MSG_API_S_VER_1 */
+ __le64 bt_secondary_ci;
+ __le32 secondary_ch_phy_id;
+} __packed; /* BT_CI_MSG_API_S_VER_2 */
#define BT_MBOX(n_dw, _msg, _pos, _nbits) \
BT_MBOX##n_dw##_##_msg##_POS = (_pos), \
@@ -290,33 +387,40 @@ enum iwl_bt_activity_grading {
BT_HIGH_TRAFFIC = 3,
}; /* BT_COEX_BT_ACTIVITY_GRADING_API_E_VER_1 */
+enum iwl_bt_ci_compliance {
+ BT_CI_COMPLIANCE_NONE = 0,
+ BT_CI_COMPLIANCE_PRIMARY = 1,
+ BT_CI_COMPLIANCE_SECONDARY = 2,
+ BT_CI_COMPLIANCE_BOTH = 3,
+}; /* BT_COEX_CI_COMPLIENCE_E_VER_1 */
+
+#define IWL_COEX_IS_TTC_ON(_ttc_rrc_status, _phy_id) \
+ (_ttc_rrc_status & BIT(_phy_id))
+
+#define IWL_COEX_IS_RRC_ON(_ttc_rrc_status, _phy_id) \
+ ((_ttc_rrc_status >> 4) & BIT(_phy_id))
+
/**
* struct iwl_bt_coex_profile_notif - notification about BT coex
* @mbox_msg: message from BT to WiFi
* @msg_idx: the index of the message
- * @bt_status: 0 - off, 1 - on
- * @bt_open_conn: number of BT connections open
- * @bt_traffic_load: load of BT traffic
- * @bt_agg_traffic_load: aggregated load of BT traffic
- * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
- * @primary_ch_lut: LUT used for primary channel
- * @secondary_ch_lut: LUT used for secondary channel
+ * @bt_ci_compliance: enum %iwl_bt_ci_compliance
+ * @primary_ch_lut: LUT used for primary channel enum %iwl_bt_coex_lut_type
+ * @secondary_ch_lut: LUT used for secondary channel enume %iwl_bt_coex_lut_type
* @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
+ * @ttc_rrc_status: is TTC or RRC enabled - one bit per PHY
*/
struct iwl_bt_coex_profile_notif {
__le32 mbox_msg[4];
__le32 msg_idx;
- u8 bt_status;
- u8 bt_open_conn;
- u8 bt_traffic_load;
- u8 bt_agg_traffic_load;
- u8 bt_ci_compliance;
- u8 reserved[3];
+ __le32 bt_ci_compliance;
__le32 primary_ch_lut;
__le32 secondary_ch_lut;
__le32 bt_activity_grading;
-} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
+ u8 ttc_rrc_status;
+ u8 reserved[3];
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_4 */
enum iwl_bt_coex_prio_table_event {
BT_COEX_PRIO_TBL_EVT_INIT_CALIB1 = 0,
@@ -355,4 +459,54 @@ struct iwl_bt_coex_prio_tbl_cmd {
u8 prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX];
} __packed;
+/**
+ * struct iwl_bt_coex_ci_cmd_old - bt coex channel inhibition command
+ * @bt_primary_ci:
+ * @bt_secondary_ci:
+ * @co_run_bw_primary:
+ * @co_run_bw_secondary:
+ * @primary_ch_phy_id:
+ * @secondary_ch_phy_id:
+ *
+ * Used for BT_COEX_CI command
+ */
+struct iwl_bt_coex_ci_cmd_old {
+ __le64 bt_primary_ci;
+ __le64 bt_secondary_ci;
+
+ u8 co_run_bw_primary;
+ u8 co_run_bw_secondary;
+ u8 primary_ch_phy_id;
+ u8 secondary_ch_phy_id;
+} __packed; /* BT_CI_MSG_API_S_VER_1 */
+
+/**
+ * struct iwl_bt_coex_profile_notif_old - notification about BT coex
+ * @mbox_msg: message from BT to WiFi
+ * @msg_idx: the index of the message
+ * @bt_status: 0 - off, 1 - on
+ * @bt_open_conn: number of BT connections open
+ * @bt_traffic_load: load of BT traffic
+ * @bt_agg_traffic_load: aggregated load of BT traffic
+ * @bt_ci_compliance: 0 - no CI compliance, 1 - CI compliant
+ * @primary_ch_lut: LUT used for primary channel
+ * @secondary_ch_lut: LUT used for secondary channel
+ * @bt_activity_grading: the activity of BT enum %iwl_bt_activity_grading
+ */
+struct iwl_bt_coex_profile_notif_old {
+ __le32 mbox_msg[4];
+ __le32 msg_idx;
+ u8 bt_status;
+ u8 bt_open_conn;
+ u8 bt_traffic_load;
+ u8 bt_agg_traffic_load;
+ u8 bt_ci_compliance;
+ u8 ttc_enabled;
+ __le16 reserved;
+
+ __le32 primary_ch_lut;
+ __le32 secondary_ch_lut;
+ __le32 bt_activity_grading;
+} __packed; /* BT_COEX_PROFILE_NTFY_API_S_VER_3 */
+
#endif /* __fw_api_bt_coex_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
index cbbcd8e284e4..c3a8c86b550d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h
@@ -336,7 +336,7 @@ struct iwl_beacon_filter_cmd {
#define IWL_BF_DEBUG_FLAG_D0I3 0
#define IWL_BF_ESCAPE_TIMER_DEFAULT 50
-#define IWL_BF_ESCAPE_TIMER_D0I3 1024
+#define IWL_BF_ESCAPE_TIMER_D0I3 0
#define IWL_BF_ESCAPE_TIMER_MAX 1024
#define IWL_BF_ESCAPE_TIMER_MIN 0
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
index 6959fda3fe09..c02a9e45ec5e 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h
@@ -170,18 +170,12 @@ enum iwl_scan_type {
}; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
/**
- * Maximal number of channels to scan
- * it should be equal to:
- * max(IWL_NUM_CHANNELS, IWL_NUM_CHANNELS_FAMILY_8000)
- */
-#define MAX_NUM_SCAN_CHANNELS 50
-
-/**
* struct iwl_scan_cmd - scan request command
* ( SCAN_REQUEST_CMD = 0x80 )
* @len: command length in bytes
* @scan_flags: scan flags from SCAN_FLAGS_*
- * @channel_count: num of channels in channel list (1 - MAX_NUM_SCAN_CHANNELS)
+ * @channel_count: num of channels in channel list
+ * (1 - ucode_capa.n_scan_channels)
* @quiet_time: in msecs, dwell this time for active scan on quiet channels
* @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
* this number of packets were received (typically 1)
@@ -345,7 +339,7 @@ struct iwl_scan_results_notif {
* @last_channel: last channel that was scanned
* @tsf_low: TSF timer (lower half) in usecs
* @tsf_high: TSF timer (higher half) in usecs
- * @results: all scan results, only "scanned_channels" of them are valid
+ * @results: array of scan results, only "scanned_channels" of them are valid
*/
struct iwl_scan_complete_notif {
u8 scanned_channels;
@@ -354,11 +348,10 @@ struct iwl_scan_complete_notif {
u8 last_channel;
__le32 tsf_low;
__le32 tsf_high;
- struct iwl_scan_results_notif results[MAX_NUM_SCAN_CHANNELS];
+ struct iwl_scan_results_notif results[];
} __packed; /* SCAN_COMPLETE_NTF_API_S_VER_2 */
/* scan offload */
-#define IWL_MAX_SCAN_CHANNELS 40
#define IWL_SCAN_MAX_BLACKLIST_LEN 64
#define IWL_SCAN_SHORT_BLACKLIST_LEN 16
#define IWL_SCAN_MAX_PROFILES 11
@@ -423,36 +416,24 @@ enum iwl_scan_offload_channel_flags {
IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL = BIT(25),
};
-/**
- * iwl_scan_channel_cfg - SCAN_CHANNEL_CFG_S
- * @type: bitmap - see enum iwl_scan_offload_channel_flags.
- * 0: passive (0) or active (1) scan.
- * 1-20: directed scan to i'th ssid.
- * 22: channel width configuation - 1 for narrow.
- * 24: full scan.
- * 25: partial scan.
- * @channel_number: channel number 1-13 etc.
- * @iter_count: repetition count for the channel.
- * @iter_interval: interval between two innteration on one channel.
- * @dwell_time: entry 0 - active scan, entry 1 - passive scan.
+/* channel configuration for struct iwl_scan_offload_cfg. Each channels needs:
+ * __le32 type: bitmap; bits 1-20 are for directed scan to i'th ssid and
+ * see enum iwl_scan_offload_channel_flags.
+ * __le16 channel_number: channel number 1-13 etc.
+ * __le16 iter_count: repetition count for the channel.
+ * __le32 iter_interval: interval between two innteration on one channel.
+ * u8 active_dwell.
+ * u8 passive_dwell.
*/
-struct iwl_scan_channel_cfg {
- __le32 type[IWL_MAX_SCAN_CHANNELS];
- __le16 channel_number[IWL_MAX_SCAN_CHANNELS];
- __le16 iter_count[IWL_MAX_SCAN_CHANNELS];
- __le32 iter_interval[IWL_MAX_SCAN_CHANNELS];
- u8 dwell_time[IWL_MAX_SCAN_CHANNELS][2];
-} __packed;
+#define IWL_SCAN_CHAN_SIZE 14
/**
* iwl_scan_offload_cfg - SCAN_OFFLOAD_CONFIG_API_S
* @scan_cmd: scan command fixed part
- * @channel_cfg: scan channel configuration
- * @data: probe request frames (one per band)
+ * @data: scan channel configuration and probe request frames
*/
struct iwl_scan_offload_cfg {
struct iwl_scan_offload_cmd scan_cmd;
- struct iwl_scan_channel_cfg channel_cfg;
u8 data[0];
} __packed;
@@ -528,7 +509,7 @@ struct iwl_scan_offload_profile_cfg {
* @full_scan_mul: number of partial scans before each full scan
*/
struct iwl_scan_offload_schedule {
- u16 delay;
+ __le16 delay;
u8 iterations;
u8 full_scan_mul;
} __packed;
@@ -601,4 +582,211 @@ struct iwl_sched_scan_results {
u8 reserved;
};
+/* Unified LMAC scan API */
+
+#define IWL_MVM_BASIC_PASSIVE_DWELL 110
+
+/**
+ * iwl_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S
+ * @tx_flags: combination of TX_CMD_FLG_*
+ * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
+ * cleared. Combination of RATE_MCS_*
+ * @sta_id: index of destination station in FW station table
+ * @reserved: for alignment and future use
+ */
+struct iwl_scan_req_tx_cmd {
+ __le32 tx_flags;
+ __le32 rate_n_flags;
+ u8 sta_id;
+ u8 reserved[3];
+} __packed;
+
+enum iwl_scan_channel_flags_lmac {
+ IWL_UNIFIED_SCAN_CHANNEL_FULL = BIT(27),
+ IWL_UNIFIED_SCAN_CHANNEL_PARTIAL = BIT(28),
+};
+
+/**
+ * iwl_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2
+ * @flags: bits 1-20: directed scan to i'th ssid
+ * other bits &enum iwl_scan_channel_flags_lmac
+ * @channel_number: channel number 1-13 etc
+ * @iter_count: scan iteration on this channel
+ * @iter_interval: interval in seconds between iterations on one channel
+ */
+struct iwl_scan_channel_cfg_lmac {
+ __le32 flags;
+ __le16 channel_num;
+ __le16 iter_count;
+ __le32 iter_interval;
+} __packed;
+
+/*
+ * iwl_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1
+ * @offset: offset in the data block
+ * @len: length of the segment
+ */
+struct iwl_scan_probe_segment {
+ __le16 offset;
+ __le16 len;
+} __packed;
+
+/* iwl_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2
+ * @mac_header: first (and common) part of the probe
+ * @band_data: band specific data
+ * @common_data: last (and common) part of the probe
+ * @buf: raw data block
+ */
+struct iwl_scan_probe_req {
+ struct iwl_scan_probe_segment mac_header;
+ struct iwl_scan_probe_segment band_data[2];
+ struct iwl_scan_probe_segment common_data;
+ u8 buf[SCAN_OFFLOAD_PROBE_REQ_SIZE];
+} __packed;
+
+enum iwl_scan_channel_flags {
+ IWL_SCAN_CHANNEL_FLAG_EBS = BIT(0),
+ IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE = BIT(1),
+ IWL_SCAN_CHANNEL_FLAG_CACHE_ADD = BIT(2),
+};
+
+/* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S
+ * @flags: enum iwl_scan_channel_flgs
+ * @non_ebs_ratio: how many regular scan iteration before EBS
+ */
+struct iwl_scan_channel_opt {
+ __le16 flags;
+ __le16 non_ebs_ratio;
+} __packed;
+
+/**
+ * iwl_mvm_lmac_scan_flags
+ * @IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses
+ * without filtering.
+ * @IWL_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels
+ * @IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan
+ * @IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification
+ * @IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching
+ * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented
+ */
+enum iwl_mvm_lmac_scan_flags {
+ IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0),
+ IWL_MVM_LMAC_SCAN_FLAG_PASSIVE = BIT(1),
+ IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = BIT(2),
+ IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = BIT(3),
+ IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4),
+ IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5),
+};
+
+enum iwl_scan_priority {
+ IWL_SCAN_PRIORITY_LOW,
+ IWL_SCAN_PRIORITY_MEDIUM,
+ IWL_SCAN_PRIORITY_HIGH,
+};
+
+/**
+ * iwl_scan_req_unified_lmac - SCAN_REQUEST_CMD_API_S_VER_1
+ * @reserved1: for alignment and future use
+ * @channel_num: num of channels to scan
+ * @active-dwell: dwell time for active channels
+ * @passive-dwell: dwell time for passive channels
+ * @fragmented-dwell: dwell time for fragmented passive scan
+ * @reserved2: for alignment and future use
+ * @rx_chain_selct: PHY_RX_CHAIN_* flags
+ * @scan_flags: &enum iwl_mvm_lmac_scan_flags
+ * @max_out_time: max time (in TU) to be out of associated channel
+ * @suspend_time: pause scan this long (TUs) when returning to service channel
+ * @flags: RXON flags
+ * @filter_flags: RXON filter
+ * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz
+ * @direct_scan: list of SSIDs for directed active scan
+ * @scan_prio: enum iwl_scan_priority
+ * @iter_num: number of scan iterations
+ * @delay: delay in seconds before first iteration
+ * @schedule: two scheduling plans. The first one is finite, the second one can
+ * be infinite.
+ * @channel_opt: channel optimization options, for full and partial scan
+ * @data: channel configuration and probe request packet.
+ */
+struct iwl_scan_req_unified_lmac {
+ /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */
+ __le32 reserved1;
+ u8 n_channels;
+ u8 active_dwell;
+ u8 passive_dwell;
+ u8 fragmented_dwell;
+ __le16 reserved2;
+ __le16 rx_chain_select;
+ __le32 scan_flags;
+ __le32 max_out_time;
+ __le32 suspend_time;
+ /* RX_ON_FLAGS_API_S_VER_1 */
+ __le32 flags;
+ __le32 filter_flags;
+ struct iwl_scan_req_tx_cmd tx_cmd[2];
+ struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
+ __le32 scan_prio;
+ /* SCAN_REQ_PERIODIC_PARAMS_API_S */
+ __le32 iter_num;
+ __le32 delay;
+ struct iwl_scan_offload_schedule schedule[2];
+ struct iwl_scan_channel_opt channel_opt[2];
+ u8 data[];
+} __packed;
+
+/**
+ * struct iwl_lmac_scan_results_notif - scan results for one channel -
+ * SCAN_RESULT_NTF_API_S_VER_3
+ * @channel: which channel the results are from
+ * @band: 0 for 5.2 GHz, 1 for 2.4 GHz
+ * @probe_status: SCAN_PROBE_STATUS_*, indicates success of probe request
+ * @num_probe_not_sent: # of request that weren't sent due to not enough time
+ * @duration: duration spent in channel, in usecs
+ */
+struct iwl_lmac_scan_results_notif {
+ u8 channel;
+ u8 band;
+ u8 probe_status;
+ u8 num_probe_not_sent;
+ __le32 duration;
+} __packed;
+
+/**
+ * struct iwl_lmac_scan_complete_notif - notifies end of scanning (all channels)
+ * SCAN_COMPLETE_NTF_API_S_VER_3
+ * @scanned_channels: number of channels scanned (and number of valid results)
+ * @status: one of SCAN_COMP_STATUS_*
+ * @bt_status: BT on/off status
+ * @last_channel: last channel that was scanned
+ * @tsf_low: TSF timer (lower half) in usecs
+ * @tsf_high: TSF timer (higher half) in usecs
+ * @results: an array of scan results, only "scanned_channels" of them are valid
+ */
+struct iwl_lmac_scan_complete_notif {
+ u8 scanned_channels;
+ u8 status;
+ u8 bt_status;
+ u8 last_channel;
+ __le32 tsf_low;
+ __le32 tsf_high;
+ struct iwl_scan_results_notif results[];
+} __packed;
+
+/**
+ * iwl_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2
+ * @last_schedule_line: last schedule line executed (fast or regular)
+ * @last_schedule_iteration: last scan iteration executed before scan abort
+ * @status: enum iwl_scan_offload_complete_status
+ * @ebs_status: EBS success status &enum iwl_scan_ebs_status
+ * @time_after_last_iter; time in seconds elapsed after last iteration
+ */
+struct iwl_periodic_scan_complete {
+ u8 last_schedule_line;
+ u8 last_schedule_iteration;
+ u8 status;
+ u8 ebs_status;
+ __le32 time_after_last_iter;
+ __le32 reserved;
+} __packed;
+
#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
index 39cebee8016f..47bd0406355d 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-sta.h
@@ -67,7 +67,7 @@
* enum iwl_sta_flags - flags for the ADD_STA host command
* @STA_FLG_REDUCED_TX_PWR_CTRL:
* @STA_FLG_REDUCED_TX_PWR_DATA:
- * @STA_FLG_FLG_ANT_MSK: Antenna selection
+ * @STA_FLG_DISABLE_TX: set if TX should be disabled
* @STA_FLG_PS: set if STA is in Power Save
* @STA_FLG_INVALID: set if STA is invalid
* @STA_FLG_DLP_EN: Direct Link Protocol is enabled
@@ -91,10 +91,7 @@ enum iwl_sta_flags {
STA_FLG_REDUCED_TX_PWR_CTRL = BIT(3),
STA_FLG_REDUCED_TX_PWR_DATA = BIT(6),
- STA_FLG_FLG_ANT_A = (1 << 4),
- STA_FLG_FLG_ANT_B = (2 << 4),
- STA_FLG_FLG_ANT_MSK = (STA_FLG_FLG_ANT_A |
- STA_FLG_FLG_ANT_B),
+ STA_FLG_DISABLE_TX = BIT(4),
STA_FLG_PS = BIT(8),
STA_FLG_DRAIN_FLOW = BIT(12),
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
index 6cc5f52b807f..d6073f67b212 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h
@@ -69,10 +69,8 @@
* @TX_CMD_FLG_ACK: expect ACK from receiving station
* @TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command.
* Otherwise, use rate_n_flags from the TX command
- * @TX_CMD_FLG_BA: this frame is a block ack
* @TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected
* Must set TX_CMD_FLG_ACK with this flag.
- * @TX_CMD_FLG_TXOP_PROT: protect frame with full TXOP protection
* @TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence
* @TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence
* @TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC)
@@ -82,12 +80,10 @@
* @TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control.
* Should be set for mgmt, non-QOS data, mcast, bcast and in scan command
* @TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU
- * @TX_CMD_FLG_NEXT_FRAME: this frame includes information of the next frame
* @TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame
* Should be set for beacons and probe responses
* @TX_CMD_FLG_CALIB: activate PA TX power calibrations
* @TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count
- * @TX_CMD_FLG_AGG_START: allow this frame to start aggregation
* @TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header.
* Should be set for 26/30 length MAC headers
* @TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW
@@ -103,7 +99,6 @@ enum iwl_tx_flags {
TX_CMD_FLG_PROT_REQUIRE = BIT(0),
TX_CMD_FLG_ACK = BIT(3),
TX_CMD_FLG_STA_RATE = BIT(4),
- TX_CMD_FLG_BA = BIT(5),
TX_CMD_FLG_BAR = BIT(6),
TX_CMD_FLG_TXOP_PROT = BIT(7),
TX_CMD_FLG_VHT_NDPA = BIT(8),
@@ -113,11 +108,9 @@ enum iwl_tx_flags {
TX_CMD_FLG_BT_DIS = BIT(12),
TX_CMD_FLG_SEQ_CTL = BIT(13),
TX_CMD_FLG_MORE_FRAG = BIT(14),
- TX_CMD_FLG_NEXT_FRAME = BIT(15),
TX_CMD_FLG_TSF = BIT(16),
TX_CMD_FLG_CALIB = BIT(17),
TX_CMD_FLG_KEEP_SEQ_CTL = BIT(18),
- TX_CMD_FLG_AGG_START = BIT(19),
TX_CMD_FLG_MH_PAD = BIT(20),
TX_CMD_FLG_RESP_TO_DRV = BIT(21),
TX_CMD_FLG_CCMP_AGG = BIT(22),
@@ -191,8 +184,6 @@ enum iwl_tx_flags {
* struct iwl_tx_cmd - TX command struct to FW
* ( TX_CMD = 0x1c )
* @len: in bytes of the payload, see below for details
- * @next_frame_len: same as len, but for next frame (0 if not applicable)
- * Used for fragmentation and bursting, but not in 11n aggregation.
* @tx_flags: combination of TX_CMD_FLG_*
* @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is
* cleared. Combination of RATE_MCS_*
@@ -210,8 +201,6 @@ enum iwl_tx_flags {
* @data_retry_limit: max attempts to send the data packet
* @tid_spec: TID/tspec
* @pm_frame_timeout: PM TX frame timeout
- * @driver_txop: duration od EDCA TXOP, in 32-usec units. Set this if not
- * specified by HCCA protocol
*
* The byte count (both len and next_frame_len) includes MAC header
* (24/26/30/32 bytes)
@@ -241,8 +230,7 @@ struct iwl_tx_cmd {
u8 initial_rate_index;
u8 reserved2;
u8 key[16];
- __le16 next_frame_flags;
- __le16 reserved3;
+ __le32 reserved3;
__le32 life_time;
__le32 dram_lsb_ptr;
u8 dram_msb_ptr;
@@ -250,7 +238,7 @@ struct iwl_tx_cmd {
u8 data_retry_limit;
u8 tid_tspec;
__le16 pm_frame_timeout;
- __le16 driver_txop;
+ __le16 reserved4;
u8 payload[0];
struct ieee80211_hdr hdr[0];
} __packed; /* TX_CMD_API_S_VER_3 */
@@ -549,6 +537,20 @@ struct iwl_beacon_notif {
} __packed;
/**
+ * struct iwl_extended_beacon_notif - notifies about beacon transmission
+ * @beacon_notify_hdr: tx response command associated with the beacon
+ * @tsf: last beacon tsf
+ * @ibss_mgr_status: whether IBSS is manager
+ * @gp2: last beacon time in gp2
+ */
+struct iwl_extended_beacon_notif {
+ struct iwl_mvm_tx_resp beacon_notify_hdr;
+ __le64 tsf;
+ __le32 ibss_mgr_status;
+ __le32 gp2;
+} __packed; /* BEACON_NTFY_API_S_VER_5 */
+
+/**
* enum iwl_dump_control - dump (flush) control flags
* @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty
* and the TFD queues are empty.
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
index 309a9b9a94fe..b8e4e78d601b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h
+++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h
@@ -86,6 +86,8 @@ enum {
#define IWL_MVM_STATION_COUNT 16
+#define IWL_MVM_TDLS_STA_COUNT 4
+
/* commands */
enum {
MVM_ALIVE = 0x1,
@@ -135,6 +137,7 @@ enum {
SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E,
SCAN_OFFLOAD_CONFIG_CMD = 0x6f,
MATCH_FOUND_NOTIFICATION = 0xd9,
+ SCAN_ITERATION_COMPLETE = 0xe7,
/* Phy */
PHY_CONFIGURATION_CMD = 0x6a,
@@ -163,7 +166,6 @@ enum {
BEACON_NOTIFICATION = 0x90,
BEACON_TEMPLATE_CMD = 0x91,
TX_ANT_CONFIGURATION_CMD = 0x98,
- BT_CONFIG = 0x9b,
STATISTICS_NOTIFICATION = 0x9d,
EOSP_NOTIFICATION = 0x9e,
REDUCE_TX_POWER_CMD = 0x9f,
@@ -185,6 +187,10 @@ enum {
BT_COEX_PRIO_TABLE = 0xcc,
BT_COEX_PROT_ENV = 0xcd,
BT_PROFILE_NOTIFICATION = 0xce,
+ BT_CONFIG = 0x9b,
+ BT_COEX_UPDATE_SW_BOOST = 0x5a,
+ BT_COEX_UPDATE_CORUN_LUT = 0x5b,
+ BT_COEX_UPDATE_REDUCED_TXP = 0x5c,
BT_COEX_CI = 0x5d,
REPLY_SF_CFG_CMD = 0xd1,
@@ -534,6 +540,9 @@ enum iwl_time_event_type {
/* WiDi Sync Events */
TE_WIDI_TX_SYNC,
+ /* Channel Switch NoA */
+ TE_P2P_GO_CSA_NOA,
+
TE_MAX
}; /* MAC_EVENT_TYPE_API_E_VER_1 */
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
index 8b5302777632..96b9cf8137e7 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c
@@ -67,6 +67,7 @@
#include "iwl-prph.h"
#include "fw-api.h"
#include "mvm.h"
+#include "time-event.h"
const u8 iwl_mvm_ac_to_tx_fifo[] = {
IWL_MVM_TX_FIFO_VO,
@@ -667,10 +668,9 @@ static void iwl_mvm_mac_ctxt_cmd_common(struct iwl_mvm *mvm,
if (vif->bss_conf.qos)
cmd->qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
- if (vif->bss_conf.use_cts_prot) {
+ if (vif->bss_conf.use_cts_prot)
cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_TGG_PROTECT);
- cmd->protection_flags |= cpu_to_le32(MAC_PROT_FLG_SELF_CTS_EN);
- }
+
IWL_DEBUG_RATE(mvm, "use_cts_prot %d, ht_operation_mode %d\n",
vif->bss_conf.use_cts_prot,
vif->bss_conf.ht_operation_mode);
@@ -904,7 +904,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
struct iwl_mac_beacon_cmd beacon_cmd = {};
struct ieee80211_tx_info *info;
u32 beacon_skb_len;
- u32 rate;
+ u32 rate, tx_flags;
if (WARN_ON(!beacon))
return -EINVAL;
@@ -914,14 +914,17 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
/* TODO: for now the beacon template id is set to be the mac context id.
* Might be better to handle it as another resource ... */
beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id);
+ info = IEEE80211_SKB_CB(beacon);
/* Set up TX command fields */
beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len);
beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id;
beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE);
- beacon_cmd.tx.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
- TX_CMD_FLG_BT_DIS |
- TX_CMD_FLG_TSF);
+ tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF;
+ tx_flags |=
+ iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) <<
+ TX_CMD_FLG_BT_PRIO_POS;
+ beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags);
mvm->mgmt_last_antenna_idx =
iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
@@ -931,8 +934,6 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm,
cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) <<
RATE_MCS_ANT_POS);
- info = IEEE80211_SKB_CB(beacon);
-
if (info->band == IEEE80211_BAND_5GHZ || vif->p2p) {
rate = IWL_FIRST_OFDM_RATE;
} else {
@@ -969,7 +970,7 @@ int iwl_mvm_mac_ctxt_beacon_changed(struct iwl_mvm *mvm,
WARN_ON(vif->type != NL80211_IFTYPE_AP &&
vif->type != NL80211_IFTYPE_ADHOC);
- beacon = ieee80211_beacon_get(mvm->hw, vif);
+ beacon = ieee80211_beacon_get_template(mvm->hw, vif, NULL);
if (!beacon)
return -ENOMEM;
@@ -1200,31 +1201,94 @@ int iwl_mvm_mac_ctxt_remove(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
return 0;
}
+static void iwl_mvm_csa_count_down(struct iwl_mvm *mvm,
+ struct ieee80211_vif *csa_vif, u32 gp2)
+{
+ struct iwl_mvm_vif *mvmvif =
+ iwl_mvm_vif_from_mac80211(csa_vif);
+
+ if (!ieee80211_csa_is_complete(csa_vif)) {
+ int c = ieee80211_csa_update_counter(csa_vif);
+
+ iwl_mvm_mac_ctxt_beacon_changed(mvm, csa_vif);
+ if (csa_vif->p2p &&
+ !iwl_mvm_te_scheduled(&mvmvif->time_event_data) && gp2) {
+ u32 rel_time = (c + 1) *
+ csa_vif->bss_conf.beacon_int -
+ IWL_MVM_CHANNEL_SWITCH_TIME;
+ u32 apply_time = gp2 + rel_time * 1024;
+
+ iwl_mvm_schedule_csa_noa(mvm, csa_vif,
+ IWL_MVM_CHANNEL_SWITCH_TIME -
+ IWL_MVM_CHANNEL_SWITCH_MARGIN,
+ apply_time);
+ }
+ } else if (!iwl_mvm_te_scheduled(&mvmvif->time_event_data)) {
+ /* we don't have CSA NoA scheduled yet, switch now */
+ ieee80211_csa_finish(csa_vif);
+ RCU_INIT_POINTER(mvm->csa_vif, NULL);
+ }
+}
+
int iwl_mvm_rx_beacon_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_beacon_notif *beacon = (void *)pkt->data;
- u16 status __maybe_unused =
- le16_to_cpu(beacon->beacon_notify_hdr.status.status);
- u32 rate __maybe_unused =
- le32_to_cpu(beacon->beacon_notify_hdr.initial_rate);
+ struct iwl_mvm_tx_resp *beacon_notify_hdr;
+ struct ieee80211_vif *csa_vif;
+ struct ieee80211_vif *tx_blocked_vif;
+ u64 tsf;
lockdep_assert_held(&mvm->mutex);
- IWL_DEBUG_RX(mvm, "beacon status %#x retries:%d tsf:0x%16llX rate:%d\n",
- status & TX_STATUS_MSK,
- beacon->beacon_notify_hdr.failure_frame,
- le64_to_cpu(beacon->tsf),
- rate);
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_CAPA_EXTENDED_BEACON) {
+ struct iwl_extended_beacon_notif *beacon = (void *)pkt->data;
- if (unlikely(mvm->csa_vif && mvm->csa_vif->csa_active)) {
- if (!ieee80211_csa_is_complete(mvm->csa_vif)) {
- iwl_mvm_mac_ctxt_beacon_changed(mvm, mvm->csa_vif);
- } else {
- ieee80211_csa_finish(mvm->csa_vif);
- mvm->csa_vif = NULL;
+ beacon_notify_hdr = &beacon->beacon_notify_hdr;
+ tsf = le64_to_cpu(beacon->tsf);
+ mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2);
+ } else {
+ struct iwl_beacon_notif *beacon = (void *)pkt->data;
+
+ beacon_notify_hdr = &beacon->beacon_notify_hdr;
+ tsf = le64_to_cpu(beacon->tsf);
+ }
+
+ IWL_DEBUG_RX(mvm,
+ "beacon status %#x retries:%d tsf:0x%16llX gp2:0x%X rate:%d\n",
+ le16_to_cpu(beacon_notify_hdr->status.status) &
+ TX_STATUS_MSK,
+ beacon_notify_hdr->failure_frame, tsf,
+ mvm->ap_last_beacon_gp2,
+ le32_to_cpu(beacon_notify_hdr->initial_rate));
+
+ csa_vif = rcu_dereference_protected(mvm->csa_vif,
+ lockdep_is_held(&mvm->mutex));
+ if (unlikely(csa_vif && csa_vif->csa_active))
+ iwl_mvm_csa_count_down(mvm, csa_vif, mvm->ap_last_beacon_gp2);
+
+ tx_blocked_vif = rcu_dereference_protected(mvm->csa_tx_blocked_vif,
+ lockdep_is_held(&mvm->mutex));
+ if (unlikely(tx_blocked_vif)) {
+ struct iwl_mvm_vif *mvmvif =
+ iwl_mvm_vif_from_mac80211(tx_blocked_vif);
+
+ /*
+ * The channel switch is started and we have blocked the
+ * stations. If this is the first beacon (the timeout wasn't
+ * set), set the unblock timeout, otherwise countdown
+ */
+ if (!mvm->csa_tx_block_bcn_timeout)
+ mvm->csa_tx_block_bcn_timeout =
+ IWL_MVM_CS_UNBLOCK_TX_TIMEOUT;
+ else
+ mvm->csa_tx_block_bcn_timeout--;
+
+ /* Check if the timeout is expired, and unblock tx */
+ if (mvm->csa_tx_block_bcn_timeout == 0) {
+ iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false);
+ RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
}
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index 7215f5980186..2eb6ebee4467 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -80,6 +80,8 @@
#include "fw-api-scan.h"
#include "iwl-phy-db.h"
#include "testmode.h"
+#include "iwl-fw-error-dump.h"
+#include "iwl-prph.h"
static const struct ieee80211_iface_limit iwl_mvm_limits[] = {
{
@@ -241,6 +243,21 @@ iwl_mvm_unref_all_except(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref)
}
}
+static int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type)
+{
+ iwl_mvm_ref(mvm, ref_type);
+
+ if (!wait_event_timeout(mvm->d0i3_exit_waitq,
+ !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status),
+ HZ)) {
+ WARN_ON_ONCE(1);
+ iwl_mvm_unref(mvm, ref_type);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static void iwl_mvm_reset_phy_ctxts(struct iwl_mvm *mvm)
{
int i;
@@ -276,6 +293,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
IEEE80211_HW_AMPDU_AGGREGATION |
IEEE80211_HW_TIMING_BEACON_ONLY |
IEEE80211_HW_CONNECTION_MONITOR |
+ IEEE80211_HW_CHANCTX_STA_CSA |
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS |
IEEE80211_HW_SUPPORTS_STATIC_SMPS;
@@ -303,6 +321,16 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
}
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT &&
+ !iwlwifi_mod_params.uapsd_disable) {
+ hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD;
+ hw->uapsd_queues = IWL_UAPSD_AC_INFO;
+ hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
+ }
+
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+ hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
+
hw->sta_data_size = sizeof(struct iwl_mvm_sta);
hw->vif_data_size = sizeof(struct iwl_mvm_vif);
hw->chanctx_data_size = sizeof(u16);
@@ -374,6 +402,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
hw->wiphy->max_sched_scan_ie_len = SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
+ NL80211_FEATURE_LOW_PRIORITY_SCAN |
NL80211_FEATURE_P2P_GO_OPPPS;
mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
@@ -549,9 +578,6 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
case IEEE80211_AMPDU_TX_OPERATIONAL:
- iwl_mvm_ref(mvm, IWL_MVM_REF_TX_AGG);
- tx_agg_ref = true;
-
/*
* for tx start, wait synchronously until D0i3 exit to
* get the correct sequence number for the tid.
@@ -560,12 +586,11 @@ static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
* by the trans layer (unlike commands), so wait for
* d0i3 exit in these cases as well.
*/
- if (!wait_event_timeout(mvm->d0i3_exit_waitq,
- !test_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status), HZ)) {
- WARN_ON_ONCE(1);
- iwl_mvm_unref(mvm, IWL_MVM_REF_TX_AGG);
- return -EIO;
- }
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_TX_AGG);
+ if (ret)
+ return ret;
+
+ tx_agg_ref = true;
break;
default:
break;
@@ -637,6 +662,104 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac,
mvmvif->phy_ctxt = NULL;
}
+#ifdef CONFIG_IWLWIFI_DEBUGFS
+static void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
+{
+ struct iwl_fw_error_dump_file *dump_file;
+ struct iwl_fw_error_dump_data *dump_data;
+ struct iwl_fw_error_dump_info *dump_info;
+ const struct fw_img *img;
+ u32 sram_len, sram_ofs;
+ u32 file_len, rxf_len;
+ unsigned long flags;
+ u32 trans_len;
+ int reg_val;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (mvm->fw_error_dump)
+ return;
+
+ img = &mvm->fw->img[mvm->cur_ucode];
+ sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
+ sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
+
+ /* reading buffer size */
+ reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
+ rxf_len = (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
+
+ /* the register holds the value divided by 128 */
+ rxf_len = rxf_len << 7;
+
+ file_len = sizeof(*dump_file) +
+ sizeof(*dump_data) * 3 +
+ sram_len +
+ rxf_len +
+ sizeof(*dump_info);
+
+ trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
+ if (trans_len)
+ file_len += trans_len;
+
+ dump_file = vzalloc(file_len);
+ if (!dump_file)
+ return;
+
+ mvm->fw_error_dump = dump_file;
+
+ dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
+ dump_file->file_len = cpu_to_le32(file_len);
+ dump_data = (void *)dump_file->data;
+
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_DEV_FW_INFO);
+ dump_data->len = cpu_to_le32(sizeof(*dump_info));
+ dump_info = (void *) dump_data->data;
+ dump_info->device_family =
+ mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ?
+ cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) :
+ cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8);
+ memcpy(dump_info->fw_human_readable, mvm->fw->human_readable,
+ sizeof(dump_info->fw_human_readable));
+ strncpy(dump_info->dev_human_readable, mvm->cfg->name,
+ sizeof(dump_info->dev_human_readable));
+ strncpy(dump_info->bus_human_readable, mvm->dev->bus->name,
+ sizeof(dump_info->bus_human_readable));
+
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
+ dump_data->len = cpu_to_le32(rxf_len);
+
+ if (iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
+ u32 *rxf = (void *)dump_data->data;
+ int i;
+
+ for (i = 0; i < (rxf_len / sizeof(u32)); i++) {
+ iwl_trans_write_prph(mvm->trans,
+ RXF_LD_FENCE_OFFSET_ADDR,
+ i * sizeof(u32));
+ rxf[i] = iwl_trans_read_prph(mvm->trans,
+ RXF_FIFO_RD_FENCE_ADDR);
+ }
+ iwl_trans_release_nic_access(mvm->trans, &flags);
+ }
+
+ dump_data = iwl_fw_error_next_data(dump_data);
+ dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
+ dump_data->len = cpu_to_le32(sram_len);
+ iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data,
+ sram_len);
+
+ if (trans_len) {
+ void *buf = iwl_fw_error_next_data(dump_data);
+ u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
+ trans_len);
+ dump_data = (void *)((u8 *)buf + real_trans_len);
+ dump_file->file_len =
+ cpu_to_le32(file_len - trans_len + real_trans_len);
+ }
+}
+#endif
+
static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
{
#ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -688,6 +811,16 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw)
iwl_mvm_restart_cleanup(mvm);
ret = iwl_mvm_up(mvm);
+
+ if (ret && test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) {
+ /* Something went wrong - we need to finish some cleanup
+ * that normally iwl_mvm_mac_restart_complete() below
+ * would do.
+ */
+ clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+ iwl_mvm_d0i3_enable_tx(mvm, NULL);
+ }
+
mutex_unlock(&mvm->mutex);
return ret;
@@ -786,6 +919,15 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
int ret;
/*
+ * make sure D0i3 exit is completed, otherwise a target access
+ * during tx queue configuration could be done when still in
+ * D0i3 state.
+ */
+ ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_ADD_IF);
+ if (ret)
+ return ret;
+
+ /*
* Not much to do here. The stack will not allow interface
* types or combinations that we didn't advertise, so we
* don't really have to check the types.
@@ -899,6 +1041,8 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
out_unlock:
mutex_unlock(&mvm->mutex);
+ iwl_mvm_unref(mvm, IWL_MVM_REF_ADD_IF);
+
return ret;
}
@@ -1159,8 +1303,12 @@ static void iwl_mvm_bcast_filter_iterator(void *_data, u8 *mac,
bcast_mac = &cmd->macs[mvmvif->id];
- /* enable filtering only for associated stations */
- if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc)
+ /*
+ * enable filtering only for associated stations, but not for P2P
+ * Clients
+ */
+ if (vif->type != NL80211_IFTYPE_STATION || vif->p2p ||
+ !vif->bss_conf.assoc)
return;
bcast_mac->default_discard = 1;
@@ -1237,10 +1385,6 @@ static int iwl_mvm_configure_bcast_filter(struct iwl_mvm *mvm,
if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING))
return 0;
- /* bcast filtering isn't supported for P2P client */
- if (vif->p2p)
- return 0;
-
if (!iwl_mvm_bcast_filter_build_cmd(mvm, &cmd))
return 0;
@@ -1278,7 +1422,7 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
if (changes & BSS_CHANGED_ASSOC) {
if (bss_conf->assoc) {
/* add quota for this interface */
- ret = iwl_mvm_update_quotas(mvm, vif);
+ ret = iwl_mvm_update_quotas(mvm, NULL);
if (ret) {
IWL_ERR(mvm, "failed to update quotas\n");
return;
@@ -1425,7 +1569,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw,
/* power updated needs to be done before quotas */
iwl_mvm_power_update_mac(mvm);
- ret = iwl_mvm_update_quotas(mvm, vif);
+ ret = iwl_mvm_update_quotas(mvm, NULL);
if (ret)
goto out_quota_failed;
@@ -1463,7 +1607,20 @@ static void iwl_mvm_stop_ap_ibss(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
+ /* Handle AP stop while in CSA */
+ if (rcu_access_pointer(mvm->csa_vif) == vif) {
+ iwl_mvm_remove_time_event(mvm, mvmvif,
+ &mvmvif->time_event_data);
+ RCU_INIT_POINTER(mvm->csa_vif, NULL);
+ }
+
+ if (rcu_access_pointer(mvm->csa_tx_blocked_vif) == vif) {
+ RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL);
+ mvm->csa_tx_block_bcn_timeout = 0;
+ }
+
mvmvif->ap_ibss_active = false;
+ mvm->ap_last_beacon_gp2 = 0;
iwl_mvm_bt_coex_vif_change(mvm);
@@ -1517,7 +1674,7 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex);
if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
- iwl_mvm_sched_scan_stop(mvm, true);
+ iwl_mvm_scan_offload_stop(mvm, true);
switch (vif->type) {
case NL80211_IFTYPE_STATION:
@@ -1537,19 +1694,21 @@ static void iwl_mvm_bss_info_changed(struct ieee80211_hw *hw,
static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req)
+ struct ieee80211_scan_request *hw_req)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct cfg80211_scan_request *req = &hw_req->req;
int ret;
- if (req->n_channels == 0 || req->n_channels > MAX_NUM_SCAN_CHANNELS)
+ if (req->n_channels == 0 ||
+ req->n_channels > mvm->fw->ucode_capa.n_scan_channels)
return -EINVAL;
mutex_lock(&mvm->mutex);
switch (mvm->scan_status) {
case IWL_MVM_SCAN_SCHED:
- ret = iwl_mvm_sched_scan_stop(mvm, true);
+ ret = iwl_mvm_scan_offload_stop(mvm, true);
if (ret) {
ret = -EBUSY;
goto out;
@@ -1564,7 +1723,11 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
- ret = iwl_mvm_scan_request(mvm, vif, req);
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+ ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
+ else
+ ret = iwl_mvm_scan_request(mvm, vif, req);
+
if (ret)
iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
out:
@@ -1680,6 +1843,70 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw,
mutex_unlock(&mvm->mutex);
}
+int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ int count = 0;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (!sta || IS_ERR(sta) || !sta->tdls)
+ continue;
+
+ if (vif) {
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ if (mvmsta->vif != vif)
+ continue;
+ }
+
+ count++;
+ }
+
+ return count;
+}
+
+static void iwl_mvm_recalc_tdls_state(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ bool sta_added)
+{
+ int tdls_sta_cnt = iwl_mvm_tdls_sta_count(mvm, vif);
+
+ /*
+ * Disable ps when the first TDLS sta is added and re-enable it
+ * when the last TDLS sta is removed
+ */
+ if ((tdls_sta_cnt == 1 && sta_added) ||
+ (tdls_sta_cnt == 0 && !sta_added))
+ iwl_mvm_power_update_mac(mvm);
+}
+
+static void iwl_mvm_teardown_tdls_peers(struct iwl_mvm *mvm)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvmsta;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (!sta || IS_ERR(sta) || !sta->tdls)
+ continue;
+
+ mvmsta = iwl_mvm_sta_from_mac80211(sta);
+ ieee80211_tdls_oper_request(mvmsta->vif, sta->addr,
+ NL80211_TDLS_TEARDOWN,
+ WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED,
+ GFP_KERNEL);
+ }
+}
+
static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
@@ -1718,7 +1945,20 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
ret = -EINVAL;
goto out_unlock;
}
+
+ if (sta->tdls &&
+ (vif->p2p ||
+ iwl_mvm_tdls_sta_count(mvm, NULL) ==
+ IWL_MVM_TDLS_STA_COUNT ||
+ iwl_mvm_phy_ctx_count(mvm) > 1)) {
+ IWL_DEBUG_MAC80211(mvm, "refusing TDLS sta\n");
+ ret = -EBUSY;
+ goto out_unlock;
+ }
+
ret = iwl_mvm_add_sta(mvm, vif, sta);
+ if (sta->tdls && ret == 0)
+ iwl_mvm_recalc_tdls_state(mvm, vif, true);
} else if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_AUTH) {
/*
@@ -1736,6 +1976,11 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
true);
} else if (old_state == IEEE80211_STA_ASSOC &&
new_state == IEEE80211_STA_AUTHORIZED) {
+
+ /* we don't support TDLS during DCM */
+ if (iwl_mvm_phy_ctx_count(mvm) > 1)
+ iwl_mvm_teardown_tdls_peers(mvm);
+
/* enable beacon filtering */
WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif, 0));
ret = 0;
@@ -1753,6 +1998,8 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw,
} else if (old_state == IEEE80211_STA_NONE &&
new_state == IEEE80211_STA_NOTEXIST) {
ret = iwl_mvm_rm_sta(mvm, vif, sta);
+ if (sta->tdls)
+ iwl_mvm_recalc_tdls_state(mvm, vif, false);
} else {
ret = -EIO;
}
@@ -1824,10 +2071,22 @@ static void iwl_mvm_mac_mgd_prepare_tx(struct ieee80211_hw *hw,
mutex_unlock(&mvm->mutex);
}
+static void iwl_mvm_mac_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ u32 duration = 2 * vif->bss_conf.dtim_period * vif->bss_conf.beacon_int;
+
+ mutex_lock(&mvm->mutex);
+ /* Protect the session to hear the TDLS setup response on the channel */
+ iwl_mvm_protect_session(mvm, vif, duration, duration, 100);
+ mutex_unlock(&mvm->mutex);
+}
+
static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies)
+ struct ieee80211_scan_ies *ies)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
@@ -1865,15 +2124,21 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
mvm->scan_status = IWL_MVM_SCAN_SCHED;
- ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
- if (ret)
- goto err;
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+ ret = iwl_mvm_config_sched_scan(mvm, vif, req, ies);
+ if (ret)
+ goto err;
+ }
ret = iwl_mvm_config_sched_scan_profiles(mvm, req);
if (ret)
goto err;
- ret = iwl_mvm_sched_scan_start(mvm, req);
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+ ret = iwl_mvm_unified_sched_scan_lmac(mvm, vif, req, ies);
+ else
+ ret = iwl_mvm_sched_scan_start(mvm, req);
+
if (!ret)
goto out;
err:
@@ -1892,7 +2157,7 @@ static int iwl_mvm_mac_sched_scan_stop(struct ieee80211_hw *hw,
int ret;
mutex_lock(&mvm->mutex);
- ret = iwl_mvm_sched_scan_stop(mvm, false);
+ ret = iwl_mvm_scan_offload_stop(mvm, false);
mutex_unlock(&mvm->mutex);
iwl_mvm_wait_for_async_handlers(mvm);
@@ -2126,17 +2391,17 @@ static int iwl_mvm_cancel_roc(struct ieee80211_hw *hw)
return 0;
}
-static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_chanctx_conf *ctx)
+static int __iwl_mvm_add_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_chanctx_conf *ctx)
{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt;
int ret;
+ lockdep_assert_held(&mvm->mutex);
+
IWL_DEBUG_MAC80211(mvm, "Add channel context\n");
- mutex_lock(&mvm->mutex);
phy_ctxt = iwl_mvm_get_free_phy_ctxt(mvm);
if (!phy_ctxt) {
ret = -ENOSPC;
@@ -2154,19 +2419,40 @@ static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
iwl_mvm_phy_ctxt_ref(mvm, phy_ctxt);
*phy_ctxt_id = phy_ctxt->id;
out:
+ return ret;
+}
+
+static int iwl_mvm_add_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ mutex_lock(&mvm->mutex);
+ ret = __iwl_mvm_add_chanctx(mvm, ctx);
mutex_unlock(&mvm->mutex);
+
return ret;
}
+static void __iwl_mvm_remove_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
+ struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
+
+ lockdep_assert_held(&mvm->mutex);
+
+ iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
+}
+
static void iwl_mvm_remove_chanctx(struct ieee80211_hw *hw,
struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
- struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
mutex_lock(&mvm->mutex);
- iwl_mvm_phy_ctxt_unref(mvm, phy_ctxt);
+ __iwl_mvm_remove_chanctx(mvm, ctx);
mutex_unlock(&mvm->mutex);
}
@@ -2195,17 +2481,17 @@ static void iwl_mvm_change_chanctx(struct ieee80211_hw *hw,
mutex_unlock(&mvm->mutex);
}
-static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_chanctx_conf *ctx)
+static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx,
+ bool switching_chanctx)
{
- struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
u16 *phy_ctxt_id = (u16 *)ctx->drv_priv;
struct iwl_mvm_phy_ctxt *phy_ctxt = &mvm->phy_ctxts[*phy_ctxt_id];
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
int ret;
- mutex_lock(&mvm->mutex);
+ lockdep_assert_held(&mvm->mutex);
mvmvif->phy_ctxt = phy_ctxt;
@@ -2222,18 +2508,18 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
* (in bss_info_changed), similarly for IBSS.
*/
ret = 0;
- goto out_unlock;
+ goto out;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_MONITOR:
break;
default:
ret = -EINVAL;
- goto out_unlock;
+ goto out;
}
ret = iwl_mvm_binding_add_vif(mvm, vif);
if (ret)
- goto out_unlock;
+ goto out;
/*
* Power state must be updated before quotas,
@@ -2247,65 +2533,162 @@ static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
*/
if (vif->type == NL80211_IFTYPE_MONITOR) {
mvmvif->monitor_active = true;
- ret = iwl_mvm_update_quotas(mvm, vif);
+ ret = iwl_mvm_update_quotas(mvm, NULL);
if (ret)
goto out_remove_binding;
}
/* Handle binding during CSA */
- if (vif->type == NL80211_IFTYPE_AP) {
- iwl_mvm_update_quotas(mvm, vif);
+ if ((vif->type == NL80211_IFTYPE_AP) ||
+ (switching_chanctx && (vif->type == NL80211_IFTYPE_STATION))) {
+ iwl_mvm_update_quotas(mvm, NULL);
iwl_mvm_mac_ctxt_changed(mvm, vif, false);
}
- goto out_unlock;
+ goto out;
- out_remove_binding:
+out_remove_binding:
iwl_mvm_binding_remove_vif(mvm, vif);
iwl_mvm_power_update_mac(mvm);
- out_unlock:
- mutex_unlock(&mvm->mutex);
+out:
if (ret)
mvmvif->phy_ctxt = NULL;
return ret;
}
-
-static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
- struct ieee80211_chanctx_conf *ctx)
+static int iwl_mvm_assign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ int ret;
mutex_lock(&mvm->mutex);
+ ret = __iwl_mvm_assign_vif_chanctx(mvm, vif, ctx, false);
+ mutex_unlock(&mvm->mutex);
+
+ return ret;
+}
+
+static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx,
+ bool switching_chanctx)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct ieee80211_vif *disabled_vif = NULL;
+
+ lockdep_assert_held(&mvm->mutex);
iwl_mvm_remove_time_event(mvm, mvmvif, &mvmvif->time_event_data);
switch (vif->type) {
case NL80211_IFTYPE_ADHOC:
- goto out_unlock;
+ goto out;
case NL80211_IFTYPE_MONITOR:
mvmvif->monitor_active = false;
- iwl_mvm_update_quotas(mvm, NULL);
break;
case NL80211_IFTYPE_AP:
/* This part is triggered only during CSA */
if (!vif->csa_active || !mvmvif->ap_ibss_active)
- goto out_unlock;
+ goto out;
+
+ /* Set CS bit on all the stations */
+ iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, true);
+
+ /* Save blocked iface, the timeout is set on the next beacon */
+ rcu_assign_pointer(mvm->csa_tx_blocked_vif, vif);
mvmvif->ap_ibss_active = false;
- iwl_mvm_update_quotas(mvm, NULL);
- /*TODO: bt_coex notification here? */
+ break;
+ case NL80211_IFTYPE_STATION:
+ if (!switching_chanctx)
+ break;
+
+ disabled_vif = vif;
+
+ iwl_mvm_mac_ctxt_changed(mvm, vif, true);
+ break;
default:
break;
}
+ iwl_mvm_update_quotas(mvm, disabled_vif);
iwl_mvm_binding_remove_vif(mvm, vif);
-out_unlock:
+out:
mvmvif->phy_ctxt = NULL;
iwl_mvm_power_update_mac(mvm);
+}
+
+static void iwl_mvm_unassign_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_chanctx_conf *ctx)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+
+ mutex_lock(&mvm->mutex);
+ __iwl_mvm_unassign_vif_chanctx(mvm, vif, ctx, false);
+ mutex_unlock(&mvm->mutex);
+}
+
+static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw,
+ struct ieee80211_vif_chanctx_switch *vifs,
+ int n_vifs,
+ enum ieee80211_chanctx_switch_mode mode)
+{
+ struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ int ret;
+
+ /* we only support SWAP_CONTEXTS and with a single-vif right now */
+ if (mode != CHANCTX_SWMODE_SWAP_CONTEXTS || n_vifs > 1)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&mvm->mutex);
+ __iwl_mvm_unassign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx, true);
+ __iwl_mvm_remove_chanctx(mvm, vifs[0].old_ctx);
+
+ ret = __iwl_mvm_add_chanctx(mvm, vifs[0].new_ctx);
+ if (ret) {
+ IWL_ERR(mvm, "failed to add new_ctx during channel switch\n");
+ goto out_reassign;
+ }
+
+ ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].new_ctx,
+ true);
+ if (ret) {
+ IWL_ERR(mvm,
+ "failed to assign new_ctx during channel switch\n");
+ goto out_remove;
+ }
+
+ goto out;
+
+out_remove:
+ __iwl_mvm_remove_chanctx(mvm, vifs[0].new_ctx);
+
+out_reassign:
+ ret = __iwl_mvm_add_chanctx(mvm, vifs[0].old_ctx);
+ if (ret) {
+ IWL_ERR(mvm, "failed to add old_ctx back after failure.\n");
+ goto out_restart;
+ }
+
+ ret = __iwl_mvm_assign_vif_chanctx(mvm, vifs[0].vif, vifs[0].old_ctx,
+ true);
+ if (ret) {
+ IWL_ERR(mvm, "failed to reassign old_ctx after failure.\n");
+ goto out_restart;
+ }
+
+ goto out;
+
+out_restart:
+ /* things keep failing, better restart the hw */
+ iwl_mvm_nic_restart(mvm, false);
+
+out:
mutex_unlock(&mvm->mutex);
+ return ret;
}
static int iwl_mvm_set_tim(struct ieee80211_hw *hw,
@@ -2395,15 +2778,19 @@ static void iwl_mvm_channel_switch_beacon(struct ieee80211_hw *hw,
struct cfg80211_chan_def *chandef)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+ struct ieee80211_vif *csa_vif;
mutex_lock(&mvm->mutex);
- if (WARN(mvm->csa_vif && mvm->csa_vif->csa_active,
+
+ csa_vif = rcu_dereference_protected(mvm->csa_vif,
+ lockdep_is_held(&mvm->mutex));
+ if (WARN(csa_vif && csa_vif->csa_active,
"Another CSA is already in progress"))
goto out_unlock;
IWL_DEBUG_MAC80211(mvm, "CSA started to freq %d\n",
chandef->center_freq1);
- mvm->csa_vif = vif;
+ rcu_assign_pointer(mvm->csa_vif, vif);
out_unlock:
mutex_unlock(&mvm->mutex);
@@ -2460,6 +2847,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.sta_rc_update = iwl_mvm_sta_rc_update,
.conf_tx = iwl_mvm_mac_conf_tx,
.mgd_prepare_tx = iwl_mvm_mac_mgd_prepare_tx,
+ .mgd_protect_tdls_discover = iwl_mvm_mac_mgd_protect_tdls_discover,
.flush = iwl_mvm_mac_flush,
.sched_scan_start = iwl_mvm_mac_sched_scan_start,
.sched_scan_stop = iwl_mvm_mac_sched_scan_stop,
@@ -2472,6 +2860,7 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
.change_chanctx = iwl_mvm_change_chanctx,
.assign_vif_chanctx = iwl_mvm_assign_vif_chanctx,
.unassign_vif_chanctx = iwl_mvm_unassign_vif_chanctx,
+ .switch_vif_chanctx = iwl_mvm_switch_vif_chanctx,
.start_ap = iwl_mvm_start_ap_ibss,
.stop_ap = iwl_mvm_stop_ap_ibss,
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index fcc6c29482d0..785e5232c757 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -83,6 +83,24 @@
#define IWL_RSSI_OFFSET 50
#define IWL_MVM_MISSED_BEACONS_THRESHOLD 8
+/*
+ * The CSA NoA is scheduled IWL_MVM_CHANNEL_SWITCH_TIME TUs before "beacon 0"
+ * TBTT. This value should be big enough to ensure that we switch in time.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_TIME 40
+
+/*
+ * This value (in TUs) is used to fine tune the CSA NoA end time which should
+ * be just before "beacon 0" TBTT.
+ */
+#define IWL_MVM_CHANNEL_SWITCH_MARGIN 4
+
+/*
+ * Number of beacons to transmit on a new channel until we unblock tx to
+ * the stations, even if we didn't identify them on a new channel
+ */
+#define IWL_MVM_CS_UNBLOCK_TX_TIMEOUT 3
+
enum iwl_mvm_tx_fifo {
IWL_MVM_TX_FIFO_BK = 0,
IWL_MVM_TX_FIFO_BE,
@@ -230,11 +248,21 @@ enum iwl_mvm_ref_type {
IWL_MVM_REF_USER,
IWL_MVM_REF_TX,
IWL_MVM_REF_TX_AGG,
+ IWL_MVM_REF_ADD_IF,
IWL_MVM_REF_EXIT_WORK,
IWL_MVM_REF_COUNT,
};
+enum iwl_bt_force_ant_mode {
+ BT_FORCE_ANT_DIS = 0,
+ BT_FORCE_ANT_AUTO,
+ BT_FORCE_ANT_BT,
+ BT_FORCE_ANT_WIFI,
+
+ BT_FORCE_ANT_MAX,
+};
+
/**
* struct iwl_mvm_vif_bf_data - beacon filtering related data
* @bf_enabled: indicates if beacon filtering is enabled
@@ -523,7 +551,7 @@ struct iwl_mvm {
/* Scan status, cmd (pre-allocated) and auxiliary station */
enum iwl_scan_status scan_status;
- struct iwl_scan_cmd *scan_cmd;
+ void *scan_cmd;
struct iwl_mcast_filter_cmd *mcast_filter_cmd;
/* rx chain antennas set through debugfs for the scan command */
@@ -586,10 +614,6 @@ struct iwl_mvm {
/* -1 for always, 0 for never, >0 for that many times */
s8 restart_fw;
void *fw_error_dump;
- void *fw_error_sram;
- u32 fw_error_sram_len;
- u32 *fw_error_rxf;
- u32 fw_error_rxf_len;
#ifdef CONFIG_IWLWIFI_LEDS
struct led_classdev led;
@@ -624,11 +648,16 @@ struct iwl_mvm {
/* BT-Coex */
u8 bt_kill_msk;
+
+ struct iwl_bt_coex_profile_notif_old last_bt_notif_old;
+ struct iwl_bt_coex_ci_cmd_old last_bt_ci_cmd_old;
struct iwl_bt_coex_profile_notif last_bt_notif;
struct iwl_bt_coex_ci_cmd last_bt_ci_cmd;
+
u32 last_ant_isol;
u8 last_corun_lut;
u8 bt_tx_prio;
+ enum iwl_bt_force_ant_mode bt_force_ant_mode;
/* Thermal Throttling and CTkill */
struct iwl_mvm_tt_mgmt thermal_throttle;
@@ -647,7 +676,12 @@ struct iwl_mvm {
/* Indicate if device power save is allowed */
bool ps_disabled;
- struct ieee80211_vif *csa_vif;
+ struct ieee80211_vif __rcu *csa_vif;
+ struct ieee80211_vif __rcu *csa_tx_blocked_vif;
+ u8 csa_tx_block_bcn_timeout;
+
+ /* system time of last beacon (for AP/GO interface) */
+ u32 ap_last_beacon_gp2;
};
/* Extract MVM priv from op_mode and _hw */
@@ -719,11 +753,6 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags,
struct ieee80211_tx_rate *r);
u8 iwl_mvm_mac80211_idx_to_hwrate(int rate_idx);
void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
-void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm);
-void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm);
-#endif
u8 first_antenna(u8 mask);
u8 iwl_mvm_next_antenna(struct iwl_mvm *mvm, u8 valid, u8 last_idx);
@@ -809,6 +838,7 @@ void iwl_mvm_phy_ctxt_ref(struct iwl_mvm *mvm,
struct iwl_mvm_phy_ctxt *ctxt);
void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm,
struct iwl_mvm_phy_ctxt *ctxt);
+int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm);
/* MAC (virtual interface) programming */
int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
@@ -835,7 +865,8 @@ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
/* Quota management */
-int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif);
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+ struct ieee80211_vif *disabled_vif);
/* Scanning */
int iwl_mvm_scan_request(struct iwl_mvm *mvm,
@@ -854,15 +885,24 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies);
+ struct ieee80211_scan_ies *ies);
int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req);
int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req);
-int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify);
-int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb,
- struct iwl_device_cmd *cmd);
+int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify);
+int iwl_mvm_rx_scan_offload_results(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+
+/* Unified scan */
+int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *req);
+int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies);
/* MVM debugfs */
#ifdef CONFIG_IWLWIFI_DEBUGFS
@@ -963,11 +1003,30 @@ u16 iwl_mvm_coex_agg_time_limit(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
bool iwl_mvm_bt_coex_is_mimo_allowed(struct iwl_mvm *mvm,
struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_shared_ant_avail(struct iwl_mvm *mvm);
bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
enum ieee80211_band band);
u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
struct ieee80211_tx_info *info, u8 ac);
+bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
+void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
+int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
+int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ enum ieee80211_rssi_event rssi_event);
+u16 iwl_mvm_coex_agg_time_limit_old(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta);
+bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
+ enum ieee80211_band band);
+int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
+ struct iwl_rx_cmd_buffer *rxb,
+ struct iwl_device_cmd *cmd);
+
enum iwl_bt_kill_msk {
BT_KILL_MSK_DEFAULT,
BT_KILL_MSK_SCO_HID_A2DP,
@@ -1039,4 +1098,9 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state);
int iwl_mvm_sf_update(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
bool added_vif);
+/* TDLS */
+int iwl_mvm_tdls_sta_count(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+
+void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
+
#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c
index 808f78f6fbf9..b04805ccb443 100644
--- a/drivers/net/wireless/iwlwifi/mvm/nvm.c
+++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c
@@ -69,7 +69,9 @@
/* Default NVM size to read */
#define IWL_NVM_DEFAULT_CHUNK_SIZE (2*1024)
-#define IWL_MAX_NVM_SECTION_SIZE 7000
+#define IWL_MAX_NVM_SECTION_SIZE 0x1b58
+#define IWL_MAX_NVM_8000A_SECTION_SIZE 0xffc
+#define IWL_MAX_NVM_8000B_SECTION_SIZE 0x1ffc
#define NVM_WRITE_OPCODE 1
#define NVM_READ_OPCODE 0
@@ -219,7 +221,7 @@ static int iwl_nvm_write_section(struct iwl_mvm *mvm, u16 section,
* without overflowing, so no check is needed.
*/
static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
- u8 *data)
+ u8 *data, u32 size_read)
{
u16 length, offset = 0;
int ret;
@@ -231,6 +233,13 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section,
/* Read the NVM until exhausted (reading less than requested) */
while (ret == length) {
+ /* Check no memory assumptions fail and cause an overflow */
+ if ((size_read + offset + length) >
+ mvm->cfg->base_params->eeprom_size) {
+ IWL_ERR(mvm, "EEPROM size is too small for NVM\n");
+ return -ENOBUFS;
+ }
+
ret = iwl_nvm_read_chunk(mvm, section, offset, length, data);
if (ret < 0) {
IWL_DEBUG_EEPROM(mvm->trans->dev,
@@ -326,6 +335,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
u8 data[];
} *file_sec;
const u8 *eof, *temp;
+ int max_section_size;
#define NVM_WORD1_LEN(x) (8 * (x & 0x03FF))
#define NVM_WORD2_ID(x) (x >> 12)
@@ -334,6 +344,14 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from external NVM\n");
+ /* Maximal size depends on HW family and step */
+ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ max_section_size = IWL_MAX_NVM_SECTION_SIZE;
+ else if ((mvm->trans->hw_rev & 0xc) == 0) /* Family 8000 A-step */
+ max_section_size = IWL_MAX_NVM_8000A_SECTION_SIZE;
+ else /* Family 8000 B-step */
+ max_section_size = IWL_MAX_NVM_8000B_SECTION_SIZE;
+
/*
* Obtain NVM image via request_firmware. Since we already used
* request_firmware_nowait() for the firmware binary load and only
@@ -392,7 +410,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm)
le16_to_cpu(file_sec->word1));
}
- if (section_size > IWL_MAX_NVM_SECTION_SIZE) {
+ if (section_size > max_section_size) {
IWL_ERR(mvm, "ERROR - section too large (%d)\n",
section_size);
ret = -EINVAL;
@@ -459,6 +477,7 @@ int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm)
int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
{
int ret, section;
+ u32 size_read = 0;
u8 *nvm_buffer, *temp;
if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS))
@@ -475,9 +494,11 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
return -ENOMEM;
for (section = 0; section < NVM_MAX_NUM_SECTIONS; section++) {
/* we override the constness for initial read */
- ret = iwl_nvm_read_section(mvm, section, nvm_buffer);
+ ret = iwl_nvm_read_section(mvm, section, nvm_buffer,
+ size_read);
if (ret < 0)
continue;
+ size_read += ret;
temp = kmemdup(nvm_buffer, ret, GFP_KERNEL);
if (!temp) {
ret = -ENOMEM;
@@ -509,6 +530,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic)
}
#endif
}
+ if (!size_read)
+ IWL_ERR(mvm, "OTP is blank\n");
kfree(nvm_buffer);
}
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index cc2f7de396de..7d7b2fbe7cd1 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -166,8 +166,15 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode)
WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) &
~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE);
- /* silicon bits */
- reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
+ /*
+ * TODO: Bits 7-8 of CSR in 8000 HW family set the ADC sampling, and
+ * shouldn't be set to any non-zero value. The same is supposed to be
+ * true of the other HW, but unsetting them (such as the 7260) causes
+ * automatic tests to fail on seemingly unrelated errors. Need to
+ * further investigate this, but for now we'll separate cases.
+ */
+ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000)
+ reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI;
iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG,
CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH |
@@ -233,7 +240,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true),
RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
iwl_mvm_rx_scan_offload_complete_notif, true),
- RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_sched_scan_results,
+ RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results,
false),
RX_HANDLER(RADIO_VERSION_NOTIFICATION, iwl_mvm_rx_radio_ver, false),
@@ -284,6 +291,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(SCAN_OFFLOAD_ABORT_CMD),
CMD(SCAN_OFFLOAD_COMPLETE),
CMD(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),
+ CMD(SCAN_ITERATION_COMPLETE),
CMD(POWER_TABLE_CMD),
CMD(WEP_KEY),
CMD(REPLY_RX_PHY_CMD),
@@ -324,6 +332,9 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(REPLY_THERMAL_MNG_BACKOFF),
CMD(MAC_PM_POWER_TABLE),
CMD(BT_COEX_CI),
+ CMD(BT_COEX_UPDATE_SW_BOOST),
+ CMD(BT_COEX_UPDATE_CORUN_LUT),
+ CMD(BT_COEX_UPDATE_REDUCED_TXP),
CMD(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),
CMD(ANTENNA_COUPLING_NOTIFICATION),
};
@@ -502,9 +513,17 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
}
}
- scan_size = sizeof(struct iwl_scan_cmd) +
- mvm->fw->ucode_capa.max_probe_length +
- (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel));
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+ scan_size = sizeof(struct iwl_scan_req_unified_lmac) +
+ sizeof(struct iwl_scan_channel_cfg_lmac) *
+ mvm->fw->ucode_capa.n_scan_channels +
+ sizeof(struct iwl_scan_probe_req);
+ else
+ scan_size = sizeof(struct iwl_scan_cmd) +
+ mvm->fw->ucode_capa.max_probe_length +
+ mvm->fw->ucode_capa.n_scan_channels *
+ sizeof(struct iwl_scan_channel);
+
mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL);
if (!mvm->scan_cmd)
goto out_free;
@@ -549,8 +568,6 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode)
kfree(mvm->scan_cmd);
vfree(mvm->fw_error_dump);
- kfree(mvm->fw_error_sram);
- kfree(mvm->fw_error_rxf);
kfree(mvm->mcast_filter_cmd);
mvm->mcast_filter_cmd = NULL;
@@ -754,7 +771,7 @@ static void iwl_mvm_reprobe_wk(struct work_struct *wk)
module_put(THIS_MODULE);
}
-static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
+void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
{
iwl_abort_notification_waits(&mvm->notif_wait);
@@ -811,93 +828,24 @@ static void iwl_mvm_nic_restart(struct iwl_mvm *mvm)
reprobe->dev = mvm->trans->dev;
INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk);
schedule_work(&reprobe->work);
- } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && mvm->restart_fw) {
+ } else if (mvm->cur_ucode == IWL_UCODE_REGULAR &&
+ (!fw_error || mvm->restart_fw)) {
/* don't let the transport/FW power down */
iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN);
- if (mvm->restart_fw > 0)
+ if (fw_error && mvm->restart_fw > 0)
mvm->restart_fw--;
ieee80211_restart_hw(mvm->hw);
}
}
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
-{
- struct iwl_fw_error_dump_file *dump_file;
- struct iwl_fw_error_dump_data *dump_data;
- u32 file_len;
- u32 trans_len;
-
- lockdep_assert_held(&mvm->mutex);
-
- if (mvm->fw_error_dump)
- return;
-
- file_len = mvm->fw_error_sram_len +
- mvm->fw_error_rxf_len +
- sizeof(*dump_file) +
- sizeof(*dump_data) * 2;
-
- trans_len = iwl_trans_dump_data(mvm->trans, NULL, 0);
- if (trans_len)
- file_len += trans_len;
-
- dump_file = vmalloc(file_len);
- if (!dump_file)
- return;
-
- mvm->fw_error_dump = dump_file;
-
- dump_file->barker = cpu_to_le32(IWL_FW_ERROR_DUMP_BARKER);
- dump_file->file_len = cpu_to_le32(file_len);
- dump_data = (void *)dump_file->data;
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF);
- dump_data->len = cpu_to_le32(mvm->fw_error_rxf_len);
- memcpy(dump_data->data, mvm->fw_error_rxf, mvm->fw_error_rxf_len);
-
- dump_data = iwl_mvm_fw_error_next_data(dump_data);
- dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM);
- dump_data->len = cpu_to_le32(mvm->fw_error_sram_len);
-
- /*
- * No need for lock since at the stage the FW isn't loaded. So it
- * can't assert - we are the only one who can possibly be accessing
- * mvm->fw_error_sram right now.
- */
- memcpy(dump_data->data, mvm->fw_error_sram, mvm->fw_error_sram_len);
-
- kfree(mvm->fw_error_rxf);
- mvm->fw_error_rxf = NULL;
- mvm->fw_error_rxf_len = 0;
-
- kfree(mvm->fw_error_sram);
- mvm->fw_error_sram = NULL;
- mvm->fw_error_sram_len = 0;
-
- if (trans_len) {
- void *buf = iwl_mvm_fw_error_next_data(dump_data);
- u32 real_trans_len = iwl_trans_dump_data(mvm->trans, buf,
- trans_len);
- dump_data = (void *)((u8 *)buf + real_trans_len);
- dump_file->file_len =
- cpu_to_le32(file_len - trans_len + real_trans_len);
- }
-}
-#endif
-
static void iwl_mvm_nic_error(struct iwl_op_mode *op_mode)
{
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
iwl_mvm_dump_nic_error_log(mvm);
-#ifdef CONFIG_IWLWIFI_DEBUGFS
- iwl_mvm_fw_error_sram_dump(mvm);
- iwl_mvm_fw_error_rxf_dump(mvm);
-#endif
-
- iwl_mvm_nic_restart(mvm);
+ iwl_mvm_nic_restart(mvm, true);
}
static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
@@ -905,7 +853,7 @@ static void iwl_mvm_cmd_queue_full(struct iwl_op_mode *op_mode)
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
WARN_ON(1);
- iwl_mvm_nic_restart(mvm);
+ iwl_mvm_nic_restart(mvm, true);
}
struct iwl_d0i3_iter_data {
diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
index 539f3a942d43..6cc243f7cf60 100644
--- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
+++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c
@@ -261,3 +261,29 @@ void iwl_mvm_phy_ctxt_unref(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt)
ctxt->ref--;
}
+
+static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ unsigned long *data = _data;
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+ if (!mvmvif->phy_ctxt)
+ return;
+
+ if (vif->type == NL80211_IFTYPE_STATION ||
+ vif->type == NL80211_IFTYPE_AP)
+ __set_bit(mvmvif->phy_ctxt->id, data);
+}
+
+int iwl_mvm_phy_ctx_count(struct iwl_mvm *mvm)
+{
+ unsigned long phy_ctxt_counter = 0;
+
+ ieee80211_iterate_active_interfaces_atomic(mvm->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ iwl_mvm_binding_iterator,
+ &phy_ctxt_counter);
+
+ return hweight8(phy_ctxt_counter);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/power.c b/drivers/net/wireless/iwlwifi/mvm/power.c
index c182a8baf685..2b2d10800a55 100644
--- a/drivers/net/wireless/iwlwifi/mvm/power.c
+++ b/drivers/net/wireless/iwlwifi/mvm/power.c
@@ -246,30 +246,10 @@ static void iwl_mvm_power_configure_uapsd(struct iwl_mvm *mvm,
IWL_MVM_PS_HEAVY_RX_THLD_PERCENT;
}
-static void iwl_mvm_binding_iterator(void *_data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- unsigned long *data = _data;
- struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
-
- if (!mvmvif->phy_ctxt)
- return;
-
- if (vif->type == NL80211_IFTYPE_STATION ||
- vif->type == NL80211_IFTYPE_AP)
- __set_bit(mvmvif->phy_ctxt->id, data);
-}
-
static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
struct ieee80211_vif *vif)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
- unsigned long phy_ctxt_counter = 0;
-
- ieee80211_iterate_active_interfaces_atomic(mvm->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- iwl_mvm_binding_iterator,
- &phy_ctxt_counter);
if (!memcmp(mvmvif->uapsd_misbehaving_bssid, vif->bss_conf.bssid,
ETH_ALEN))
@@ -291,7 +271,7 @@ static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm,
* Avoid using uAPSD if client is in DCM -
* low latency issue in Miracast
*/
- if (hweight8(phy_ctxt_counter) >= 2)
+ if (iwl_mvm_phy_ctx_count(mvm) >= 2)
return false;
return true;
@@ -503,6 +483,7 @@ int iwl_mvm_power_uapsd_misbehaving_ap_notif(struct iwl_mvm *mvm,
}
struct iwl_power_vifs {
+ struct iwl_mvm *mvm;
struct ieee80211_vif *bf_vif;
struct ieee80211_vif *bss_vif;
struct ieee80211_vif *p2p_vif;
@@ -512,6 +493,8 @@ struct iwl_power_vifs {
bool bss_active;
bool ap_active;
bool monitor_active;
+ bool bss_tdls;
+ bool p2p_tdls;
};
static void iwl_mvm_power_iterator(void *_data, u8 *mac,
@@ -548,6 +531,8 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac,
/* only a single MAC of the same type */
WARN_ON(power_iterator->p2p_vif);
power_iterator->p2p_vif = vif;
+ power_iterator->p2p_tdls =
+ !!iwl_mvm_tdls_sta_count(power_iterator->mvm, vif);
if (mvmvif->phy_ctxt)
if (mvmvif->phy_ctxt->id < MAX_PHYS)
power_iterator->p2p_active = true;
@@ -557,6 +542,8 @@ static void iwl_mvm_power_iterator(void *_data, u8 *mac,
/* only a single MAC of the same type */
WARN_ON(power_iterator->bss_vif);
power_iterator->bss_vif = vif;
+ power_iterator->bss_tdls =
+ !!iwl_mvm_tdls_sta_count(power_iterator->mvm, vif);
if (mvmvif->phy_ctxt)
if (mvmvif->phy_ctxt->id < MAX_PHYS)
power_iterator->bss_active = true;
@@ -599,13 +586,15 @@ iwl_mvm_power_set_pm(struct iwl_mvm *mvm,
ap_mvmvif = iwl_mvm_vif_from_mac80211(vifs->ap_vif);
/* enable PM on bss if bss stand alone */
- if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active) {
+ if (vifs->bss_active && !vifs->p2p_active && !vifs->ap_active &&
+ !vifs->bss_tdls) {
bss_mvmvif->pm_enabled = true;
return;
}
/* enable PM on p2p if p2p stand alone */
- if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active) {
+ if (vifs->p2p_active && !vifs->bss_active && !vifs->ap_active &&
+ !vifs->p2p_tdls) {
if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_P2P_PM)
p2p_mvmvif->pm_enabled = true;
return;
@@ -831,7 +820,9 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
int iwl_mvm_power_update_mac(struct iwl_mvm *mvm)
{
struct iwl_mvm_vif *mvmvif;
- struct iwl_power_vifs vifs = {};
+ struct iwl_power_vifs vifs = {
+ .mvm = mvm,
+ };
bool ba_enable;
int ret;
diff --git a/drivers/net/wireless/iwlwifi/mvm/quota.c b/drivers/net/wireless/iwlwifi/mvm/quota.c
index ba68d7b84505..4e20b3ce2b6a 100644
--- a/drivers/net/wireless/iwlwifi/mvm/quota.c
+++ b/drivers/net/wireless/iwlwifi/mvm/quota.c
@@ -73,7 +73,7 @@ struct iwl_mvm_quota_iterator_data {
int colors[MAX_BINDINGS];
int low_latency[MAX_BINDINGS];
int n_low_latency_bindings;
- struct ieee80211_vif *new_vif;
+ struct ieee80211_vif *disabled_vif;
};
static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
@@ -83,13 +83,8 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
u16 id;
- /*
- * We'll account for the new interface (if any) below,
- * skip it here in case we're not called from within
- * the add_interface callback (otherwise it won't show
- * up in iteration)
- */
- if (vif == data->new_vif)
+ /* skip disabled interfaces here immediately */
+ if (vif == data->disabled_vif)
return;
if (!mvmvif->phy_ctxt)
@@ -104,11 +99,6 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
if (WARN_ON_ONCE(id >= MAX_BINDINGS))
return;
- if (data->colors[id] < 0)
- data->colors[id] = mvmvif->phy_ctxt->color;
- else
- WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
-
switch (vif->type) {
case NL80211_IFTYPE_STATION:
if (vif->bss_conf.assoc)
@@ -130,6 +120,11 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac,
return;
}
+ if (data->colors[id] < 0)
+ data->colors[id] = mvmvif->phy_ctxt->color;
+ else
+ WARN_ON_ONCE(data->colors[id] != mvmvif->phy_ctxt->color);
+
data->n_interfaces[id]++;
if (iwl_mvm_vif_low_latency(mvmvif) && !data->low_latency[id]) {
@@ -171,14 +166,15 @@ static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm,
#endif
}
-int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
+int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
+ struct ieee80211_vif *disabled_vif)
{
struct iwl_time_quota_cmd cmd = {};
int i, idx, ret, num_active_macs, quota, quota_rem, n_non_lowlat;
struct iwl_mvm_quota_iterator_data data = {
.n_interfaces = {},
.colors = { -1, -1, -1, -1 },
- .new_vif = newvif,
+ .disabled_vif = disabled_vif,
};
lockdep_assert_held(&mvm->mutex);
@@ -193,10 +189,6 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_quota_iterator, &data);
- if (newvif) {
- data.new_vif = NULL;
- iwl_mvm_quota_iterator(&data, newvif->addr, newvif);
- }
/*
* The FW's scheduling session consists of
@@ -285,6 +277,14 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, struct ieee80211_vif *newvif)
iwl_mvm_adjust_quota_for_noa(mvm, &cmd);
+ /* check that we have non-zero quota for all valid bindings */
+ for (i = 0; i < MAX_BINDINGS; i++) {
+ if (cmd.quotas[i].id_and_color == cpu_to_le32(FW_CTXT_INVALID))
+ continue;
+ WARN_ONCE(cmd.quotas[i].quota == 0,
+ "zero quota on binding %d\n", i);
+ }
+
ret = iwl_mvm_send_cmd_pdu(mvm, TIME_QUOTA_CMD, 0,
sizeof(cmd), &cmd);
if (ret)
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c
index 306a6caa4868..c70e959bf0e3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.c
@@ -927,7 +927,7 @@ static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
u8 low;
u16 high_low;
u16 rate_mask;
- struct iwl_mvm *mvm = lq_sta->drv;
+ struct iwl_mvm *mvm = lq_sta->pers.drv;
rate_mask = rs_get_supported_rates(lq_sta, rate);
high_low = rs_get_adjacent_rate(mvm, rate->index, rate_mask,
@@ -946,7 +946,7 @@ static bool rs_get_lower_rate_in_column(struct iwl_lq_sta *lq_sta,
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;
+ struct iwl_mvm *mvm = lq_sta->pers.drv;
if (is_legacy(rate)) {
/* No column to downgrade from Legacy */
@@ -1026,14 +1026,14 @@ static void rs_tx_status(void *mvm_r, struct ieee80211_supported_band *sband,
if (!lq_sta) {
IWL_DEBUG_RATE(mvm, "Station rate scaling not created yet.\n");
return;
- } else if (!lq_sta->drv) {
+ } else if (!lq_sta->pers.drv) {
IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
return;
}
#ifdef CONFIG_MAC80211_DEBUGFS
/* Disable last tx check if we are debugging with fixed rate */
- if (lq_sta->dbg_fixed_rate) {
+ if (lq_sta->pers.dbg_fixed_rate) {
IWL_DEBUG_RATE(mvm, "Fixed rate. avoid rate scaling\n");
return;
}
@@ -1405,7 +1405,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search)
int flush_interval_passed = 0;
struct iwl_mvm *mvm;
- mvm = lq_sta->drv;
+ mvm = lq_sta->pers.drv;
active_tbl = lq_sta->active_tbl;
tbl = &(lq_sta->lq_info[active_tbl]);
@@ -1865,11 +1865,11 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm,
int weak_tpt = IWL_INVALID_VALUE, strong_tpt = IWL_INVALID_VALUE;
#ifdef CONFIG_MAC80211_DEBUGFS
- if (lq_sta->dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
+ if (lq_sta->pers.dbg_fixed_txp_reduction <= TPC_MAX_REDUCTION) {
IWL_DEBUG_RATE(mvm, "fixed tpc: %d\n",
- lq_sta->dbg_fixed_txp_reduction);
- lq_sta->lq.reduced_tpc = lq_sta->dbg_fixed_txp_reduction;
- return cur != lq_sta->dbg_fixed_txp_reduction;
+ lq_sta->pers.dbg_fixed_txp_reduction);
+ lq_sta->lq.reduced_tpc = lq_sta->pers.dbg_fixed_txp_reduction;
+ return cur != lq_sta->pers.dbg_fixed_txp_reduction;
}
#endif
@@ -2382,7 +2382,7 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta,
}
/* Treat uninitialized rate scaling data same as non-existing. */
- if (lq_sta && !lq_sta->drv) {
+ if (lq_sta && !lq_sta->pers.drv) {
IWL_DEBUG_RATE(mvm, "Rate scaling not initialized yet.\n");
mvm_sta = NULL;
}
@@ -2401,12 +2401,18 @@ static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta,
gfp_t gfp)
{
struct iwl_mvm_sta *sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
- struct iwl_op_mode *op_mode __maybe_unused =
- (struct iwl_op_mode *)mvm_rate;
- struct iwl_mvm *mvm __maybe_unused = IWL_OP_MODE_GET_MVM(op_mode);
+ struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate;
+ struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
+ struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta;
IWL_DEBUG_RATE(mvm, "create station rate scale window\n");
+ lq_sta->pers.drv = mvm;
+#ifdef CONFIG_MAC80211_DEBUGFS
+ lq_sta->pers.dbg_fixed_rate = 0;
+ lq_sta->pers.dbg_fixed_txp_reduction = TPC_INVALID;
+#endif
+
return &sta_priv->lq_sta;
}
@@ -2552,7 +2558,9 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
sta_priv = (struct iwl_mvm_sta *)sta->drv_priv;
lq_sta = &sta_priv->lq_sta;
- memset(lq_sta, 0, sizeof(*lq_sta));
+
+ /* clear all non-persistent lq data */
+ memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers));
sband = hw->wiphy->bands[band];
@@ -2630,17 +2638,12 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
/* as default allow aggregation for all tids */
lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID;
- lq_sta->drv = mvm;
/* Set last_txrate_idx to lowest rate */
lq_sta->last_txrate_idx = rate_lowest_index(sband, sta);
if (sband->band == IEEE80211_BAND_5GHZ)
lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE;
lq_sta->is_agg = 0;
-#ifdef CONFIG_MAC80211_DEBUGFS
- lq_sta->dbg_fixed_rate = 0;
- lq_sta->dbg_fixed_txp_reduction = TPC_INVALID;
-#endif
#ifdef CONFIG_IWLWIFI_DEBUGFS
iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats);
#endif
@@ -2811,12 +2814,12 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm,
u8 ant = initial_rate->ant;
#ifdef CONFIG_MAC80211_DEBUGFS
- if (lq_sta->dbg_fixed_rate) {
+ if (lq_sta->pers.dbg_fixed_rate) {
rs_build_rates_table_from_fixed(mvm, lq_cmd,
lq_sta->band,
- lq_sta->dbg_fixed_rate);
+ lq_sta->pers.dbg_fixed_rate);
lq_cmd->reduced_tpc = 0;
- ant = (lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
+ ant = (lq_sta->pers.dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) >>
RATE_MCS_ANT_POS;
} else
#endif
@@ -2926,14 +2929,14 @@ static void rs_program_fix_rate(struct iwl_mvm *mvm,
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);
+ lq_sta->lq.sta_id, lq_sta->pers.dbg_fixed_rate);
- if (lq_sta->dbg_fixed_rate) {
+ if (lq_sta->pers.dbg_fixed_rate) {
struct rs_rate rate;
- rs_rate_from_ucode_rate(lq_sta->dbg_fixed_rate,
+ rs_rate_from_ucode_rate(lq_sta->pers.dbg_fixed_rate,
lq_sta->band, &rate);
rs_fill_lq_cmd(mvm, NULL, lq_sta, &rate);
- iwl_mvm_send_lq_cmd(lq_sta->drv, &lq_sta->lq, false);
+ iwl_mvm_send_lq_cmd(lq_sta->pers.drv, &lq_sta->lq, false);
}
}
@@ -2946,16 +2949,16 @@ static ssize_t rs_sta_dbgfs_scale_table_write(struct file *file,
size_t buf_size;
u32 parsed_rate;
- mvm = lq_sta->drv;
+ mvm = lq_sta->pers.drv;
memset(buf, 0, sizeof(buf));
buf_size = min(count, sizeof(buf) - 1);
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
if (sscanf(buf, "%x", &parsed_rate) == 1)
- lq_sta->dbg_fixed_rate = parsed_rate;
+ lq_sta->pers.dbg_fixed_rate = parsed_rate;
else
- lq_sta->dbg_fixed_rate = 0;
+ lq_sta->pers.dbg_fixed_rate = 0;
rs_program_fix_rate(mvm, lq_sta);
@@ -2974,7 +2977,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
struct iwl_mvm *mvm;
struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]);
struct rs_rate *rate = &tbl->rate;
- mvm = lq_sta->drv;
+ mvm = lq_sta->pers.drv;
buff = kmalloc(2048, GFP_KERNEL);
if (!buff)
return -ENOMEM;
@@ -2984,7 +2987,7 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file,
lq_sta->total_failed, lq_sta->total_success,
lq_sta->active_legacy_rate);
desc += sprintf(buff+desc, "fixed rate 0x%X\n",
- lq_sta->dbg_fixed_rate);
+ lq_sta->pers.dbg_fixed_rate);
desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n",
(mvm->fw->valid_tx_ant & ANT_A) ? "ANT_A," : "",
(mvm->fw->valid_tx_ant & ANT_B) ? "ANT_B," : "",
@@ -3182,31 +3185,20 @@ static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = {
static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir)
{
struct iwl_lq_sta *lq_sta = mvm_sta;
- lq_sta->rs_sta_dbgfs_scale_table_file =
- debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
- lq_sta, &rs_sta_dbgfs_scale_table_ops);
- lq_sta->rs_sta_dbgfs_stats_table_file =
- debugfs_create_file("rate_stats_table", S_IRUSR, dir,
- lq_sta, &rs_sta_dbgfs_stats_table_ops);
- lq_sta->rs_sta_dbgfs_drv_tx_stats_file =
- debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
- lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
- lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file =
- debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
- &lq_sta->tx_agg_tid_en);
- lq_sta->rs_sta_dbgfs_reduced_txp_file =
- debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
- &lq_sta->dbg_fixed_txp_reduction);
+ debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir,
+ lq_sta, &rs_sta_dbgfs_scale_table_ops);
+ debugfs_create_file("rate_stats_table", S_IRUSR, dir,
+ lq_sta, &rs_sta_dbgfs_stats_table_ops);
+ debugfs_create_file("drv_tx_stats", S_IRUSR | S_IWUSR, dir,
+ lq_sta, &rs_sta_dbgfs_drv_tx_stats_ops);
+ debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir,
+ &lq_sta->tx_agg_tid_en);
+ debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir,
+ &lq_sta->pers.dbg_fixed_txp_reduction);
}
static void rs_remove_debugfs(void *mvm, void *mvm_sta)
{
- struct iwl_lq_sta *lq_sta = mvm_sta;
- debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file);
- debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file);
- debugfs_remove(lq_sta->rs_sta_dbgfs_drv_tx_stats_file);
- debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file);
- debugfs_remove(lq_sta->rs_sta_dbgfs_reduced_txp_file);
}
#endif
diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h
index 374a83d7db25..f27b9d687a25 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rs.h
+++ b/drivers/net/wireless/iwlwifi/mvm/rs.h
@@ -349,16 +349,6 @@ struct iwl_lq_sta {
struct iwl_lq_cmd lq;
struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */
u8 tx_agg_tid_en;
-#ifdef CONFIG_MAC80211_DEBUGFS
- struct dentry *rs_sta_dbgfs_scale_table_file;
- struct dentry *rs_sta_dbgfs_stats_table_file;
- struct dentry *rs_sta_dbgfs_drv_tx_stats_file;
- struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file;
- struct dentry *rs_sta_dbgfs_reduced_txp_file;
- u32 dbg_fixed_rate;
- u8 dbg_fixed_txp_reduction;
-#endif
- struct iwl_mvm *drv;
/* used to be in sta_info */
int last_txrate_idx;
@@ -369,6 +359,15 @@ struct iwl_lq_sta {
/* tx power reduce for this sta */
int tpc_reduce;
+
+ /* persistent fields - initialized only once - keep last! */
+ struct {
+#ifdef CONFIG_MAC80211_DEBUGFS
+ u32 dbg_fixed_rate;
+ u8 dbg_fixed_txp_reduction;
+#endif
+ struct iwl_mvm *drv;
+ } pers;
};
/* Initialize station's rate scaling information after adding station */
diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c
index cf7276967acd..4b98987fc413 100644
--- a/drivers/net/wireless/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/rx.c
@@ -259,6 +259,23 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
memset(&rx_status, 0, sizeof(rx_status));
/*
+ * We have tx blocked stations (with CS bit). If we heard frames from
+ * a blocked station on a new channel we can TX to it again.
+ */
+ if (unlikely(mvm->csa_tx_block_bcn_timeout)) {
+ struct ieee80211_sta *sta;
+
+ rcu_read_lock();
+
+ sta = ieee80211_find_sta(
+ rcu_dereference(mvm->csa_tx_blocked_vif), hdr->addr2);
+ if (sta)
+ iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false);
+
+ rcu_read_unlock();
+ }
+
+ /*
* drop the packet if it has failed being decrypted by HW
*/
if (iwl_mvm_set_mac80211_rx_flag(mvm, hdr, &rx_status, rx_pkt_status)) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c
index 4b6c7d4bd199..004b1f5d0314 100644
--- a/drivers/net/wireless/iwlwifi/mvm/scan.c
+++ b/drivers/net/wireless/iwlwifi/mvm/scan.c
@@ -97,10 +97,9 @@ static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm)
return cpu_to_le16(rx_chain);
}
-static inline __le32
-iwl_mvm_scan_rxon_flags(struct cfg80211_scan_request *req)
+static __le32 iwl_mvm_scan_rxon_flags(enum ieee80211_band band)
{
- if (req->channels[0]->band == IEEE80211_BAND_2GHZ)
+ if (band == IEEE80211_BAND_2GHZ)
return cpu_to_le32(PHY_BAND_24);
else
return cpu_to_le32(PHY_BAND_5);
@@ -130,19 +129,19 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band,
* request list, is not copied here, but inserted directly to the probe
* request.
*/
-static void iwl_mvm_scan_fill_ssids(struct iwl_scan_cmd *cmd,
- struct cfg80211_scan_request *req,
- int first)
+static void iwl_mvm_scan_fill_ssids(struct iwl_ssid_ie *cmd_ssid,
+ struct cfg80211_ssid *ssids,
+ int n_ssids, int first)
{
int fw_idx, req_idx;
- for (req_idx = req->n_ssids - 1, fw_idx = 0; req_idx >= first;
+ for (req_idx = n_ssids - 1, fw_idx = 0; req_idx >= first;
req_idx--, fw_idx++) {
- cmd->direct_scan[fw_idx].id = WLAN_EID_SSID;
- cmd->direct_scan[fw_idx].len = req->ssids[req_idx].ssid_len;
- memcpy(cmd->direct_scan[fw_idx].ssid,
- req->ssids[req_idx].ssid,
- req->ssids[req_idx].ssid_len);
+ cmd_ssid[fw_idx].id = WLAN_EID_SSID;
+ cmd_ssid[fw_idx].len = ssids[req_idx].ssid_len;
+ memcpy(cmd_ssid[fw_idx].ssid,
+ ssids[req_idx].ssid,
+ ssids[req_idx].ssid_len);
}
}
@@ -204,7 +203,8 @@ static void iwl_mvm_scan_fill_channels(struct iwl_scan_cmd *cmd,
*/
static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
int n_ssids, const u8 *ssid, int ssid_len,
- const u8 *ie, int ie_len,
+ const u8 *band_ie, int band_ie_len,
+ const u8 *common_ie, int common_ie_len,
int left)
{
int len = 0;
@@ -244,12 +244,19 @@ static u16 iwl_mvm_fill_probe_req(struct ieee80211_mgmt *frame, const u8 *ta,
len += ssid_len + 2;
- if (WARN_ON(left < ie_len))
+ if (WARN_ON(left < band_ie_len + common_ie_len))
return len;
- if (ie && ie_len) {
- memcpy(pos, ie, ie_len);
- len += ie_len;
+ if (band_ie && band_ie_len) {
+ memcpy(pos, band_ie, band_ie_len);
+ pos += band_ie_len;
+ len += band_ie_len;
+ }
+
+ if (common_ie && common_ie_len) {
+ memcpy(pos, common_ie, common_ie_len);
+ pos += common_ie_len;
+ len += common_ie_len;
}
return (u16)len;
@@ -267,7 +274,7 @@ static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac,
static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- int n_ssids,
+ int n_ssids, u32 flags,
struct iwl_mvm_scan_params *params)
{
bool global_bound = false;
@@ -289,6 +296,9 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm,
params->max_out_time = 250;
}
+ if (flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
+ params->max_out_time = 200;
+
not_bound:
for (band = IEEE80211_BAND_2GHZ; band < IEEE80211_NUM_BANDS; band++) {
@@ -325,22 +335,20 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
IWL_DEBUG_SCAN(mvm, "Handling mac80211 scan request\n");
mvm->scan_status = IWL_MVM_SCAN_OS;
- memset(cmd, 0, sizeof(struct iwl_scan_cmd) +
- mvm->fw->ucode_capa.max_probe_length +
- (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel)));
+ memset(cmd, 0, ksize(cmd));
cmd->channel_count = (u8)req->n_channels;
cmd->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME);
cmd->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
cmd->rxchain_sel_flags = iwl_mvm_scan_rx_chain(mvm);
- iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, &params);
+ iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, req->flags, &params);
cmd->max_out_time = cpu_to_le32(params.max_out_time);
cmd->suspend_time = cpu_to_le32(params.suspend_time);
if (params.passive_fragmented)
cmd->scan_flags |= SCAN_FLAGS_FRAGMENTED_SCAN;
- cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req);
+ cmd->rxon_flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band);
cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
MAC_FILTER_IN_BEACON);
@@ -367,7 +375,8 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
cmd->scan_flags &= ~SCAN_FLAGS_PASSIVE2ACTIVE;
}
- iwl_mvm_scan_fill_ssids(cmd, req, basic_ssid ? 1 : 0);
+ iwl_mvm_scan_fill_ssids(cmd->direct_scan, req->ssids, req->n_ssids,
+ basic_ssid ? 1 : 0);
cmd->tx_cmd.tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
TX_CMD_FLG_BT_DIS);
@@ -382,7 +391,7 @@ int iwl_mvm_scan_request(struct iwl_mvm *mvm,
(struct ieee80211_mgmt *)cmd->data,
vif->addr,
req->n_ssids, ssid, ssid_len,
- req->ie, req->ie_len,
+ req->ie, req->ie_len, NULL, 0,
mvm->fw->ucode_capa.max_probe_length));
iwl_mvm_scan_fill_channels(cmd, req, basic_ssid, &params);
@@ -441,16 +450,27 @@ int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
return 0;
}
-int iwl_mvm_rx_sched_scan_results(struct iwl_mvm *mvm,
- struct iwl_rx_cmd_buffer *rxb,
- struct iwl_device_cmd *cmd)
+int iwl_mvm_rx_scan_offload_results(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_sched_scan_results *notif = (void *)pkt->data;
+ u8 client_bitmap = 0;
- if (notif->client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
- IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
- ieee80211_sched_scan_results(mvm->hw);
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)) {
+ struct iwl_sched_scan_results *notif = (void *)pkt->data;
+
+ client_bitmap = notif->client_bitmap;
+ }
+
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
+ client_bitmap & SCAN_CLIENT_SCHED_SCAN) {
+ if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
+ IWL_DEBUG_SCAN(mvm, "Scheduled scan results\n");
+ ieee80211_sched_scan_results(mvm->hw);
+ } else {
+ IWL_DEBUG_SCAN(mvm, "Scan results\n");
+ }
}
return 0;
@@ -494,7 +514,7 @@ static bool iwl_mvm_scan_abort_notif(struct iwl_notif_wait_data *notif_wait,
};
}
-int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
+static int iwl_mvm_cancel_regular_scan(struct iwl_mvm *mvm)
{
struct iwl_notification_wait wait_scan_abort;
static const u8 scan_abort_notif[] = { SCAN_ABORT_CMD,
@@ -535,33 +555,52 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
struct iwl_device_cmd *cmd)
{
struct iwl_rx_packet *pkt = rxb_addr(rxb);
- struct iwl_scan_offload_complete *scan_notif = (void *)pkt->data;
+ u8 status, ebs_status;
+
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) {
+ struct iwl_periodic_scan_complete *scan_notif;
+
+ scan_notif = (void *)pkt->data;
+ status = scan_notif->status;
+ ebs_status = scan_notif->ebs_status;
+ } else {
+ struct iwl_scan_offload_complete *scan_notif;
+ scan_notif = (void *)pkt->data;
+ status = scan_notif->status;
+ ebs_status = scan_notif->ebs_status;
+ }
/* scan status must be locked for proper checking */
lockdep_assert_held(&mvm->mutex);
IWL_DEBUG_SCAN(mvm,
- "Scheduled scan completed, status %s EBS status %s:%d\n",
- scan_notif->status == IWL_SCAN_OFFLOAD_COMPLETED ?
- "completed" : "aborted", scan_notif->ebs_status ==
- IWL_SCAN_EBS_SUCCESS ? "success" : "failed",
- scan_notif->ebs_status);
+ "%s completed, status %s, EBS status %s\n",
+ mvm->scan_status == IWL_MVM_SCAN_SCHED ?
+ "Scheduled scan" : "Scan",
+ status == IWL_SCAN_OFFLOAD_COMPLETED ?
+ "completed" : "aborted",
+ ebs_status == IWL_SCAN_EBS_SUCCESS ?
+ "success" : "failed");
/* only call mac80211 completion if the stop was initiated by FW */
if (mvm->scan_status == IWL_MVM_SCAN_SCHED) {
mvm->scan_status = IWL_MVM_SCAN_NONE;
ieee80211_sched_scan_stopped(mvm->hw);
+ } else if (mvm->scan_status == IWL_MVM_SCAN_OS) {
+ mvm->scan_status = IWL_MVM_SCAN_NONE;
+ ieee80211_scan_completed(mvm->hw,
+ status == IWL_SCAN_OFFLOAD_ABORTED);
}
- mvm->last_ebs_successful = !scan_notif->ebs_status;
+ mvm->last_ebs_successful = !ebs_status;
return 0;
}
static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
- struct ieee80211_sched_scan_ies *ies,
+ struct ieee80211_scan_ies *ies,
enum ieee80211_band band,
struct iwl_tx_cmd *cmd,
u8 *data)
@@ -577,7 +616,8 @@ static void iwl_scan_offload_build_tx_cmd(struct iwl_mvm *mvm,
cmd_len = iwl_mvm_fill_probe_req((struct ieee80211_mgmt *)data,
vif->addr,
1, NULL, 0,
- ies->ie[band], ies->len[band],
+ ies->ies[band], ies->len[band],
+ ies->common_ies, ies->common_ie_len,
SCAN_OFFLOAD_PROBE_REQ_SIZE);
cmd->len = cpu_to_le16(cmd_len);
}
@@ -588,9 +628,7 @@ static void iwl_build_scan_cmd(struct iwl_mvm *mvm,
struct iwl_scan_offload_cmd *scan,
struct iwl_mvm_scan_params *params)
{
- scan->channel_count =
- mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels +
- mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
+ scan->channel_count = req->n_channels;
scan->quiet_time = cpu_to_le16(IWL_ACTIVE_QUIET_TIME);
scan->quiet_plcp_th = cpu_to_le16(IWL_PLCP_QUIET_THRESH);
scan->good_CRC_th = IWL_GOOD_CRC_TH_DEFAULT;
@@ -623,8 +661,8 @@ static int iwl_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list)
}
static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
- struct iwl_scan_offload_cmd *scan,
- u32 *ssid_bitmap)
+ struct iwl_ssid_ie *direct_scan,
+ u32 *ssid_bitmap, bool basic_ssid)
{
int i, j;
int index;
@@ -638,10 +676,10 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
/* skip empty SSID matchsets */
if (!req->match_sets[i].ssid.ssid_len)
continue;
- scan->direct_scan[i].id = WLAN_EID_SSID;
- scan->direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
- memcpy(scan->direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
- scan->direct_scan[i].len);
+ direct_scan[i].id = WLAN_EID_SSID;
+ direct_scan[i].len = req->match_sets[i].ssid.ssid_len;
+ memcpy(direct_scan[i].ssid, req->match_sets[i].ssid.ssid,
+ direct_scan[i].len);
}
/* add SSIDs from scan SSID list */
@@ -649,14 +687,14 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
for (j = 0; j < req->n_ssids && i < PROBE_OPTION_MAX; j++) {
index = iwl_ssid_exist(req->ssids[j].ssid,
req->ssids[j].ssid_len,
- scan->direct_scan);
+ direct_scan);
if (index < 0) {
- if (!req->ssids[j].ssid_len)
+ if (!req->ssids[j].ssid_len && basic_ssid)
continue;
- scan->direct_scan[i].id = WLAN_EID_SSID;
- scan->direct_scan[i].len = req->ssids[j].ssid_len;
- memcpy(scan->direct_scan[i].ssid, req->ssids[j].ssid,
- scan->direct_scan[i].len);
+ direct_scan[i].id = WLAN_EID_SSID;
+ direct_scan[i].len = req->ssids[j].ssid_len;
+ memcpy(direct_scan[i].ssid, req->ssids[j].ssid,
+ direct_scan[i].len);
*ssid_bitmap |= BIT(i + 1);
i++;
} else {
@@ -667,83 +705,67 @@ static void iwl_scan_offload_build_ssid(struct cfg80211_sched_scan_request *req,
static void iwl_build_channel_cfg(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req,
- struct iwl_scan_channel_cfg *channels,
+ u8 *channels_buffer,
enum ieee80211_band band,
- int *head, int *tail,
+ int *head,
u32 ssid_bitmap,
struct iwl_mvm_scan_params *params)
{
- struct ieee80211_supported_band *s_band;
- int n_channels = req->n_channels;
- int i, j, index = 0;
- bool partial;
+ u32 n_channels = mvm->fw->ucode_capa.n_scan_channels;
+ __le32 *type = (__le32 *)channels_buffer;
+ __le16 *channel_number = (__le16 *)(type + n_channels);
+ __le16 *iter_count = channel_number + n_channels;
+ __le32 *iter_interval = (__le32 *)(iter_count + n_channels);
+ u8 *active_dwell = (u8 *)(iter_interval + n_channels);
+ u8 *passive_dwell = active_dwell + n_channels;
+ int i, index = 0;
+
+ for (i = 0; i < req->n_channels; i++) {
+ struct ieee80211_channel *chan = req->channels[i];
+
+ if (chan->band != band)
+ continue;
- /*
- * We have to configure all supported channels, even if we don't want to
- * scan on them, but we have to send channels in the order that we want
- * to scan. So add requested channels to head of the list and others to
- * the end.
- */
- s_band = &mvm->nvm_data->bands[band];
-
- for (i = 0; i < s_band->n_channels && *head <= *tail; i++) {
- partial = false;
- for (j = 0; j < n_channels; j++)
- if (s_band->channels[i].center_freq ==
- req->channels[j]->center_freq) {
- index = *head;
- (*head)++;
- /*
- * Channels that came with the request will be
- * in partial scan .
- */
- partial = true;
- break;
- }
- if (!partial) {
- index = *tail;
- (*tail)--;
- }
- channels->channel_number[index] =
- cpu_to_le16(ieee80211_frequency_to_channel(
- s_band->channels[i].center_freq));
- channels->dwell_time[index][0] = params->dwell[band].active;
- channels->dwell_time[index][1] = params->dwell[band].passive;
+ index = *head;
+ (*head)++;
+
+ channel_number[index] = cpu_to_le16(chan->hw_value);
+ active_dwell[index] = params->dwell[band].active;
+ passive_dwell[index] = params->dwell[band].passive;
- channels->iter_count[index] = cpu_to_le16(1);
- channels->iter_interval[index] = 0;
+ iter_count[index] = cpu_to_le16(1);
+ iter_interval[index] = 0;
- if (!(s_band->channels[i].flags & IEEE80211_CHAN_NO_IR))
- channels->type[index] |=
+ if (!(chan->flags & IEEE80211_CHAN_NO_IR))
+ type[index] |=
cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_ACTIVE);
- channels->type[index] |=
- cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL);
- if (partial)
- channels->type[index] |=
- cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL);
+ type[index] |= cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_FULL |
+ IWL_SCAN_OFFLOAD_CHANNEL_PARTIAL);
- if (s_band->channels[i].flags & IEEE80211_CHAN_NO_HT40)
- channels->type[index] |=
+ if (chan->flags & IEEE80211_CHAN_NO_HT40)
+ type[index] |=
cpu_to_le32(IWL_SCAN_OFFLOAD_CHANNEL_NARROW);
/* scan for all SSIDs from req->ssids */
- channels->type[index] |= cpu_to_le32(ssid_bitmap);
+ type[index] |= cpu_to_le32(ssid_bitmap);
}
}
int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies)
+ struct ieee80211_scan_ies *ies)
{
int band_2ghz = mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels;
int band_5ghz = mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels;
int head = 0;
- int tail = band_2ghz + band_5ghz - 1;
u32 ssid_bitmap;
int cmd_len;
int ret;
+ u8 *probes;
+ bool basic_ssid = !(mvm->fw->ucode_capa.flags &
+ IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID);
struct iwl_scan_offload_cfg *scan_cfg;
struct iwl_host_cmd cmd = {
@@ -754,35 +776,40 @@ int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
lockdep_assert_held(&mvm->mutex);
cmd_len = sizeof(struct iwl_scan_offload_cfg) +
+ mvm->fw->ucode_capa.n_scan_channels * IWL_SCAN_CHAN_SIZE +
2 * SCAN_OFFLOAD_PROBE_REQ_SIZE;
scan_cfg = kzalloc(cmd_len, GFP_KERNEL);
if (!scan_cfg)
return -ENOMEM;
- iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, &params);
+ probes = scan_cfg->data +
+ mvm->fw->ucode_capa.n_scan_channels * IWL_SCAN_CHAN_SIZE;
+
+ iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, 0, &params);
iwl_build_scan_cmd(mvm, vif, req, &scan_cfg->scan_cmd, &params);
scan_cfg->scan_cmd.len = cpu_to_le16(cmd_len);
- iwl_scan_offload_build_ssid(req, &scan_cfg->scan_cmd, &ssid_bitmap);
+ iwl_scan_offload_build_ssid(req, scan_cfg->scan_cmd.direct_scan,
+ &ssid_bitmap, basic_ssid);
/* build tx frames for supported bands */
if (band_2ghz) {
iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
IEEE80211_BAND_2GHZ,
&scan_cfg->scan_cmd.tx_cmd[0],
- scan_cfg->data);
- iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg,
- IEEE80211_BAND_2GHZ, &head, &tail,
+ probes);
+ iwl_build_channel_cfg(mvm, req, scan_cfg->data,
+ IEEE80211_BAND_2GHZ, &head,
ssid_bitmap, &params);
}
if (band_5ghz) {
iwl_scan_offload_build_tx_cmd(mvm, vif, ies,
IEEE80211_BAND_5GHZ,
&scan_cfg->scan_cmd.tx_cmd[1],
- scan_cfg->data +
+ probes +
SCAN_OFFLOAD_PROBE_REQ_SIZE);
- iwl_build_channel_cfg(mvm, req, &scan_cfg->channel_cfg,
- IEEE80211_BAND_5GHZ, &head, &tail,
+ iwl_build_channel_cfg(mvm, req, scan_cfg->data,
+ IEEE80211_BAND_5GHZ, &head,
ssid_bitmap, &params);
}
@@ -872,11 +899,11 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
.watchdog = IWL_SCHED_SCAN_WATCHDOG,
.schedule_line[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS,
- .schedule_line[0].delay = req->interval / 1000,
+ .schedule_line[0].delay = cpu_to_le16(req->interval / 1000),
.schedule_line[0].full_scan_mul = 1,
.schedule_line[1].iterations = 0xff,
- .schedule_line[1].delay = req->interval / 1000,
+ .schedule_line[1].delay = cpu_to_le16(req->interval / 1000),
.schedule_line[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER,
};
@@ -899,7 +926,7 @@ int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
sizeof(scan_req), &scan_req);
}
-static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
+static int iwl_mvm_send_scan_offload_abort(struct iwl_mvm *mvm)
{
int ret;
struct iwl_host_cmd cmd = {
@@ -910,7 +937,9 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
/* Exit instantly with error when device is not ready
* to receive scan abort command or it does not perform
* scheduled scan currently */
- if (mvm->scan_status != IWL_MVM_SCAN_SCHED)
+ if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
+ (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
+ mvm->scan_status != IWL_MVM_SCAN_OS))
return -EIO;
ret = iwl_mvm_send_cmd_status(mvm, &cmd, &status);
@@ -932,16 +961,19 @@ static int iwl_mvm_send_sched_scan_abort(struct iwl_mvm *mvm)
return ret;
}
-int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
+int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify)
{
int ret;
struct iwl_notification_wait wait_scan_done;
static const u8 scan_done_notif[] = { SCAN_OFFLOAD_COMPLETE, };
+ bool sched = mvm->scan_status == IWL_MVM_SCAN_SCHED;
lockdep_assert_held(&mvm->mutex);
- if (mvm->scan_status != IWL_MVM_SCAN_SCHED) {
- IWL_DEBUG_SCAN(mvm, "No offloaded scan to stop\n");
+ if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
+ (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) ||
+ mvm->scan_status != IWL_MVM_SCAN_OS)) {
+ IWL_DEBUG_SCAN(mvm, "No scan to stop\n");
return 0;
}
@@ -950,14 +982,16 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
ARRAY_SIZE(scan_done_notif),
NULL, NULL);
- ret = iwl_mvm_send_sched_scan_abort(mvm);
+ ret = iwl_mvm_send_scan_offload_abort(mvm);
if (ret) {
- IWL_DEBUG_SCAN(mvm, "Send stop offload scan failed %d\n", ret);
+ IWL_DEBUG_SCAN(mvm, "Send stop %sscan failed %d\n",
+ sched ? "offloaded " : "", ret);
iwl_remove_notification(&mvm->notif_wait, &wait_scan_done);
return ret;
}
- IWL_DEBUG_SCAN(mvm, "Successfully sent stop offload scan\n");
+ IWL_DEBUG_SCAN(mvm, "Successfully sent stop %sscan\n",
+ sched ? "offloaded " : "");
ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ);
if (ret)
@@ -970,8 +1004,317 @@ int iwl_mvm_sched_scan_stop(struct iwl_mvm *mvm, bool notify)
*/
mvm->scan_status = IWL_MVM_SCAN_NONE;
- if (notify)
- ieee80211_sched_scan_stopped(mvm->hw);
+ if (notify) {
+ if (sched)
+ ieee80211_sched_scan_stopped(mvm->hw);
+ else
+ ieee80211_scan_completed(mvm->hw, true);
+ }
return 0;
}
+
+static void iwl_mvm_unified_scan_fill_tx_cmd(struct iwl_mvm *mvm,
+ struct iwl_scan_req_tx_cmd *tx_cmd,
+ bool no_cck)
+{
+ tx_cmd[0].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+ TX_CMD_FLG_BT_DIS);
+ tx_cmd[0].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm,
+ IEEE80211_BAND_2GHZ,
+ no_cck);
+ tx_cmd[0].sta_id = mvm->aux_sta.sta_id;
+
+ tx_cmd[1].tx_flags = cpu_to_le32(TX_CMD_FLG_SEQ_CTL |
+ TX_CMD_FLG_BT_DIS);
+ tx_cmd[1].rate_n_flags = iwl_mvm_scan_rate_n_flags(mvm,
+ IEEE80211_BAND_5GHZ,
+ no_cck);
+ tx_cmd[1].sta_id = mvm->aux_sta.sta_id;
+}
+
+static void
+iwl_mvm_lmac_scan_cfg_channels(struct iwl_mvm *mvm,
+ struct ieee80211_channel **channels,
+ int n_channels, u32 ssid_bitmap,
+ struct iwl_scan_req_unified_lmac *cmd)
+{
+ struct iwl_scan_channel_cfg_lmac *channel_cfg = (void *)&cmd->data;
+ int i;
+
+ for (i = 0; i < n_channels; i++) {
+ channel_cfg[i].channel_num =
+ cpu_to_le16(channels[i]->hw_value);
+ channel_cfg[i].iter_count = cpu_to_le16(1);
+ channel_cfg[i].iter_interval = 0;
+ channel_cfg[i].flags =
+ cpu_to_le32(IWL_UNIFIED_SCAN_CHANNEL_PARTIAL |
+ ssid_bitmap);
+ }
+}
+
+static void
+iwl_mvm_build_unified_scan_probe(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+ struct ieee80211_scan_ies *ies,
+ struct iwl_scan_req_unified_lmac *cmd)
+{
+ struct iwl_scan_probe_req *preq = (void *)(cmd->data +
+ sizeof(struct iwl_scan_channel_cfg_lmac) *
+ mvm->fw->ucode_capa.n_scan_channels);
+ struct ieee80211_mgmt *frame = (struct ieee80211_mgmt *)preq->buf;
+ u8 *pos;
+
+ frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ);
+ eth_broadcast_addr(frame->da);
+ memcpy(frame->sa, vif->addr, ETH_ALEN);
+ eth_broadcast_addr(frame->bssid);
+ frame->seq_ctrl = 0;
+
+ pos = frame->u.probe_req.variable;
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = 0;
+
+ preq->mac_header.offset = 0;
+ preq->mac_header.len = cpu_to_le16(24 + 2);
+
+ memcpy(pos, ies->ies[IEEE80211_BAND_2GHZ],
+ ies->len[IEEE80211_BAND_2GHZ]);
+ preq->band_data[0].offset = cpu_to_le16(pos - preq->buf);
+ preq->band_data[0].len = cpu_to_le16(ies->len[IEEE80211_BAND_2GHZ]);
+ pos += ies->len[IEEE80211_BAND_2GHZ];
+
+ memcpy(pos, ies->ies[IEEE80211_BAND_5GHZ],
+ ies->len[IEEE80211_BAND_5GHZ]);
+ preq->band_data[1].offset = cpu_to_le16(pos - preq->buf);
+ preq->band_data[1].len = cpu_to_le16(ies->len[IEEE80211_BAND_5GHZ]);
+ pos += ies->len[IEEE80211_BAND_5GHZ];
+
+ memcpy(pos, ies->common_ies, ies->common_ie_len);
+ preq->common_data.offset = cpu_to_le16(pos - preq->buf);
+ preq->common_data.len = cpu_to_le16(ies->common_ie_len);
+}
+
+static void
+iwl_mvm_build_generic_unified_scan_cmd(struct iwl_mvm *mvm,
+ struct iwl_scan_req_unified_lmac *cmd,
+ struct iwl_mvm_scan_params *params)
+{
+ memset(cmd, 0, ksize(cmd));
+ cmd->active_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].active;
+ cmd->passive_dwell = (u8)params->dwell[IEEE80211_BAND_2GHZ].passive;
+ /* TODO: Use params; now fragmented isn't used. */
+ cmd->fragmented_dwell = 0;
+ cmd->rx_chain_select = iwl_mvm_scan_rx_chain(mvm);
+ cmd->max_out_time = cpu_to_le32(params->max_out_time);
+ cmd->suspend_time = cpu_to_le32(params->suspend_time);
+ cmd->scan_prio = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH);
+ cmd->iter_num = cpu_to_le32(1);
+
+ if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT &&
+ mvm->last_ebs_successful) {
+ cmd->channel_opt[0].flags =
+ cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+ IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+ IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+ cmd->channel_opt[1].flags =
+ cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS |
+ IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE |
+ IWL_SCAN_CHANNEL_FLAG_CACHE_ADD);
+ }
+}
+
+int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *req)
+{
+ struct iwl_host_cmd hcmd = {
+ .id = SCAN_OFFLOAD_REQUEST_CMD,
+ .len = { sizeof(struct iwl_scan_req_unified_lmac) +
+ sizeof(struct iwl_scan_channel_cfg_lmac) *
+ mvm->fw->ucode_capa.n_scan_channels +
+ sizeof(struct iwl_scan_probe_req), },
+ .data = { mvm->scan_cmd, },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+ struct iwl_mvm_scan_params params = {};
+ u32 flags;
+ int ssid_bitmap = 0;
+ int ret, i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* we should have failed registration if scan_cmd was NULL */
+ if (WARN_ON(mvm->scan_cmd == NULL))
+ return -ENOMEM;
+
+ if (WARN_ON_ONCE(req->req.n_ssids > PROBE_OPTION_MAX ||
+ req->ies.common_ie_len + req->ies.len[0] +
+ req->ies.len[1] + 24 + 2 >
+ SCAN_OFFLOAD_PROBE_REQ_SIZE ||
+ req->req.n_channels >
+ mvm->fw->ucode_capa.n_scan_channels))
+ return -1;
+
+ mvm->scan_status = IWL_MVM_SCAN_OS;
+
+ iwl_mvm_scan_calc_params(mvm, vif, req->req.n_ssids, req->req.flags,
+ &params);
+
+ iwl_mvm_build_generic_unified_scan_cmd(mvm, cmd, &params);
+
+ cmd->n_channels = (u8)req->req.n_channels;
+
+ flags = IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
+
+ if (req->req.n_ssids == 1 && req->req.ssids[0].ssid_len != 0)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
+
+ if (params.passive_fragmented)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED;
+
+ if (req->req.n_ssids == 0)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE;
+
+ cmd->scan_flags = cpu_to_le32(flags);
+
+ cmd->flags = iwl_mvm_scan_rxon_flags(req->req.channels[0]->band);
+ cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+ MAC_FILTER_IN_BEACON);
+ iwl_mvm_unified_scan_fill_tx_cmd(mvm, cmd->tx_cmd, req->req.no_cck);
+ iwl_mvm_scan_fill_ssids(cmd->direct_scan, req->req.ssids,
+ req->req.n_ssids, 0);
+
+ cmd->schedule[0].delay = 0;
+ cmd->schedule[0].iterations = 1;
+ cmd->schedule[0].full_scan_mul = 0;
+ cmd->schedule[1].delay = 0;
+ cmd->schedule[1].iterations = 0;
+ cmd->schedule[1].full_scan_mul = 0;
+
+ for (i = 1; i <= req->req.n_ssids; i++)
+ ssid_bitmap |= BIT(i);
+
+ iwl_mvm_lmac_scan_cfg_channels(mvm, req->req.channels,
+ req->req.n_channels, ssid_bitmap,
+ cmd);
+
+ iwl_mvm_build_unified_scan_probe(mvm, vif, &req->ies, cmd);
+
+ ret = iwl_mvm_send_cmd(mvm, &hcmd);
+ if (!ret) {
+ IWL_DEBUG_SCAN(mvm, "Scan request was sent successfully\n");
+ } else {
+ /*
+ * If the scan failed, it usually means that the FW was unable
+ * to allocate the time events. Warn on it, but maybe we
+ * should try to send the command again with different params.
+ */
+ IWL_ERR(mvm, "Scan failed! ret %d\n", ret);
+ mvm->scan_status = IWL_MVM_SCAN_NONE;
+ ret = -EIO;
+ }
+ return ret;
+}
+
+int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_scan_ies *ies)
+{
+ struct iwl_host_cmd hcmd = {
+ .id = SCAN_OFFLOAD_REQUEST_CMD,
+ .len = { sizeof(struct iwl_scan_req_unified_lmac) +
+ sizeof(struct iwl_scan_channel_cfg_lmac) *
+ mvm->fw->ucode_capa.n_scan_channels +
+ sizeof(struct iwl_scan_probe_req), },
+ .data = { mvm->scan_cmd, },
+ .dataflags = { IWL_HCMD_DFL_NOCOPY, },
+ };
+ struct iwl_scan_req_unified_lmac *cmd = mvm->scan_cmd;
+ struct iwl_mvm_scan_params params = {};
+ int ret;
+ u32 flags = 0, ssid_bitmap = 0;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* we should have failed registration if scan_cmd was NULL */
+ if (WARN_ON(mvm->scan_cmd == NULL))
+ return -ENOMEM;
+
+ if (WARN_ON_ONCE(req->n_ssids > PROBE_OPTION_MAX ||
+ ies->common_ie_len + ies->len[0] + ies->len[1] + 24 + 2
+ > SCAN_OFFLOAD_PROBE_REQ_SIZE ||
+ req->n_channels > mvm->fw->ucode_capa.n_scan_channels))
+ return -ENOBUFS;
+
+ iwl_mvm_scan_calc_params(mvm, vif, req->n_ssids, 0, &params);
+
+ iwl_mvm_build_generic_unified_scan_cmd(mvm, cmd, &params);
+
+ cmd->n_channels = (u8)req->n_channels;
+
+ if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) {
+ IWL_DEBUG_SCAN(mvm,
+ "Sending scheduled scan with filtering, n_match_sets %d\n",
+ req->n_match_sets);
+ } else {
+ IWL_DEBUG_SCAN(mvm,
+ "Sending Scheduled scan without filtering\n");
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL;
+ }
+
+ if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION;
+
+ if (params.passive_fragmented)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED;
+
+ if (req->n_ssids == 0)
+ flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE;
+
+ cmd->scan_flags = cpu_to_le32(flags);
+
+ cmd->flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band);
+ cmd->filter_flags = cpu_to_le32(MAC_FILTER_ACCEPT_GRP |
+ MAC_FILTER_IN_BEACON);
+ iwl_mvm_unified_scan_fill_tx_cmd(mvm, cmd->tx_cmd, false);
+ iwl_scan_offload_build_ssid(req, cmd->direct_scan, &ssid_bitmap, false);
+
+ cmd->schedule[0].delay = cpu_to_le16(req->interval / MSEC_PER_SEC);
+ cmd->schedule[0].iterations = IWL_FAST_SCHED_SCAN_ITERATIONS;
+ cmd->schedule[0].full_scan_mul = 1;
+
+ cmd->schedule[1].delay = cpu_to_le16(req->interval / MSEC_PER_SEC);
+ cmd->schedule[1].iterations = 0xff;
+ cmd->schedule[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER;
+
+ iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels,
+ ssid_bitmap, cmd);
+
+ iwl_mvm_build_unified_scan_probe(mvm, vif, ies, cmd);
+
+ ret = iwl_mvm_send_cmd(mvm, &hcmd);
+ if (!ret) {
+ IWL_DEBUG_SCAN(mvm,
+ "Sched scan request was sent successfully\n");
+ } else {
+ /*
+ * If the scan failed, it usually means that the FW was unable
+ * to allocate the time events. Warn on it, but maybe we
+ * should try to send the command again with different params.
+ */
+ IWL_ERR(mvm, "Sched scan failed! ret %d\n", ret);
+ mvm->scan_status = IWL_MVM_SCAN_NONE;
+ ret = -EIO;
+ }
+ return ret;
+}
+
+
+int iwl_mvm_cancel_scan(struct iwl_mvm *mvm)
+{
+ if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
+ return iwl_mvm_scan_offload_stop(mvm, true);
+ return iwl_mvm_cancel_regular_scan(mvm);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c
index 1fb01ea2e704..812813964847 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.c
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.c
@@ -1448,3 +1448,77 @@ int iwl_mvm_rx_eosp_notif(struct iwl_mvm *mvm,
return 0;
}
+
+void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
+ struct iwl_mvm_sta *mvmsta, bool disable)
+{
+ struct iwl_mvm_add_sta_cmd cmd = {
+ .add_modify = STA_MODE_MODIFY,
+ .sta_id = mvmsta->sta_id,
+ .station_flags = disable ? cpu_to_le32(STA_FLG_DISABLE_TX) : 0,
+ .station_flags_msk = cpu_to_le32(STA_FLG_DISABLE_TX),
+ .mac_id_n_color = cpu_to_le32(mvmsta->mac_id_n_color),
+ };
+ int ret;
+
+ if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_DISABLE_STA_TX))
+ return;
+
+ ret = iwl_mvm_send_cmd_pdu(mvm, ADD_STA, CMD_ASYNC, sizeof(cmd), &cmd);
+ if (ret)
+ IWL_ERR(mvm, "Failed to send ADD_STA command (%d)\n", ret);
+}
+
+void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ bool disable)
+{
+ struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+
+ spin_lock_bh(&mvm_sta->lock);
+
+ if (mvm_sta->disable_tx == disable) {
+ spin_unlock_bh(&mvm_sta->lock);
+ return;
+ }
+
+ mvm_sta->disable_tx = disable;
+
+ /*
+ * Tell mac80211 to start/stop queueing tx for this station,
+ * but don't stop queueing if there are still pending frames
+ * for this station.
+ */
+ if (disable || !atomic_read(&mvm->pending_frames[mvm_sta->sta_id]))
+ ieee80211_sta_block_awake(mvm->hw, sta, disable);
+
+ iwl_mvm_sta_modify_disable_tx(mvm, mvm_sta, disable);
+
+ spin_unlock_bh(&mvm_sta->lock);
+}
+
+void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
+ struct iwl_mvm_vif *mvmvif,
+ bool disable)
+{
+ struct ieee80211_sta *sta;
+ struct iwl_mvm_sta *mvm_sta;
+ int i;
+
+ lockdep_assert_held(&mvm->mutex);
+
+ /* Block/unblock all the stations of the given mvmvif */
+ for (i = 0; i < IWL_MVM_STATION_COUNT; i++) {
+ sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[i],
+ lockdep_is_held(&mvm->mutex));
+ if (IS_ERR_OR_NULL(sta))
+ continue;
+
+ mvm_sta = iwl_mvm_sta_from_mac80211(sta);
+ if (mvm_sta->mac_id_n_color !=
+ FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color))
+ continue;
+
+ iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, disable);
+ }
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.h b/drivers/net/wireless/iwlwifi/mvm/sta.h
index d98e8a2142b8..3b1c8bd6cb54 100644
--- a/drivers/net/wireless/iwlwifi/mvm/sta.h
+++ b/drivers/net/wireless/iwlwifi/mvm/sta.h
@@ -73,6 +73,7 @@
#include "rs.h"
struct iwl_mvm;
+struct iwl_mvm_vif;
/**
* DOC: station table - introduction
@@ -295,6 +296,7 @@ static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data)
* @tid_data: per tid data. Look at %iwl_mvm_tid_data.
* @tx_protection: reference counter for controlling the Tx protection.
* @tt_tx_protection: is thermal throttling enable Tx protection?
+ * @disable_tx: is tx to this STA disabled?
*
* When mac80211 creates a station it reserves some space (hw->sta_data_size)
* in the structure for use by driver. This structure is placed in that
@@ -317,6 +319,8 @@ struct iwl_mvm_sta {
/* Temporary, until the new TLC will control the Tx protection */
s8 tx_protection;
bool tt_tx_protection;
+
+ bool disable_tx;
};
static inline struct iwl_mvm_sta *
@@ -404,5 +408,13 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm,
bool agg);
int iwl_mvm_drain_sta(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta,
bool drain);
+void iwl_mvm_sta_modify_disable_tx(struct iwl_mvm *mvm,
+ struct iwl_mvm_sta *mvmsta, bool disable);
+void iwl_mvm_sta_modify_disable_tx_ap(struct iwl_mvm *mvm,
+ struct ieee80211_sta *sta,
+ bool disable);
+void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm,
+ struct iwl_mvm_vif *mvmvif,
+ bool disable);
#endif /* __sta_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.c b/drivers/net/wireless/iwlwifi/mvm/time-event.c
index 80100f6cc12a..ae52613b97f2 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.c
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.c
@@ -138,6 +138,41 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
schedule_work(&mvm->roc_done_wk);
}
+static void iwl_mvm_csa_noa_start(struct iwl_mvm *mvm)
+{
+ struct ieee80211_vif *csa_vif;
+
+ rcu_read_lock();
+
+ csa_vif = rcu_dereference(mvm->csa_vif);
+ if (!csa_vif || !csa_vif->csa_active)
+ goto out_unlock;
+
+ IWL_DEBUG_TE(mvm, "CSA NOA started\n");
+
+ /*
+ * CSA NoA is started but we still have beacons to
+ * transmit on the current channel.
+ * So we just do nothing here and the switch
+ * will be performed on the last TBTT.
+ */
+ if (!ieee80211_csa_is_complete(csa_vif)) {
+ IWL_WARN(mvm, "CSA NOA started too early\n");
+ goto out_unlock;
+ }
+
+ ieee80211_csa_finish(csa_vif);
+
+ rcu_read_unlock();
+
+ RCU_INIT_POINTER(mvm->csa_vif, NULL);
+
+ return;
+
+out_unlock:
+ rcu_read_unlock();
+}
+
static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
const char *errmsg)
@@ -213,6 +248,14 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
set_bit(IWL_MVM_STATUS_ROC_RUNNING, &mvm->status);
iwl_mvm_ref(mvm, IWL_MVM_REF_ROC);
ieee80211_ready_on_channel(mvm->hw);
+ } else if (te_data->vif->type == NL80211_IFTYPE_AP) {
+ if (le32_to_cpu(notif->status))
+ iwl_mvm_csa_noa_start(mvm);
+ else
+ IWL_DEBUG_TE(mvm, "CSA NOA failed to start\n");
+
+ /* we don't need it anymore */
+ iwl_mvm_te_clear_data(mvm, te_data);
}
} else {
IWL_WARN(mvm, "Got TE with unknown action\n");
@@ -538,3 +581,33 @@ void iwl_mvm_stop_p2p_roc(struct iwl_mvm *mvm)
iwl_mvm_roc_finished(mvm);
}
+
+int iwl_mvm_schedule_csa_noa(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 apply_time)
+{
+ struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+ struct iwl_mvm_time_event_data *te_data = &mvmvif->time_event_data;
+ struct iwl_time_event_cmd time_cmd = {};
+
+ lockdep_assert_held(&mvm->mutex);
+
+ if (te_data->running) {
+ IWL_DEBUG_TE(mvm, "CS NOA is already scheduled\n");
+ return -EBUSY;
+ }
+
+ time_cmd.action = cpu_to_le32(FW_CTXT_ACTION_ADD);
+ time_cmd.id_and_color =
+ cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color));
+ time_cmd.id = cpu_to_le32(TE_P2P_GO_CSA_NOA);
+ time_cmd.apply_time = cpu_to_le32(apply_time);
+ time_cmd.max_frags = TE_V2_FRAG_NONE;
+ time_cmd.duration = cpu_to_le32(duration);
+ time_cmd.repeat = 1;
+ time_cmd.interval = cpu_to_le32(1);
+ time_cmd.policy = cpu_to_le16(TE_V2_NOTIF_HOST_EVENT_START |
+ TE_V2_ABSENCE);
+
+ return iwl_mvm_time_event_send_add(mvm, vif, te_data, &time_cmd);
+}
diff --git a/drivers/net/wireless/iwlwifi/mvm/time-event.h b/drivers/net/wireless/iwlwifi/mvm/time-event.h
index 4a61c8c02372..2f48a90d4ad3 100644
--- a/drivers/net/wireless/iwlwifi/mvm/time-event.h
+++ b/drivers/net/wireless/iwlwifi/mvm/time-event.h
@@ -214,4 +214,33 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
void iwl_mvm_roc_done_wk(struct work_struct *wk);
+/**
+ * iwl_mvm_schedule_csa_noa - request NoA for channel switch
+ * @mvm: the mvm component
+ * @vif: the virtual interface for which the channel switch is issued
+ * @duration: the duration of the NoA in TU.
+ * @apply_time: NoA start time in GP2.
+ *
+ * This function is used to schedule NoA time event and is used to perform
+ * the channel switch flow.
+ */
+int iwl_mvm_schedule_csa_noa(struct iwl_mvm *mvm,
+ struct ieee80211_vif *vif,
+ u32 duration, u32 apply_time);
+
+/**
+ * iwl_mvm_te_scheduled - check if the fw received the TE cmd
+ * @te_data: the time event data that corresponds to that time event
+ *
+ * This function returns true iff this TE is added to the fw.
+ */
+static inline bool
+iwl_mvm_te_scheduled(struct iwl_mvm_time_event_data *te_data)
+{
+ if (!te_data)
+ return false;
+
+ return !!te_data->uid;
+}
+
#endif /* __time_event_h__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c
index 3846a6c41eb1..e9ff38635c21 100644
--- a/drivers/net/wireless/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/iwlwifi/mvm/tx.c
@@ -131,7 +131,6 @@ static void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb,
!is_multicast_ether_addr(ieee80211_get_DA(hdr)))
tx_flags |= TX_CMD_FLG_PROT_REQUIRE;
- tx_cmd->driver_txop = 0;
tx_cmd->tx_flags = cpu_to_le32(tx_flags);
/* Total # bytes to be transmitted */
tx_cmd->len = cpu_to_le16((u16)skb->len);
@@ -205,7 +204,13 @@ static void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm,
mvm->mgmt_last_antenna_idx =
iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant,
mvm->mgmt_last_antenna_idx);
- rate_flags = BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
+
+ if (info->band == IEEE80211_BAND_2GHZ &&
+ !iwl_mvm_bt_coex_is_shared_ant_avail(mvm))
+ rate_flags = BIT(ANT_A) << RATE_MCS_ANT_POS;
+ else
+ rate_flags =
+ BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS;
/* Set CCK flag as needed */
if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE))
@@ -717,18 +722,26 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
/* We can't free more than one frame at once on a shared queue */
WARN_ON(skb_freed > 1);
- /* If we have still frames from this STA nothing to do here */
+ /* If we have still frames for this STA nothing to do here */
if (!atomic_sub_and_test(skb_freed, &mvm->pending_frames[sta_id]))
goto out;
if (mvmsta && mvmsta->vif->type == NL80211_IFTYPE_AP) {
+
/*
- * If there are no pending frames for this STA, notify
- * mac80211 that this station can go to sleep in its
+ * If there are no pending frames for this STA and
+ * the tx to this station is not disabled, notify
+ * mac80211 that this station can now wake up in its
* STA table.
* If mvmsta is not NULL, sta is valid.
*/
- ieee80211_sta_block_awake(mvm->hw, sta, false);
+
+ spin_lock_bh(&mvmsta->lock);
+
+ if (!mvmsta->disable_tx)
+ ieee80211_sta_block_awake(mvm->hw, sta, false);
+
+ spin_unlock_bh(&mvmsta->lock);
}
if (PTR_ERR(sta) == -EBUSY || PTR_ERR(sta) == -ENOENT) {
diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c
index aa9fc77e8413..ac249da8a22b 100644
--- a/drivers/net/wireless/iwlwifi/mvm/utils.c
+++ b/drivers/net/wireless/iwlwifi/mvm/utils.c
@@ -519,71 +519,6 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
iwl_mvm_dump_umac_error_log(mvm);
}
-#ifdef CONFIG_IWLWIFI_DEBUGFS
-void iwl_mvm_fw_error_sram_dump(struct iwl_mvm *mvm)
-{
- const struct fw_img *img;
- u32 ofs, sram_len;
- void *sram;
-
- if (!mvm->ucode_loaded || mvm->fw_error_sram || mvm->fw_error_dump)
- return;
-
- img = &mvm->fw->img[mvm->cur_ucode];
- ofs = img->sec[IWL_UCODE_SECTION_DATA].offset;
- sram_len = img->sec[IWL_UCODE_SECTION_DATA].len;
-
- sram = kzalloc(sram_len, GFP_ATOMIC);
- if (!sram)
- return;
-
- iwl_trans_read_mem_bytes(mvm->trans, ofs, sram, sram_len);
- mvm->fw_error_sram = sram;
- mvm->fw_error_sram_len = sram_len;
-}
-
-void iwl_mvm_fw_error_rxf_dump(struct iwl_mvm *mvm)
-{
- int i, reg_val;
- unsigned long flags;
-
- if (!mvm->ucode_loaded || mvm->fw_error_rxf || mvm->fw_error_dump)
- return;
-
- /* reading buffer size */
- reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR);
- mvm->fw_error_rxf_len =
- (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS;
-
- /* the register holds the value divided by 128 */
- mvm->fw_error_rxf_len = mvm->fw_error_rxf_len << 7;
-
- if (!mvm->fw_error_rxf_len)
- return;
-
- mvm->fw_error_rxf = kzalloc(mvm->fw_error_rxf_len, GFP_ATOMIC);
- if (!mvm->fw_error_rxf) {
- mvm->fw_error_rxf_len = 0;
- return;
- }
-
- if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) {
- kfree(mvm->fw_error_rxf);
- mvm->fw_error_rxf = NULL;
- mvm->fw_error_rxf_len = 0;
- return;
- }
-
- for (i = 0; i < (mvm->fw_error_rxf_len / sizeof(u32)); i++) {
- iwl_trans_write_prph(mvm->trans, RXF_LD_FENCE_OFFSET_ADDR,
- i * sizeof(u32));
- mvm->fw_error_rxf[i] =
- iwl_trans_read_prph(mvm->trans, RXF_FIFO_RD_FENCE_ADDR);
- }
- iwl_trans_release_nic_access(mvm->trans, &flags);
-}
-#endif
-
/**
* iwl_mvm_send_lq_cmd() - Send link quality command
* @init: This command is sent as part of station initialization right
diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c
index 7091a18d5a72..98950e45c7b0 100644
--- a/drivers/net/wireless/iwlwifi/pcie/drv.c
+++ b/drivers/net/wireless/iwlwifi/pcie/drv.c
@@ -367,6 +367,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
{IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)},
@@ -380,7 +381,7 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = {
{IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x9210, iwl7265_2ac_cfg)},
- {IWL_PCI_DEVICE(0x095A, 0x9200, iwl7265_2ac_cfg)},
+ {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x9310, iwl7265_2ac_cfg)},
{IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)},
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 6c22b23a2845..78f72c34438a 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -260,6 +260,9 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx)
* @wd_timeout: queue watchdog timeout (jiffies)
* @reg_lock: protect hw register access
* @cmd_in_flight: true when we have a host command in flight
+ * @fw_mon_phys: physical address of the buffer for the firmware monitor
+ * @fw_mon_page: points to the first page of the buffer for the firmware monitor
+ * @fw_mon_size: size of the buffer for the firmware monitor
*/
struct iwl_trans_pcie {
struct iwl_rxq rxq;
@@ -312,6 +315,10 @@ struct iwl_trans_pcie {
/*protect hw register */
spinlock_t reg_lock;
bool cmd_in_flight;
+
+ dma_addr_t fw_mon_phys;
+ struct page *fw_mon_page;
+ u32 fw_mon_size;
};
#define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 788085bc65d7..5b5b0d8c6f60 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -76,6 +76,68 @@
#include "iwl-fw-error-dump.h"
#include "internal.h"
+static void iwl_pcie_free_fw_monitor(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+
+ if (!trans_pcie->fw_mon_page)
+ return;
+
+ dma_unmap_page(trans->dev, trans_pcie->fw_mon_phys,
+ trans_pcie->fw_mon_size, DMA_FROM_DEVICE);
+ __free_pages(trans_pcie->fw_mon_page,
+ get_order(trans_pcie->fw_mon_size));
+ trans_pcie->fw_mon_page = NULL;
+ trans_pcie->fw_mon_phys = 0;
+ trans_pcie->fw_mon_size = 0;
+}
+
+static void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans)
+{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
+ struct page *page;
+ dma_addr_t phys;
+ u32 size;
+ u8 power;
+
+ if (trans_pcie->fw_mon_page) {
+ dma_sync_single_for_device(trans->dev, trans_pcie->fw_mon_phys,
+ trans_pcie->fw_mon_size,
+ DMA_FROM_DEVICE);
+ return;
+ }
+
+ phys = 0;
+ for (power = 26; power >= 11; power--) {
+ int order;
+
+ size = BIT(power);
+ order = get_order(size);
+ page = alloc_pages(__GFP_COMP | __GFP_NOWARN | __GFP_ZERO,
+ order);
+ if (!page)
+ continue;
+
+ phys = dma_map_page(trans->dev, page, 0, PAGE_SIZE << order,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(trans->dev, phys)) {
+ __free_pages(page, order);
+ continue;
+ }
+ IWL_INFO(trans,
+ "Allocated 0x%08x bytes (order %d) for firmware monitor.\n",
+ size, order);
+ break;
+ }
+
+ if (!page)
+ return;
+
+ trans_pcie->fw_mon_page = page;
+ trans_pcie->fw_mon_phys = phys;
+ trans_pcie->fw_mon_size = size;
+}
+
static u32 iwl_trans_pcie_read_shr(struct iwl_trans *trans, u32 reg)
{
iwl_write32(trans, HEEP_CTRL_WRD_PCIEX_CTRL_REG,
@@ -675,6 +737,7 @@ static int iwl_pcie_load_cpu_sections(struct iwl_trans *trans,
static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
const struct fw_img *image)
{
+ struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
int ret = 0;
int first_ucode_section;
@@ -733,6 +796,20 @@ static int iwl_pcie_load_given_ucode(struct iwl_trans *trans,
return ret;
}
+ /* supported for 7000 only for the moment */
+ if (iwlwifi_mod_params.fw_monitor &&
+ trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
+ iwl_pcie_alloc_fw_monitor(trans);
+
+ if (trans_pcie->fw_mon_size) {
+ iwl_write_prph(trans, MON_BUFF_BASE_ADDR,
+ trans_pcie->fw_mon_phys >> 4);
+ iwl_write_prph(trans, MON_BUFF_END_ADDR,
+ (trans_pcie->fw_mon_phys +
+ trans_pcie->fw_mon_size) >> 4);
+ }
+ }
+
/* release CPU reset */
if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
@@ -1126,6 +1203,8 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
if (trans_pcie->napi.poll)
netif_napi_del(&trans_pcie->napi);
+ iwl_pcie_free_fw_monitor(trans);
+
kfree(trans);
}
@@ -1494,10 +1573,12 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file,
txq = &trans_pcie->txq[cnt];
q = &txq->q;
pos += scnprintf(buf + pos, bufsz - pos,
- "hwq %.2d: read=%u write=%u use=%d stop=%d\n",
+ "hwq %.2d: read=%u write=%u use=%d stop=%d need_update=%d%s\n",
cnt, q->read_ptr, q->write_ptr,
!!test_bit(cnt, trans_pcie->queue_used),
- !!test_bit(cnt, trans_pcie->queue_stopped));
+ !!test_bit(cnt, trans_pcie->queue_stopped),
+ txq->need_update,
+ (cnt == trans_pcie->cmd_queue ? " HCMD" : ""));
}
ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
kfree(buf);
@@ -1519,6 +1600,10 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file,
rxq->read);
pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n",
rxq->write);
+ pos += scnprintf(buf + pos, bufsz - pos, "write_actual: %u\n",
+ rxq->write_actual);
+ pos += scnprintf(buf + pos, bufsz - pos, "need_update: %d\n",
+ rxq->need_update);
pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n",
rxq->free_count);
if (rxq->rb_stts) {
@@ -1698,10 +1783,15 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
u32 len;
int i, ptr;
+ len = sizeof(*data) +
+ cmdq->q.n_window * (sizeof(*txcmd) + TFD_MAX_PAYLOAD_SIZE);
+
+ if (trans_pcie->fw_mon_page)
+ len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_fw_mon) +
+ trans_pcie->fw_mon_size;
+
if (!buf)
- return sizeof(*data) +
- cmdq->q.n_window * (sizeof(*txcmd) +
- TFD_MAX_PAYLOAD_SIZE);
+ return len;
len = 0;
data = buf;
@@ -1729,7 +1819,40 @@ static u32 iwl_trans_pcie_dump_data(struct iwl_trans *trans,
spin_unlock_bh(&cmdq->lock);
data->len = cpu_to_le32(len);
- return sizeof(*data) + len;
+ len += sizeof(*data);
+
+ if (trans_pcie->fw_mon_page) {
+ struct iwl_fw_error_dump_fw_mon *fw_mon_data;
+
+ data = iwl_fw_error_next_data(data);
+ data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR);
+ data->len = cpu_to_le32(trans_pcie->fw_mon_size +
+ sizeof(*fw_mon_data));
+ fw_mon_data = (void *)data->data;
+ fw_mon_data->fw_mon_wr_ptr =
+ cpu_to_le32(iwl_read_prph(trans, MON_BUFF_WRPTR));
+ fw_mon_data->fw_mon_cycle_cnt =
+ cpu_to_le32(iwl_read_prph(trans, MON_BUFF_CYCLE_CNT));
+ fw_mon_data->fw_mon_base_ptr =
+ cpu_to_le32(iwl_read_prph(trans, MON_BUFF_BASE_ADDR));
+
+ /*
+ * The firmware is now asserted, it won't write anything to
+ * the buffer. CPU can take ownership to fetch the data.
+ * The buffer will be handed back to the device before the
+ * firmware will be restarted.
+ */
+ dma_sync_single_for_cpu(trans->dev, trans_pcie->fw_mon_phys,
+ trans_pcie->fw_mon_size,
+ DMA_FROM_DEVICE);
+ memcpy(fw_mon_data->data, page_address(trans_pcie->fw_mon_page),
+ trans_pcie->fw_mon_size);
+
+ len += sizeof(*data) + sizeof(*fw_mon_data) +
+ trans_pcie->fw_mon_size;
+ }
+
+ return len;
}
#else
static int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans,
@@ -1870,6 +1993,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
}
trans->hw_rev = iwl_read32(trans, CSR_HW_REV);
+ /*
+ * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have
+ * changed, and now the revision step also includes bit 0-1 (no more
+ * "dash" value). To keep hw_rev backwards compatible - we'll store it
+ * in the old format.
+ */
+ if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000)
+ trans->hw_rev = (trans->hw_rev & 0xfff0) |
+ ((trans->hw_rev << 2) & 0xc);
+
trans->hw_id = (pdev->device << 16) + pdev->subsystem_device;
snprintf(trans->hw_id_str, sizeof(trans->hw_id_str),
"PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device);
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index 038940afbdc5..6acccb19c4f3 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -1438,6 +1438,7 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans,
CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ);
spin_unlock_irqrestore(&trans_pcie->reg_lock, flags);
trans_pcie->cmd_in_flight = false;
+ IWL_ERR(trans, "Failed to wake NIC for hcmd\n");
idx = -EIO;
goto out;
}
diff --git a/drivers/net/wireless/libertas/Kconfig b/drivers/net/wireless/libertas/Kconfig
index 0485c9957575..e6268ceacbf1 100644
--- a/drivers/net/wireless/libertas/Kconfig
+++ b/drivers/net/wireless/libertas/Kconfig
@@ -16,7 +16,7 @@ config LIBERTAS_USB
config LIBERTAS_CS
tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards"
- depends on LIBERTAS && PCMCIA
+ depends on LIBERTAS && PCMCIA && HAS_IOPORT_MAP
---help---
A driver for Marvell Libertas 8385 CompactFlash devices.
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index aaa297315c47..0387a5b380c8 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -1111,6 +1111,7 @@ int lbs_set_radio(struct lbs_private *priv, u8 preamble, u8 radio_on)
cmd.hdr.size = cpu_to_le16(sizeof(cmd));
cmd.action = cpu_to_le16(CMD_ACT_SET);
+ cmd.control = 0;
/* Only v8 and below support setting the preamble */
if (priv->fwrelease < 0x09000000) {
diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index a312c653d116..eba51460a5de 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -781,6 +781,36 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan,
netif_rx(skb);
}
+struct mac80211_hwsim_addr_match_data {
+ u8 addr[ETH_ALEN];
+ bool ret;
+};
+
+static void mac80211_hwsim_addr_iter(void *data, u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct mac80211_hwsim_addr_match_data *md = data;
+
+ if (memcmp(mac, md->addr, ETH_ALEN) == 0)
+ md->ret = true;
+}
+
+static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
+ const u8 *addr)
+{
+ struct mac80211_hwsim_addr_match_data md = {
+ .ret = false,
+ };
+
+ memcpy(md.addr, addr, ETH_ALEN);
+
+ ieee80211_iterate_active_interfaces_atomic(data->hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ mac80211_hwsim_addr_iter,
+ &md);
+
+ return md.ret;
+}
static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
struct sk_buff *skb)
@@ -798,8 +828,7 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
/* Allow unicast frames to own address if there is a pending
* PS-Poll */
if (data->ps_poll_pending &&
- memcmp(data->hw->wiphy->perm_addr, skb->data + 4,
- ETH_ALEN) == 0) {
+ mac80211_hwsim_addr_match(data, skb->data + 4)) {
data->ps_poll_pending = false;
return true;
}
@@ -809,39 +838,6 @@ static bool hwsim_ps_rx_ok(struct mac80211_hwsim_data *data,
return true;
}
-
-struct mac80211_hwsim_addr_match_data {
- bool ret;
- const u8 *addr;
-};
-
-static void mac80211_hwsim_addr_iter(void *data, u8 *mac,
- struct ieee80211_vif *vif)
-{
- struct mac80211_hwsim_addr_match_data *md = data;
- if (memcmp(mac, md->addr, ETH_ALEN) == 0)
- md->ret = true;
-}
-
-
-static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
- const u8 *addr)
-{
- struct mac80211_hwsim_addr_match_data md;
-
- if (memcmp(addr, data->hw->wiphy->perm_addr, ETH_ALEN) == 0)
- return true;
-
- md.ret = false;
- md.addr = addr;
- ieee80211_iterate_active_interfaces_atomic(data->hw,
- IEEE80211_IFACE_ITER_NORMAL,
- mac80211_hwsim_addr_iter,
- &md);
-
- return md.ret;
-}
-
static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
struct sk_buff *my_skb,
int dst_portid)
@@ -1740,9 +1736,10 @@ static void hw_scan_work(struct work_struct *work)
static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req)
+ struct ieee80211_scan_request *hw_req)
{
struct mac80211_hwsim_data *hwsim = hw->priv;
+ struct cfg80211_scan_request *req = &hw_req->req;
mutex_lock(&hwsim->mutex);
if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) {
diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c
index 706831df1fa2..59d23fb2365f 100644
--- a/drivers/net/wireless/mwifiex/11ac.c
+++ b/drivers/net/wireless/mwifiex/11ac.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: 802.11ac
*
- * Copyright (C) 2013, Marvell International Ltd.
+ * Copyright (C) 2013-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/11ac.h b/drivers/net/wireless/mwifiex/11ac.h
index 0b02cb6cfcb4..1ca92c7a8a4a 100644
--- a/drivers/net/wireless/mwifiex/11ac.h
+++ b/drivers/net/wireless/mwifiex/11ac.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: 802.11ac
*
- * Copyright (C) 2013, Marvell International Ltd.
+ * Copyright (C) 2013-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c
index e76b0db4e3e6..2668e83afbb6 100644
--- a/drivers/net/wireless/mwifiex/11h.c
+++ b/drivers/net/wireless/mwifiex/11h.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: 802.11h
*
- * Copyright (C) 2013, Marvell International Ltd.
+ * Copyright (C) 2013-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c
index e1c2f67ae85e..9d6d8d9f01e3 100644
--- a/drivers/net/wireless/mwifiex/11n.c
+++ b/drivers/net/wireless/mwifiex/11n.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: 802.11n
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h
index 0b73fa08f5d4..2ee268b632be 100644
--- a/drivers/net/wireless/mwifiex/11n.h
+++ b/drivers/net/wireless/mwifiex/11n.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: 802.11n
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c
index 5b32106182f8..b4c14b0fd3cb 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.c
+++ b/drivers/net/wireless/mwifiex/11n_aggr.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: 802.11n Aggregation
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/mwifiex/11n_aggr.h
index 892098d6a696..0cd2a3eb6c17 100644
--- a/drivers/net/wireless/mwifiex/11n_aggr.h
+++ b/drivers/net/wireless/mwifiex/11n_aggr.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: 802.11n Aggregation
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c
index 0c3571f830b0..b22bae3d1205 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.c
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: 802.11n RX Re-ordering
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h
index 0fc76e4a60f8..3a87bb0e3a62 100644
--- a/drivers/net/wireless/mwifiex/11n_rxreorder.h
+++ b/drivers/net/wireless/mwifiex/11n_rxreorder.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: 802.11n RX Re-ordering
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile
index 2aa208ffbe23..9487d728ac20 100644
--- a/drivers/net/wireless/mwifiex/Makefile
+++ b/drivers/net/wireless/mwifiex/Makefile
@@ -1,5 +1,5 @@
#
-# Copyright (C) 2011, Marvell International Ltd.
+# Copyright (C) 2011-2014, Marvell International Ltd.
#
# This software file (the "File") is distributed by Marvell International
# Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/mwifiex/README
index 3b55ce5690a5..31928caeeed2 100644
--- a/drivers/net/wireless/mwifiex/README
+++ b/drivers/net/wireless/mwifiex/README
@@ -1,4 +1,4 @@
-# Copyright (C) 2011, Marvell International Ltd.
+# Copyright (C) 2011-2014, Marvell International Ltd.
#
# This software file (the "File") is distributed by Marvell International
# Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -194,6 +194,36 @@ rdeeprom
Example:
echo "0 20" > rdeeprom : Read 20 bytes of EEPROM data from offset 0
+hscfg
+ This command is used to debug/simulate host sleep feature using
+ different configuration parameters.
+
+ Usage:
+ echo "<condition> [GPIO# [gap]]]" > hscfg
+ cat hscfg
+
+ where the parameters are,
+ <condition>: bit 0 = 1 -- broadcast data
+ bit 1 = 1 -- unicast data
+ bit 2 = 1 -- mac event
+ bit 3 = 1 -- multicast data
+ [GPIO#]: pin number of GPIO used to wakeup the host.
+ GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO
+ will be used instead).
+ [gap]: the gap in milliseconds between wakeup signal and
+ wakeup event or 0xff for special setting (host
+ acknowledge required) when GPIO is used to wakeup host.
+
+ Examples:
+ echo "-1" > hscfg : Cancel host sleep mode
+ echo "3" > hscfg : Broadcast and unicast data;
+ Use GPIO and gap set previously
+ echo "2 3" > hscfg : Unicast data and GPIO 3;
+ Use gap set previously
+ echo "2 1 160" > hscfg : Unicast data, GPIO 1 and gap 160 ms
+ echo "2 1 0xff" > hscfg : Unicast data, GPIO 1; Wait for host
+ to ack before sending wakeup event
+
getlog
This command is used to get the statistics available in the station.
Usage:
diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c
index e95dec91a561..15fa7b453372 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.c
+++ b/drivers/net/wireless/mwifiex/cfg80211.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: CFG80211
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -42,36 +42,6 @@ static const struct ieee80211_iface_combination mwifiex_iface_comb_ap_sta = {
.beacon_int_infra_match = true,
};
-static const struct ieee80211_regdomain mwifiex_world_regdom_custom = {
- .n_reg_rules = 7,
- .alpha2 = "99",
- .reg_rules = {
- /* Channel 1 - 11 */
- REG_RULE(2412-10, 2462+10, 40, 3, 20, 0),
- /* Channel 12 - 13 */
- REG_RULE(2467-10, 2472+10, 20, 3, 20,
- NL80211_RRF_NO_IR),
- /* Channel 14 */
- REG_RULE(2484-10, 2484+10, 20, 3, 20,
- NL80211_RRF_NO_IR |
- NL80211_RRF_NO_OFDM),
- /* Channel 36 - 48 */
- REG_RULE(5180-10, 5240+10, 40, 3, 20,
- NL80211_RRF_NO_IR),
- /* Channel 149 - 165 */
- REG_RULE(5745-10, 5825+10, 40, 3, 20,
- NL80211_RRF_NO_IR),
- /* Channel 52 - 64 */
- REG_RULE(5260-10, 5320+10, 40, 3, 30,
- NL80211_RRF_NO_IR |
- NL80211_RRF_DFS),
- /* Channel 100 - 140 */
- REG_RULE(5500-10, 5700+10, 40, 3, 30,
- NL80211_RRF_NO_IR |
- NL80211_RRF_DFS),
- }
-};
-
/*
* This function maps the nl802.11 channel type into driver channel type.
*
@@ -151,7 +121,6 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len)
u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
u16 pkt_len;
u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT;
- struct timeval tv;
pkt_len = len + ETH_ALEN;
@@ -173,8 +142,7 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len)
len - sizeof(struct ieee80211_hdr_3addr));
skb->priority = LOW_PRIO_TID;
- do_gettimeofday(&tv);
- skb->tstamp = timeval_to_ktime(tv);
+ __net_timestamp(skb);
return 0;
}
@@ -2483,6 +2451,16 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
mef_entry->filter[filt_num].filt_type = TYPE_EQ;
if (filt_num)
mef_entry->filter[filt_num].filt_action = TYPE_OR;
+
+ filt_num++;
+ mef_entry->filter[filt_num].repeat = 16;
+ memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
+ ETH_ALEN);
+ mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
+ ETH_ALEN;
+ mef_entry->filter[filt_num].offset = 56;
+ mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+ mef_entry->filter[filt_num].filt_action = TYPE_OR;
}
if (!mef_cfg.criteria)
@@ -2631,7 +2609,8 @@ static int
mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, u8 action_code, u8 dialog_token,
u16 status_code, u32 peer_capability,
- const u8 *extra_ies, size_t extra_ies_len)
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len)
{
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
int ret;
@@ -2916,12 +2895,6 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter)
wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS |
WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
- wiphy->regulatory_flags |=
- REGULATORY_CUSTOM_REG |
- REGULATORY_STRICT_REG;
-
- wiphy_apply_custom_regulatory(wiphy, &mwifiex_world_regdom_custom);
-
#ifdef CONFIG_PM
wiphy->wowlan = &mwifiex_wowlan_support;
#endif
diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/mwifiex/cfg80211.h
index c5848934f111..908367857d58 100644
--- a/drivers/net/wireless/mwifiex/cfg80211.h
+++ b/drivers/net/wireless/mwifiex/cfg80211.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: CFG80211
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c
index 0ddec3d4b059..b8242eb2be6f 100644
--- a/drivers/net/wireless/mwifiex/cfp.c
+++ b/drivers/net/wireless/mwifiex/cfp.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: Channel, Frequence and Power
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c
index 8dee6c86f4f1..df42f066d70c 100644
--- a/drivers/net/wireless/mwifiex/cmdevt.c
+++ b/drivers/net/wireless/mwifiex/cmdevt.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: commands and events
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -273,6 +273,7 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
(struct mwifiex_opt_sleep_confirm *)
adapter->sleep_cfm->data;
struct sk_buff *sleep_cfm_tmp;
+ struct timeval ts;
__le32 tmp;
priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY);
@@ -283,6 +284,14 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter)
(adapter->seq_num, priv->bss_num,
priv->bss_type)));
+ do_gettimeofday(&ts);
+ dev_dbg(adapter->dev,
+ "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d, seqno %#x\n",
+ ts.tv_sec, ts.tv_usec, le16_to_cpu(sleep_cfm_buf->command),
+ le16_to_cpu(sleep_cfm_buf->action),
+ le16_to_cpu(sleep_cfm_buf->size),
+ le16_to_cpu(sleep_cfm_buf->seq_num));
+
if (adapter->iface_type == MWIFIEX_USB) {
sleep_cfm_tmp =
dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm)
@@ -457,11 +466,10 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter)
rx_info->bss_type = priv->bss_type;
}
- if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE) {
- do_gettimeofday(&tstamp);
- dev_dbg(adapter->dev, "event: %lu.%lu: cause: %#x\n",
- tstamp.tv_sec, tstamp.tv_usec, eventcause);
- } else {
+ do_gettimeofday(&tstamp);
+ dev_dbg(adapter->dev, "EVENT: %lu.%lu: cause: %#x\n",
+ tstamp.tv_sec, tstamp.tv_usec, eventcause);
+ if (eventcause == EVENT_PS_SLEEP || eventcause == EVENT_PS_AWAKE) {
/* Handle PS_SLEEP/AWAKE events on STA */
priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA);
if (!priv)
@@ -960,6 +968,9 @@ mwifiex_cmd_timeout_func(unsigned long function_context)
if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING)
mwifiex_init_fw_complete(adapter);
+ if (adapter->if_ops.fw_dump)
+ adapter->if_ops.fw_dump(adapter);
+
if (adapter->if_ops.card_reset)
adapter->if_ops.card_reset(adapter);
}
@@ -1225,12 +1236,19 @@ mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter,
uint16_t result = le16_to_cpu(cmd->result);
uint16_t command = le16_to_cpu(cmd->command);
uint16_t seq_num = le16_to_cpu(cmd->seq_num);
+ struct timeval ts;
if (!upld_len) {
dev_err(adapter->dev, "%s: cmd size is 0\n", __func__);
return;
}
+ do_gettimeofday(&ts);
+ dev_dbg(adapter->dev,
+ "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d, len %d, seqno 0x%x\n",
+ ts.tv_sec, ts.tv_usec, command, result, le16_to_cpu(cmd->size),
+ seq_num);
+
/* Get BSS number and corresponding priv */
priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num),
HostCmd_GET_BSS_TYPE(seq_num));
diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c
index 7b419bbcd544..2713f7acd35e 100644
--- a/drivers/net/wireless/mwifiex/debugfs.c
+++ b/drivers/net/wireless/mwifiex/debugfs.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: debugfs
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -692,6 +692,97 @@ done:
return ret;
}
+/* Proc hscfg file write handler
+ * This function can be used to configure the host sleep parameters.
+ */
+static ssize_t
+mwifiex_hscfg_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv = (void *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1);
+ int ret, arg_num;
+ struct mwifiex_ds_hs_cfg hscfg;
+ int conditions = HS_CFG_COND_DEF;
+ u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF;
+
+ if (!buf)
+ return -ENOMEM;
+
+ if (copy_from_user(buf, ubuf, buf_size)) {
+ ret = -EFAULT;
+ goto done;
+ }
+
+ arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap);
+
+ memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg));
+
+ if (arg_num > 3) {
+ dev_err(priv->adapter->dev, "Too many arguments\n");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (arg_num >= 1 && arg_num < 3)
+ mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
+ MWIFIEX_SYNC_CMD, &hscfg);
+
+ if (arg_num) {
+ if (conditions == HS_CFG_CANCEL) {
+ mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD);
+ ret = count;
+ goto done;
+ }
+ hscfg.conditions = conditions;
+ }
+ if (arg_num >= 2)
+ hscfg.gpio = gpio;
+ if (arg_num == 3)
+ hscfg.gap = gap;
+
+ hscfg.is_invoke_hostcmd = false;
+ mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET,
+ MWIFIEX_SYNC_CMD, &hscfg);
+
+ mwifiex_enable_hs(priv->adapter);
+ priv->adapter->hs_enabling = false;
+ ret = count;
+done:
+ free_page(addr);
+ return ret;
+}
+
+/* Proc hscfg file read handler
+ * This function can be used to read host sleep configuration
+ * parameters from driver.
+ */
+static ssize_t
+mwifiex_hscfg_read(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct mwifiex_private *priv = (void *)file->private_data;
+ unsigned long addr = get_zeroed_page(GFP_KERNEL);
+ char *buf = (char *)addr;
+ int pos, ret;
+ struct mwifiex_ds_hs_cfg hscfg;
+
+ if (!buf)
+ return -ENOMEM;
+
+ mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET,
+ MWIFIEX_SYNC_CMD, &hscfg);
+
+ pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions,
+ hscfg.gpio, hscfg.gap);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos);
+
+ free_page(addr);
+ return ret;
+}
#define MWIFIEX_DFS_ADD_FILE(name) do { \
if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \
@@ -725,6 +816,7 @@ MWIFIEX_DFS_FILE_READ_OPS(getlog);
MWIFIEX_DFS_FILE_READ_OPS(fw_dump);
MWIFIEX_DFS_FILE_OPS(regrdwr);
MWIFIEX_DFS_FILE_OPS(rdeeprom);
+MWIFIEX_DFS_FILE_OPS(hscfg);
/*
* This function creates the debug FS directory structure and the files.
@@ -747,6 +839,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv)
MWIFIEX_DFS_ADD_FILE(regrdwr);
MWIFIEX_DFS_ADD_FILE(rdeeprom);
MWIFIEX_DFS_ADD_FILE(fw_dump);
+ MWIFIEX_DFS_ADD_FILE(hscfg);
}
/*
diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h
index 38da6ff6f416..0e03fe39fc35 100644
--- a/drivers/net/wireless/mwifiex/decl.h
+++ b/drivers/net/wireless/mwifiex/decl.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: generic data structures and APIs
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c
index bfb39908b2c6..04e56b5fc535 100644
--- a/drivers/net/wireless/mwifiex/ethtool.c
+++ b/drivers/net/wireless/mwifiex/ethtool.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: ethtool
*
- * Copyright (C) 2013, Marvell International Ltd.
+ * Copyright (C) 2013-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -64,7 +64,90 @@ static int mwifiex_ethtool_set_wol(struct net_device *dev,
return 0;
}
+static int
+mwifiex_get_dump_flag(struct net_device *dev, struct ethtool_dump *dump)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct memory_type_mapping *entry;
+
+ if (!adapter->if_ops.fw_dump)
+ return -ENOTSUPP;
+
+ dump->flag = adapter->curr_mem_idx;
+ dump->version = 1;
+ if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) {
+ entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx];
+ dump->len = entry->mem_size;
+ } else {
+ dump->len = 0;
+ }
+
+ return 0;
+}
+
+static int
+mwifiex_get_dump_data(struct net_device *dev, struct ethtool_dump *dump,
+ void *buffer)
+{
+ u8 *p = buffer;
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_adapter *adapter = priv->adapter;
+ struct memory_type_mapping *entry;
+
+ if (!adapter->if_ops.fw_dump)
+ return -ENOTSUPP;
+
+ if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) {
+ dev_err(adapter->dev, "firmware dump in progress!!\n");
+ return -EBUSY;
+ }
+
+ entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx];
+
+ if (!entry->mem_ptr)
+ return -EFAULT;
+
+ memcpy(p, entry->mem_ptr, entry->mem_size);
+
+ entry->mem_size = 0;
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+
+ return 0;
+}
+
+static int mwifiex_set_dump(struct net_device *dev, struct ethtool_dump *val)
+{
+ struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
+ struct mwifiex_adapter *adapter = priv->adapter;
+
+ if (!adapter->if_ops.fw_dump)
+ return -ENOTSUPP;
+
+ if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) {
+ dev_err(adapter->dev, "firmware dump in progress!!\n");
+ return -EBUSY;
+ }
+
+ if (val->flag == MWIFIEX_FW_DUMP_IDX) {
+ adapter->curr_mem_idx = val->flag;
+ adapter->if_ops.fw_dump(adapter);
+ return 0;
+ }
+
+ if (val->flag < 0 || val->flag >= adapter->num_mem_types)
+ return -EINVAL;
+
+ adapter->curr_mem_idx = val->flag;
+
+ return 0;
+}
+
const struct ethtool_ops mwifiex_ethtool_ops = {
.get_wol = mwifiex_ethtool_get_wol,
.set_wol = mwifiex_ethtool_set_wol,
+ .get_dump_flag = mwifiex_get_dump_flag,
+ .get_dump_data = mwifiex_get_dump_data,
+ .set_dump = mwifiex_set_dump,
};
diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h
index 3175dd04834b..5561573452bb 100644
--- a/drivers/net/wireless/mwifiex/fw.h
+++ b/drivers/net/wireless/mwifiex/fw.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: Firmware specific macros & structures
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c
index 3bf3d58bbc02..b933794758b7 100644
--- a/drivers/net/wireless/mwifiex/ie.c
+++ b/drivers/net/wireless/mwifiex/ie.c
@@ -2,7 +2,7 @@
* Marvell Wireless LAN device driver: management IE handling- setting and
* deleting IE.
*
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c
index 4ecd0b208ac6..269a277d0a2e 100644
--- a/drivers/net/wireless/mwifiex/init.c
+++ b/drivers/net/wireless/mwifiex/init.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: HW/FW Initialization
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -382,6 +382,8 @@ static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter)
static void
mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
{
+ int idx;
+
if (!adapter) {
pr_err("%s: adapter is NULL\n", __func__);
return;
@@ -396,7 +398,16 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter)
dev_dbg(adapter->dev, "info: free cmd buffer\n");
mwifiex_free_cmd_buffer(adapter);
- dev_dbg(adapter->dev, "info: free scan table\n");
+ for (idx = 0; idx < adapter->num_mem_types; idx++) {
+ struct memory_type_mapping *entry =
+ &adapter->mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
if (adapter->sleep_cfm)
dev_kfree_skb_any(adapter->sleep_cfm);
diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h
index 1b576722671d..0847f3e07ab7 100644
--- a/drivers/net/wireless/mwifiex/ioctl.h
+++ b/drivers/net/wireless/mwifiex/ioctl.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: ioctl data structures & APIs
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c
index 89dc62a467f4..fc135649b85f 100644
--- a/drivers/net/wireless/mwifiex/join.c
+++ b/drivers/net/wireless/mwifiex/join.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: association and ad-hoc start/join
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c
index cbabc12fbda3..657504c3c79d 100644
--- a/drivers/net/wireless/mwifiex/main.c
+++ b/drivers/net/wireless/mwifiex/main.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: major functions
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -609,7 +609,6 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev);
struct sk_buff *new_skb;
struct mwifiex_txinfo *tx_info;
- struct timeval tv;
dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n",
jiffies, priv->bss_type, priv->bss_num);
@@ -656,8 +655,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
* firmware for aggregate delay calculation for stats and
* MSDU lifetime expiry.
*/
- do_gettimeofday(&tv);
- skb->tstamp = timeval_to_ktime(tv);
+ __net_timestamp(skb);
mwifiex_queue_tx_pkt(priv, skb);
@@ -881,6 +879,8 @@ mwifiex_add_card(void *card, struct semaphore *sem,
goto err_kmalloc;
INIT_WORK(&adapter->main_work, mwifiex_main_work_queue);
+ if (adapter->if_ops.iface_work)
+ INIT_WORK(&adapter->iface_work, adapter->if_ops.iface_work);
/* Register the device. Fill up the private data structure with relevant
information from the card. */
diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h
index 1398afa84064..a2733b1e63f9 100644
--- a/drivers/net/wireless/mwifiex/main.h
+++ b/drivers/net/wireless/mwifiex/main.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: major data structures and prototypes
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -30,6 +30,7 @@
#include <linux/etherdevice.h>
#include <net/sock.h>
#include <net/lib80211.h>
+#include <linux/vmalloc.h>
#include <linux/firmware.h>
#include <linux/ctype.h>
#include <linux/of.h>
@@ -410,6 +411,29 @@ struct mwifiex_roc_cfg {
struct ieee80211_channel chan;
};
+#define MWIFIEX_FW_DUMP_IDX 0xff
+#define FW_DUMP_MAX_NAME_LEN 8
+#define FW_DUMP_HOST_READY 0xEE
+#define FW_DUMP_DONE 0xFF
+
+struct memory_type_mapping {
+ u8 mem_name[FW_DUMP_MAX_NAME_LEN];
+ u8 *mem_ptr;
+ u32 mem_size;
+ u8 done_flag;
+};
+
+enum rdwr_status {
+ RDWR_STATUS_SUCCESS = 0,
+ RDWR_STATUS_FAILURE = 1,
+ RDWR_STATUS_DONE = 2
+};
+
+enum mwifiex_iface_work_flags {
+ MWIFIEX_IFACE_WORK_FW_DUMP,
+ MWIFIEX_IFACE_WORK_CARD_RESET,
+};
+
struct mwifiex_adapter;
struct mwifiex_private;
@@ -674,6 +698,7 @@ struct mwifiex_if_ops {
void (*card_reset) (struct mwifiex_adapter *);
void (*fw_dump)(struct mwifiex_adapter *);
int (*clean_pcie_ring) (struct mwifiex_adapter *adapter);
+ void (*iface_work)(struct work_struct *work);
};
struct mwifiex_adapter {
@@ -809,6 +834,11 @@ struct mwifiex_adapter {
bool ext_scan;
u8 fw_api_ver;
u8 fw_key_api_major_ver, fw_key_api_minor_ver;
+ struct work_struct iface_work;
+ unsigned long iface_work_flags;
+ struct memory_type_mapping *mem_type_mapping_tbl;
+ u8 num_mem_types;
+ u8 curr_mem_idx;
};
int mwifiex_init_lock_list(struct mwifiex_adapter *adapter);
@@ -890,6 +920,8 @@ int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv,
void mwifiex_process_hs_config(struct mwifiex_adapter *adapter);
void mwifiex_hs_activated_event(struct mwifiex_private *priv,
u8 activated);
+int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
+ int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg);
int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv,
struct host_cmd_ds_command *resp);
int mwifiex_process_rx_packet(struct mwifiex_private *priv,
diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c
index 2cc9b6fca490..5f7afffdd34e 100644
--- a/drivers/net/wireless/mwifiex/pcie.c
+++ b/drivers/net/wireless/mwifiex/pcie.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: PCIE specific handling
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -37,6 +37,13 @@ static struct mwifiex_if_ops pcie_ops;
static struct semaphore add_remove_card_sem;
+static struct memory_type_mapping mem_type_mapping_tbl[] = {
+ {"ITCM", NULL, 0, 0xF0},
+ {"DTCM", NULL, 0, 0xF1},
+ {"SQRAM", NULL, 0, 0xF2},
+ {"IRAM", NULL, 0, 0xF3},
+};
+
static int
mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb,
size_t size, int flags)
@@ -192,6 +199,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev,
card->pcie.reg = data->reg;
card->pcie.blksz_fw_dl = data->blksz_fw_dl;
card->pcie.tx_buf_size = data->tx_buf_size;
+ card->pcie.supports_fw_dump = data->supports_fw_dump;
}
if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops,
@@ -221,6 +229,8 @@ static void mwifiex_pcie_remove(struct pci_dev *pdev)
if (!adapter || !adapter->priv_num)
return;
+ cancel_work_sync(&adapter->iface_work);
+
if (user_rmmod) {
#ifdef CONFIG_PM_SLEEP
if (adapter->is_suspended)
@@ -307,6 +317,17 @@ static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data)
return 0;
}
+/* This function reads u8 data from PCIE card register. */
+static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter,
+ int reg, u8 *data)
+{
+ struct pcie_service_card *card = adapter->card;
+
+ *data = ioread8(card->pci_mmap1 + reg);
+
+ return 0;
+}
+
/*
* This function adds delay loop to ensure FW is awake before proceeding.
*/
@@ -2173,6 +2194,174 @@ static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type,
return 0;
}
+/* This function read/write firmware */
+static enum rdwr_status
+mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag)
+{
+ int ret, tries;
+ u8 ctrl_data;
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *reg = card->pcie.reg;
+
+ ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY);
+ if (ret) {
+ dev_err(adapter->dev, "PCIE write err\n");
+ return RDWR_STATUS_FAILURE;
+ }
+
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data);
+ if (ctrl_data == FW_DUMP_DONE)
+ return RDWR_STATUS_SUCCESS;
+ if (doneflag && ctrl_data == doneflag)
+ return RDWR_STATUS_DONE;
+ if (ctrl_data != FW_DUMP_HOST_READY) {
+ dev_info(adapter->dev,
+ "The ctrl reg was changed, re-try again!\n");
+ mwifiex_write_reg(adapter, reg->fw_dump_ctrl,
+ FW_DUMP_HOST_READY);
+ if (ret) {
+ dev_err(adapter->dev, "PCIE write err\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ }
+ usleep_range(100, 200);
+ }
+
+ dev_err(adapter->dev, "Fail to pull ctrl_data\n");
+ return RDWR_STATUS_FAILURE;
+}
+
+/* This function dump firmware memory to file */
+static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter)
+{
+ struct pcie_service_card *card = adapter->card;
+ const struct mwifiex_pcie_card_reg *creg = card->pcie.reg;
+ unsigned int reg, reg_start, reg_end;
+ struct timeval t;
+ u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
+ enum rdwr_status stat;
+ u32 memory_size;
+ static char *env[] = { "DRIVER=mwifiex_pcie", "EVENT=fw_dump", NULL };
+
+ if (!card->pcie.supports_fw_dump)
+ return;
+
+ for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+
+ do_gettimeofday(&t);
+ dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n",
+ (u32)t.tv_sec, (u32)t.tv_usec);
+
+ /* Read the number of the memories which will dump */
+ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg = creg->fw_dump_start;
+ mwifiex_read_reg_byte(adapter, reg, &dump_num);
+
+ /* Read the length of every memory which will dump */
+ for (idx = 0; idx < dump_num; idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ memory_size = 0;
+ reg = creg->fw_dump_start;
+ for (i = 0; i < 4; i++) {
+ mwifiex_read_reg_byte(adapter, reg, &read_reg);
+ memory_size |= (read_reg << (i * 8));
+ reg++;
+ }
+
+ if (memory_size == 0) {
+ dev_info(adapter->dev, "Firmware dump Finished!\n");
+ break;
+ }
+
+ dev_info(adapter->dev,
+ "%s_SIZE=0x%x\n", entry->mem_name, memory_size);
+ entry->mem_ptr = vmalloc(memory_size + 1);
+ entry->mem_size = memory_size;
+ if (!entry->mem_ptr) {
+ dev_err(adapter->dev,
+ "Vmalloc %s failed\n", entry->mem_name);
+ goto done;
+ }
+ dbg_ptr = entry->mem_ptr;
+ end_ptr = dbg_ptr + memory_size;
+
+ doneflag = entry->done_flag;
+ do_gettimeofday(&t);
+ dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n",
+ entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec);
+
+ do {
+ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag);
+ if (RDWR_STATUS_FAILURE == stat)
+ goto done;
+
+ reg_start = creg->fw_dump_start;
+ reg_end = creg->fw_dump_end;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ mwifiex_read_reg_byte(adapter, reg, dbg_ptr);
+ if (dbg_ptr < end_ptr)
+ dbg_ptr++;
+ else
+ dev_err(adapter->dev,
+ "Allocated buf not enough\n");
+ }
+
+ if (stat != RDWR_STATUS_DONE)
+ continue;
+
+ dev_info(adapter->dev, "%s done: size=0x%tx\n",
+ entry->mem_name, dbg_ptr - entry->mem_ptr);
+ break;
+ } while (true);
+ }
+ do_gettimeofday(&t);
+ dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n",
+ (u32)t.tv_sec, (u32)t.tv_usec);
+
+ kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env);
+
+done:
+ adapter->curr_mem_idx = 0;
+}
+
+static void mwifiex_pcie_work(struct work_struct *work)
+{
+ struct mwifiex_adapter *adapter =
+ container_of(work, struct mwifiex_adapter, iface_work);
+
+ if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP,
+ &adapter->iface_work_flags))
+ mwifiex_pcie_fw_dump_work(adapter);
+}
+
+/* This function dumps FW information */
+static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter)
+{
+ if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags))
+ return;
+
+ set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags);
+
+ schedule_work(&adapter->iface_work);
+}
+
/*
* This function initializes the PCI-E host memory space, WCB rings, etc.
*
@@ -2342,6 +2531,8 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
adapter->dev = &pdev->dev;
adapter->tx_buf_size = card->pcie.tx_buf_size;
+ adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
+ adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
strcpy(adapter->fw_name, card->pcie.firmware);
return 0;
@@ -2394,6 +2585,8 @@ static struct mwifiex_if_ops pcie_ops = {
.cleanup_mpa_buf = NULL,
.init_fw_port = mwifiex_pcie_init_fw_port,
.clean_pcie_ring = mwifiex_clean_pcie_ring_buf,
+ .fw_dump = mwifiex_pcie_fw_dump,
+ .iface_work = mwifiex_pcie_work,
};
/*
diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h
index e8ec561f8a64..a1a8fd3bc1be 100644
--- a/drivers/net/wireless/mwifiex/pcie.h
+++ b/drivers/net/wireless/mwifiex/pcie.h
@@ -3,7 +3,7 @@
* @brief This file contains definitions for PCI-E interface.
* driver.
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -129,6 +129,9 @@ struct mwifiex_pcie_card_reg {
u32 ring_tx_start_ptr;
u8 pfu_enabled;
u8 sleep_cookie;
+ u16 fw_dump_ctrl;
+ u16 fw_dump_start;
+ u16 fw_dump_end;
};
static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = {
@@ -191,6 +194,9 @@ static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = {
.ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR,
.pfu_enabled = 1,
.sleep_cookie = 0,
+ .fw_dump_ctrl = 0xcf4,
+ .fw_dump_start = 0xcf8,
+ .fw_dump_end = 0xcff
};
struct mwifiex_pcie_device {
@@ -198,6 +204,7 @@ struct mwifiex_pcie_device {
const struct mwifiex_pcie_card_reg *reg;
u16 blksz_fw_dl;
u16 tx_buf_size;
+ bool supports_fw_dump;
};
static const struct mwifiex_pcie_device mwifiex_pcie8766 = {
@@ -205,6 +212,7 @@ static const struct mwifiex_pcie_device mwifiex_pcie8766 = {
.reg = &mwifiex_reg_8766,
.blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+ .supports_fw_dump = false,
};
static const struct mwifiex_pcie_device mwifiex_pcie8897 = {
@@ -212,6 +220,7 @@ static const struct mwifiex_pcie_device mwifiex_pcie8897 = {
.reg = &mwifiex_reg_8897,
.blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD,
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
+ .supports_fw_dump = true,
};
struct mwifiex_evt_buf_desc {
@@ -322,4 +331,5 @@ mwifiex_pcie_txbd_not_full(struct pcie_service_card *card)
return 0;
}
+
#endif /* _MWIFIEX_PCIE_H */
diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c
index 45c5b3450cf5..dee717a19ddb 100644
--- a/drivers/net/wireless/mwifiex/scan.c
+++ b/drivers/net/wireless/mwifiex/scan.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: scan ioctl and command handling
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index 4ce3d7b33991..1da04a086bd9 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: SDIO specific handling
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -50,6 +50,24 @@ static struct mwifiex_if_ops sdio_ops;
static struct semaphore add_remove_card_sem;
+static struct memory_type_mapping mem_type_mapping_tbl[] = {
+ {"ITCM", NULL, 0, 0xF0},
+ {"DTCM", NULL, 0, 0xF1},
+ {"SQRAM", NULL, 0, 0xF2},
+ {"APU", NULL, 0, 0xF3},
+ {"CIU", NULL, 0, 0xF4},
+ {"ICU", NULL, 0, 0xF5},
+ {"MAC", NULL, 0, 0xF6},
+ {"EXT7", NULL, 0, 0xF7},
+ {"EXT8", NULL, 0, 0xF8},
+ {"EXT9", NULL, 0, 0xF9},
+ {"EXT10", NULL, 0, 0xFA},
+ {"EXT11", NULL, 0, 0xFB},
+ {"EXT12", NULL, 0, 0xFC},
+ {"EXT13", NULL, 0, 0xFD},
+ {"EXTLAST", NULL, 0, 0xFE},
+};
+
/*
* SDIO probe.
*
@@ -87,6 +105,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
card->tx_buf_size = data->tx_buf_size;
card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size;
card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size;
+ card->supports_fw_dump = data->supports_fw_dump;
}
sdio_claim_host(func);
@@ -179,6 +198,8 @@ mwifiex_sdio_remove(struct sdio_func *func)
if (!adapter || !adapter->priv_num)
return;
+ cancel_work_sync(&adapter->iface_work);
+
if (user_rmmod) {
if (adapter->is_suspended)
mwifiex_sdio_resume(adapter->dev);
@@ -1777,6 +1798,8 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
adapter->dev = &func->dev;
strcpy(adapter->fw_name, card->firmware);
+ adapter->mem_type_mapping_tbl = mem_type_mapping_tbl;
+ adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl);
return 0;
}
@@ -1914,10 +1937,10 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port)
port, card->mp_data_port_mask);
}
-static struct mmc_host *reset_host;
-static void sdio_card_reset_worker(struct work_struct *work)
+static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
{
- struct mmc_host *target = reset_host;
+ struct sdio_mmc_card *card = adapter->card;
+ struct mmc_host *target = card->func->card->host;
/* The actual reset operation must be run outside of driver thread.
* This is because mmc_remove_host() will cause the device to be
@@ -1933,15 +1956,213 @@ static void sdio_card_reset_worker(struct work_struct *work)
mdelay(20);
mmc_add_host(target);
}
-static DECLARE_WORK(card_reset_work, sdio_card_reset_worker);
+
+/* This function read/write firmware */
+static enum
+rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter,
+ u8 doneflag)
+{
+ struct sdio_mmc_card *card = adapter->card;
+ int ret, tries;
+ u8 ctrl_data = 0;
+
+ sdio_writeb(card->func, FW_DUMP_HOST_READY, card->reg->fw_dump_ctrl,
+ &ret);
+ if (ret) {
+ dev_err(adapter->dev, "SDIO Write ERR\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ for (tries = 0; tries < MAX_POLL_TRIES; tries++) {
+ ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl,
+ &ret);
+ if (ret) {
+ dev_err(adapter->dev, "SDIO read err\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ if (ctrl_data == FW_DUMP_DONE)
+ break;
+ if (doneflag && ctrl_data == doneflag)
+ return RDWR_STATUS_DONE;
+ if (ctrl_data != FW_DUMP_HOST_READY) {
+ dev_info(adapter->dev,
+ "The ctrl reg was changed, re-try again!\n");
+ sdio_writeb(card->func, FW_DUMP_HOST_READY,
+ card->reg->fw_dump_ctrl, &ret);
+ if (ret) {
+ dev_err(adapter->dev, "SDIO write err\n");
+ return RDWR_STATUS_FAILURE;
+ }
+ }
+ usleep_range(100, 200);
+ }
+ if (ctrl_data == FW_DUMP_HOST_READY) {
+ dev_err(adapter->dev, "Fail to pull ctrl_data\n");
+ return RDWR_STATUS_FAILURE;
+ }
+
+ return RDWR_STATUS_SUCCESS;
+}
+
+/* This function dump firmware memory to file */
+static void mwifiex_sdio_fw_dump_work(struct work_struct *work)
+{
+ struct mwifiex_adapter *adapter =
+ container_of(work, struct mwifiex_adapter, iface_work);
+ struct sdio_mmc_card *card = adapter->card;
+ int ret = 0;
+ unsigned int reg, reg_start, reg_end;
+ u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0;
+ struct timeval t;
+ enum rdwr_status stat;
+ u32 memory_size;
+ static char *env[] = { "DRIVER=mwifiex_sdio", "EVENT=fw_dump", NULL };
+
+ if (!card->supports_fw_dump)
+ return;
+
+ for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ if (entry->mem_ptr) {
+ vfree(entry->mem_ptr);
+ entry->mem_ptr = NULL;
+ }
+ entry->mem_size = 0;
+ }
+
+ mwifiex_pm_wakeup_card(adapter);
+ sdio_claim_host(card->func);
+
+ do_gettimeofday(&t);
+ dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n",
+ (u32)t.tv_sec, (u32)t.tv_usec);
+
+ stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg = card->reg->fw_dump_start;
+ /* Read the number of the memories which will dump */
+ dump_num = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ dev_err(adapter->dev, "SDIO read memory length err\n");
+ goto done;
+ }
+
+ /* Read the length of every memory which will dump */
+ for (idx = 0; idx < dump_num; idx++) {
+ struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx];
+
+ stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ memory_size = 0;
+ reg = card->reg->fw_dump_start;
+ for (i = 0; i < 4; i++) {
+ read_reg = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ dev_err(adapter->dev, "SDIO read err\n");
+ goto done;
+ }
+ memory_size |= (read_reg << i*8);
+ reg++;
+ }
+
+ if (memory_size == 0) {
+ dev_info(adapter->dev, "Firmware dump Finished!\n");
+ break;
+ }
+
+ dev_info(adapter->dev,
+ "%s_SIZE=0x%x\n", entry->mem_name, memory_size);
+ entry->mem_ptr = vmalloc(memory_size + 1);
+ entry->mem_size = memory_size;
+ if (!entry->mem_ptr) {
+ dev_err(adapter->dev, "Vmalloc %s failed\n",
+ entry->mem_name);
+ goto done;
+ }
+ dbg_ptr = entry->mem_ptr;
+ end_ptr = dbg_ptr + memory_size;
+
+ doneflag = entry->done_flag;
+ do_gettimeofday(&t);
+ dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n",
+ entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec);
+
+ do {
+ stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag);
+ if (stat == RDWR_STATUS_FAILURE)
+ goto done;
+
+ reg_start = card->reg->fw_dump_start;
+ reg_end = card->reg->fw_dump_end;
+ for (reg = reg_start; reg <= reg_end; reg++) {
+ *dbg_ptr = sdio_readb(card->func, reg, &ret);
+ if (ret) {
+ dev_err(adapter->dev,
+ "SDIO read err\n");
+ goto done;
+ }
+ if (dbg_ptr < end_ptr)
+ dbg_ptr++;
+ else
+ dev_err(adapter->dev,
+ "Allocated buf not enough\n");
+ }
+
+ if (stat != RDWR_STATUS_DONE)
+ continue;
+
+ dev_info(adapter->dev, "%s done: size=0x%tx\n",
+ entry->mem_name, dbg_ptr - entry->mem_ptr);
+ break;
+ } while (1);
+ }
+ do_gettimeofday(&t);
+ dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n",
+ (u32)t.tv_sec, (u32)t.tv_usec);
+
+ kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env);
+
+done:
+ sdio_release_host(card->func);
+ adapter->curr_mem_idx = 0;
+}
+
+static void mwifiex_sdio_work(struct work_struct *work)
+{
+ struct mwifiex_adapter *adapter =
+ container_of(work, struct mwifiex_adapter, iface_work);
+
+ if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET,
+ &adapter->iface_work_flags))
+ mwifiex_sdio_card_reset_work(adapter);
+ if (test_and_clear_bit(MWIFIEX_IFACE_WORK_FW_DUMP,
+ &adapter->iface_work_flags))
+ mwifiex_sdio_fw_dump_work(work);
+}
/* This function resets the card */
static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter)
{
- struct sdio_mmc_card *card = adapter->card;
+ if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags))
+ return;
+
+ set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &adapter->iface_work_flags);
+
+ schedule_work(&adapter->iface_work);
+}
+
+/* This function dumps FW information */
+static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter)
+{
+ if (test_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags))
+ return;
- reset_host = card->func->card->host;
- schedule_work(&card_reset_work);
+ set_bit(MWIFIEX_IFACE_WORK_FW_DUMP, &adapter->iface_work_flags);
+ schedule_work(&adapter->iface_work);
}
static struct mwifiex_if_ops sdio_ops = {
@@ -1964,6 +2185,8 @@ static struct mwifiex_if_ops sdio_ops = {
.cmdrsp_complete = mwifiex_sdio_cmdrsp_complete,
.event_complete = mwifiex_sdio_event_complete,
.card_reset = mwifiex_sdio_card_reset,
+ .iface_work = mwifiex_sdio_work,
+ .fw_dump = mwifiex_sdio_fw_dump,
};
/*
@@ -2001,7 +2224,6 @@ mwifiex_sdio_cleanup_module(void)
/* Set the flag as user is removing this module. */
user_rmmod = 1;
- cancel_work_sync(&card_reset_work);
sdio_unregister_driver(&mwifiex_sdio);
}
diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h
index 6eea30b43ed7..6b8835ec88f1 100644
--- a/drivers/net/wireless/mwifiex/sdio.h
+++ b/drivers/net/wireless/mwifiex/sdio.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: SDIO specific definitions
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -219,6 +219,9 @@ struct mwifiex_sdio_card_reg {
u8 rd_len_p0_l;
u8 rd_len_p0_u;
u8 card_misc_cfg_reg;
+ u8 fw_dump_ctrl;
+ u8 fw_dump_start;
+ u8 fw_dump_end;
};
struct sdio_mmc_card {
@@ -231,6 +234,7 @@ struct sdio_mmc_card {
u8 mp_agg_pkt_limit;
bool supports_sdio_new_mode;
bool has_control_mask;
+ bool supports_fw_dump;
u16 tx_buf_size;
u32 mp_tx_agg_buf_size;
u32 mp_rx_agg_buf_size;
@@ -257,6 +261,7 @@ struct mwifiex_sdio_device {
u8 mp_agg_pkt_limit;
bool supports_sdio_new_mode;
bool has_control_mask;
+ bool supports_fw_dump;
u16 tx_buf_size;
u32 mp_tx_agg_buf_size;
u32 mp_rx_agg_buf_size;
@@ -307,6 +312,9 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = {
.rd_len_p0_l = 0x0c,
.rd_len_p0_u = 0x0d,
.card_misc_cfg_reg = 0xcc,
+ .fw_dump_ctrl = 0xe2,
+ .fw_dump_start = 0xe3,
+ .fw_dump_end = 0xea,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
@@ -319,6 +327,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+ .supports_fw_dump = false,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
@@ -331,6 +340,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+ .supports_fw_dump = false,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
@@ -343,6 +353,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K,
+ .supports_fw_dump = false,
};
static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
@@ -355,6 +366,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
.tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
.mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
.mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K,
+ .supports_fw_dump = true,
};
/*
diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c
index 88202ce0c139..0f077aaadab6 100644
--- a/drivers/net/wireless/mwifiex/sta_cmd.c
+++ b/drivers/net/wireless/mwifiex/sta_cmd.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: station command handling
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c
index 577f2979ed8f..822357b7b0bb 100644
--- a/drivers/net/wireless/mwifiex/sta_cmdresp.c
+++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: station command response handling
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c
index f6395ef11a72..f1c240eca0cd 100644
--- a/drivers/net/wireless/mwifiex/sta_event.c
+++ b/drivers/net/wireless/mwifiex/sta_event.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: station event handling
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c
index 536c14aa71f3..1a03d4d8b418 100644
--- a/drivers/net/wireless/mwifiex/sta_ioctl.c
+++ b/drivers/net/wireless/mwifiex/sta_ioctl.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: functions for station ioctl
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -26,7 +26,7 @@
#include "11n.h"
#include "cfg80211.h"
-static int disconnect_on_suspend = 1;
+static int disconnect_on_suspend;
module_param(disconnect_on_suspend, int, 0644);
/*
@@ -389,8 +389,8 @@ done:
* This function prepares the correct firmware command and
* issues it.
*/
-static int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
- int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
+int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action,
+ int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg)
{
struct mwifiex_adapter *adapter = priv->adapter;
diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c
index 8b639d7fe6df..9ceb1dbe34c5 100644
--- a/drivers/net/wireless/mwifiex/sta_rx.c
+++ b/drivers/net/wireless/mwifiex/sta_rx.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: station RX data handling
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c
index 5fce7e78a36e..cf330ba951cd 100644
--- a/drivers/net/wireless/mwifiex/sta_tx.c
+++ b/drivers/net/wireless/mwifiex/sta_tx.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: station TX data handling
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c
index e73034fbbde9..3efbcbe7e891 100644
--- a/drivers/net/wireless/mwifiex/tdls.c
+++ b/drivers/net/wireless/mwifiex/tdls.c
@@ -530,7 +530,6 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
{
struct sk_buff *skb;
struct mwifiex_txinfo *tx_info;
- struct timeval tv;
int ret;
u16 skb_len;
@@ -608,8 +607,7 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer,
tx_info->bss_num = priv->bss_num;
tx_info->bss_type = priv->bss_type;
- do_gettimeofday(&tv);
- skb->tstamp = timeval_to_ktime(tv);
+ __net_timestamp(skb);
mwifiex_queue_tx_pkt(priv, skb);
return 0;
@@ -702,7 +700,6 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
{
struct sk_buff *skb;
struct mwifiex_txinfo *tx_info;
- struct timeval tv;
u8 *pos;
u32 pkt_type, tx_control;
u16 pkt_len, skb_len;
@@ -767,8 +764,7 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer,
pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len);
memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len,
sizeof(pkt_len));
- do_gettimeofday(&tv);
- skb->tstamp = timeval_to_ktime(tv);
+ __net_timestamp(skb);
mwifiex_queue_tx_pkt(priv, skb);
return 0;
diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c
index 37f26afd4314..08205683f877 100644
--- a/drivers/net/wireless/mwifiex/txrx.c
+++ b/drivers/net/wireless/mwifiex/txrx.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: generic TX/RX data handling
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c
index 32643555dd2a..300bab438011 100644
--- a/drivers/net/wireless/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/mwifiex/uap_cmd.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: AP specific command handling
*
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c
index 92e77a398ecf..7c2b97660a03 100644
--- a/drivers/net/wireless/mwifiex/uap_event.c
+++ b/drivers/net/wireless/mwifiex/uap_event.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: AP event handling
*
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c
index 9a56bc61cb1d..ddfc3c6c1e78 100644
--- a/drivers/net/wireless/mwifiex/uap_txrx.c
+++ b/drivers/net/wireless/mwifiex/uap_txrx.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: AP TX and RX data handling
*
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -96,7 +96,6 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
struct sk_buff *new_skb;
struct mwifiex_txinfo *tx_info;
int hdr_chop;
- struct timeval tv;
struct ethhdr *p_ethhdr;
uap_rx_pd = (struct uap_rxpd *)(skb->data);
@@ -192,8 +191,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv,
tx_info->pkt_len = skb->len;
}
- do_gettimeofday(&tv);
- skb->tstamp = timeval_to_ktime(tv);
+ __net_timestamp(skb);
mwifiex_wmm_add_buf_txqueue(priv, skb);
atomic_inc(&adapter->tx_pending);
atomic_inc(&adapter->pending_bridged_pkts);
diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c
index a8ce8130cfae..7118a18b91ba 100644
--- a/drivers/net/wireless/mwifiex/usb.c
+++ b/drivers/net/wireless/mwifiex/usb.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: USB specific handling
*
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h
index 15b73d12e998..4c41c2a193c5 100644
--- a/drivers/net/wireless/mwifiex/usb.h
+++ b/drivers/net/wireless/mwifiex/usb.h
@@ -1,7 +1,7 @@
/*
* This file contains definitions for mwifiex USB interface driver.
*
- * Copyright (C) 2012, Marvell International Ltd.
+ * Copyright (C) 2012-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c
index 6da5abf52e61..cee028321a9a 100644
--- a/drivers/net/wireless/mwifiex/util.c
+++ b/drivers/net/wireless/mwifiex/util.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: utility functions
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h
index caadb3737b9e..40296cb4a3f1 100644
--- a/drivers/net/wireless/mwifiex/util.h
+++ b/drivers/net/wireless/mwifiex/util.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: utility functions
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c
index d3671d009f6c..94c98a86ebbe 100644
--- a/drivers/net/wireless/mwifiex/wmm.c
+++ b/drivers/net/wireless/mwifiex/wmm.c
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: WMM
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
@@ -878,15 +878,8 @@ u8
mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv,
const struct sk_buff *skb)
{
+ u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp));
u8 ret_val;
- struct timeval out_tstamp, in_tstamp;
- u32 queue_delay;
-
- do_gettimeofday(&out_tstamp);
- in_tstamp = ktime_to_timeval(skb->tstamp);
-
- queue_delay = (out_tstamp.tv_sec - in_tstamp.tv_sec) * 1000;
- queue_delay += (out_tstamp.tv_usec - in_tstamp.tv_usec) / 1000;
/*
* Queue delay is passed as a uint8 in units of 2ms (ms shifted
diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h
index eca56e371a57..569bd73f33c5 100644
--- a/drivers/net/wireless/mwifiex/wmm.h
+++ b/drivers/net/wireless/mwifiex/wmm.h
@@ -1,7 +1,7 @@
/*
* Marvell Wireless LAN device driver: WMM
*
- * Copyright (C) 2011, Marvell International Ltd.
+ * Copyright (C) 2011-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig
index 60819bcf4377..60698b020851 100644
--- a/drivers/net/wireless/orinoco/Kconfig
+++ b/drivers/net/wireless/orinoco/Kconfig
@@ -107,7 +107,7 @@ config PCI_HERMES
config PCMCIA_HERMES
tristate "Hermes PCMCIA card support"
- depends on PCMCIA && HERMES
+ depends on PCMCIA && HERMES && HAS_IOPORT_MAP
---help---
A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
@@ -122,7 +122,7 @@ config PCMCIA_HERMES
config PCMCIA_SPECTRUM
tristate "Symbol Spectrum24 Trilogy PCMCIA card support"
- depends on PCMCIA && HERMES
+ depends on PCMCIA && HERMES && HAS_IOPORT_MAP
---help---
This is a driver for 802.11b cards using RAM-loadable Symbol
diff --git a/drivers/net/wireless/p54/p54spi.c b/drivers/net/wireless/p54/p54spi.c
index de15171e2cd8..63de5eed25cf 100644
--- a/drivers/net/wireless/p54/p54spi.c
+++ b/drivers/net/wireless/p54/p54spi.c
@@ -193,7 +193,7 @@ static int p54spi_request_eeprom(struct ieee80211_hw *dev)
/* allow users to customize their eeprom.
*/
- ret = request_firmware(&eeprom, "3826.eeprom", &priv->spi->dev);
+ ret = request_firmware_direct(&eeprom, "3826.eeprom", &priv->spi->dev);
if (ret < 0) {
#ifdef CONFIG_P54_SPI_DEFAULT_EEPROM
dev_info(&priv->spi->dev, "loading default eeprom...\n");
diff --git a/drivers/net/wireless/rsi/rsi_91x_core.c b/drivers/net/wireless/rsi/rsi_91x_core.c
index cf61d6e3eaa7..f3d3995d8f6b 100644
--- a/drivers/net/wireless/rsi/rsi_91x_core.c
+++ b/drivers/net/wireless/rsi/rsi_91x_core.c
@@ -77,6 +77,52 @@ static bool rsi_recalculate_weights(struct rsi_common *common)
}
/**
+ * rsi_get_num_pkts_dequeue() - This function determines the number of
+ * packets to be dequeued based on the number
+ * of bytes calculated using txop.
+ *
+ * @common: Pointer to the driver private structure.
+ * @q_num: the queue from which pkts have to be dequeued
+ *
+ * Return: pkt_num: Number of pkts to be dequeued.
+ */
+static u32 rsi_get_num_pkts_dequeue(struct rsi_common *common, u8 q_num)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct sk_buff *skb;
+ u32 pkt_cnt = 0;
+ s16 txop = common->tx_qinfo[q_num].txop * 32;
+ __le16 r_txop;
+ struct ieee80211_rate rate;
+
+ rate.bitrate = RSI_RATE_MCS0 * 5 * 10; /* Convert to Kbps */
+ if (q_num == VI_Q)
+ txop = ((txop << 5) / 80);
+
+ if (skb_queue_len(&common->tx_queue[q_num]))
+ skb = skb_peek(&common->tx_queue[q_num]);
+ else
+ return 0;
+
+ do {
+ r_txop = ieee80211_generic_frame_duration(adapter->hw,
+ adapter->vifs[0],
+ common->band,
+ skb->len, &rate);
+ txop -= le16_to_cpu(r_txop);
+ pkt_cnt += 1;
+ /*checking if pkts are still there*/
+ if (skb_queue_len(&common->tx_queue[q_num]) - pkt_cnt)
+ skb = skb->next;
+ else
+ break;
+
+ } while (txop > 0);
+
+ return pkt_cnt;
+}
+
+/**
* rsi_core_determine_hal_queue() - This function determines the queue from
* which packet has to be dequeued.
* @common: Pointer to the driver private structure.
@@ -88,7 +134,7 @@ static u8 rsi_core_determine_hal_queue(struct rsi_common *common)
bool recontend_queue = false;
u32 q_len = 0;
u8 q_num = INVALID_QUEUE;
- u8 ii = 0, min = 0;
+ u8 ii = 0;
if (skb_queue_len(&common->tx_queue[MGMT_SOFT_Q])) {
if (!common->mgmt_q_block)
@@ -96,6 +142,9 @@ static u8 rsi_core_determine_hal_queue(struct rsi_common *common)
return q_num;
}
+ if (common->hw_data_qs_blocked)
+ return q_num;
+
if (common->pkt_cnt != 0) {
--common->pkt_cnt;
return common->selected_qnum;
@@ -106,14 +155,15 @@ get_queue_num:
q_num = rsi_determine_min_weight_queue(common);
- q_len = skb_queue_len(&common->tx_queue[ii]);
ii = q_num;
/* Selecting the queue with least back off */
for (; ii < NUM_EDCA_QUEUES; ii++) {
+ q_len = skb_queue_len(&common->tx_queue[ii]);
if (((common->tx_qinfo[ii].pkt_contended) &&
- (common->tx_qinfo[ii].weight < min)) && q_len) {
- min = common->tx_qinfo[ii].weight;
+ (common->tx_qinfo[ii].weight < common->min_weight)) &&
+ q_len) {
+ common->min_weight = common->tx_qinfo[ii].weight;
q_num = ii;
}
}
@@ -140,25 +190,9 @@ get_queue_num:
common->selected_qnum = q_num;
q_len = skb_queue_len(&common->tx_queue[q_num]);
- switch (common->selected_qnum) {
- case VO_Q:
- if (q_len > MAX_CONTINUOUS_VO_PKTS)
- common->pkt_cnt = (MAX_CONTINUOUS_VO_PKTS - 1);
- else
- common->pkt_cnt = --q_len;
- break;
-
- case VI_Q:
- if (q_len > MAX_CONTINUOUS_VI_PKTS)
- common->pkt_cnt = (MAX_CONTINUOUS_VI_PKTS - 1);
- else
- common->pkt_cnt = --q_len;
-
- break;
-
- default:
- common->pkt_cnt = 0;
- break;
+ if (q_num == VO_Q || q_num == VI_Q) {
+ common->pkt_cnt = rsi_get_num_pkts_dequeue(common, q_num);
+ common->pkt_cnt -= 1;
}
return q_num;
@@ -252,6 +286,7 @@ void rsi_core_qos_processor(struct rsi_common *common)
skb = rsi_core_dequeue_pkt(common, q_num);
if (skb == NULL) {
+ rsi_dbg(ERR_ZONE, "skb null\n");
mutex_unlock(&common->tx_rxlock);
break;
}
@@ -306,7 +341,8 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
}
if ((ieee80211_is_mgmt(tmp_hdr->frame_control)) ||
- (ieee80211_is_ctl(tmp_hdr->frame_control))) {
+ (ieee80211_is_ctl(tmp_hdr->frame_control)) ||
+ (ieee80211_is_qos_nullfunc(tmp_hdr->frame_control))) {
q_num = MGMT_SOFT_Q;
skb->priority = q_num;
} else {
@@ -325,6 +361,7 @@ void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb)
if ((q_num != MGMT_SOFT_Q) &&
((skb_queue_len(&common->tx_queue[q_num]) + 1) >=
DATA_QUEUE_WATER_MARK)) {
+ rsi_dbg(ERR_ZONE, "%s: sw queue full\n", __func__);
if (!ieee80211_queue_stopped(adapter->hw, WME_AC(q_num)))
ieee80211_stop_queue(adapter->hw, WME_AC(q_num));
rsi_set_event(&common->tx_thread.event);
diff --git a/drivers/net/wireless/rsi/rsi_91x_debugfs.c b/drivers/net/wireless/rsi/rsi_91x_debugfs.c
index c466246a323f..828a042f903f 100644
--- a/drivers/net/wireless/rsi/rsi_91x_debugfs.c
+++ b/drivers/net/wireless/rsi/rsi_91x_debugfs.c
@@ -145,7 +145,7 @@ static int rsi_stats_read(struct seq_file *seq, void *data)
seq_printf(seq, "total_mgmt_pkt_send : %d\n",
common->tx_stats.total_tx_pkt_send[MGMT_SOFT_Q]);
seq_printf(seq, "total_mgmt_pkt_queued : %d\n",
- skb_queue_len(&common->tx_queue[4]));
+ skb_queue_len(&common->tx_queue[MGMT_SOFT_Q]));
seq_printf(seq, "total_mgmt_pkt_freed : %d\n",
common->tx_stats.total_tx_pkt_freed[MGMT_SOFT_Q]);
@@ -153,25 +153,25 @@ static int rsi_stats_read(struct seq_file *seq, void *data)
seq_printf(seq, "total_data_vo_pkt_send: %8d\t",
common->tx_stats.total_tx_pkt_send[VO_Q]);
seq_printf(seq, "total_data_vo_pkt_queued: %8d\t",
- skb_queue_len(&common->tx_queue[0]));
+ skb_queue_len(&common->tx_queue[VO_Q]));
seq_printf(seq, "total_vo_pkt_freed: %8d\n",
common->tx_stats.total_tx_pkt_freed[VO_Q]);
seq_printf(seq, "total_data_vi_pkt_send: %8d\t",
common->tx_stats.total_tx_pkt_send[VI_Q]);
seq_printf(seq, "total_data_vi_pkt_queued: %8d\t",
- skb_queue_len(&common->tx_queue[1]));
+ skb_queue_len(&common->tx_queue[VI_Q]));
seq_printf(seq, "total_vi_pkt_freed: %8d\n",
common->tx_stats.total_tx_pkt_freed[VI_Q]);
seq_printf(seq, "total_data_be_pkt_send: %8d\t",
common->tx_stats.total_tx_pkt_send[BE_Q]);
seq_printf(seq, "total_data_be_pkt_queued: %8d\t",
- skb_queue_len(&common->tx_queue[2]));
+ skb_queue_len(&common->tx_queue[BE_Q]));
seq_printf(seq, "total_be_pkt_freed: %8d\n",
common->tx_stats.total_tx_pkt_freed[BE_Q]);
seq_printf(seq, "total_data_bk_pkt_send: %8d\t",
common->tx_stats.total_tx_pkt_send[BK_Q]);
seq_printf(seq, "total_data_bk_pkt_queued: %8d\t",
- skb_queue_len(&common->tx_queue[3]));
+ skb_queue_len(&common->tx_queue[BK_Q]));
seq_printf(seq, "total_bk_pkt_freed: %8d\n",
common->tx_stats.total_tx_pkt_freed[BK_Q]);
diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
index 54aaeb09debf..aeaf87bb5518 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c
@@ -177,7 +177,7 @@ static void rsi_register_rates_channels(struct rsi_hw *adapter, int band)
sbands->ht_cap.cap = (IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
IEEE80211_HT_CAP_SGI_20 |
IEEE80211_HT_CAP_SGI_40);
- sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K;
+ sbands->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_16K;
sbands->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE;
sbands->ht_cap.mcs.rx_mask[0] = 0xff;
sbands->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
@@ -185,7 +185,7 @@ static void rsi_register_rates_channels(struct rsi_hw *adapter, int band)
}
/**
- * rsi_mac80211_attach() - This function is used to de-initialize the
+ * rsi_mac80211_detach() - This function is used to de-initialize the
* Mac80211 stack.
* @adapter: Pointer to the adapter structure.
*
@@ -341,6 +341,59 @@ static void rsi_mac80211_remove_interface(struct ieee80211_hw *hw,
}
/**
+ * rsi_channel_change() - This function is a performs the checks
+ * required for changing a channel and sets
+ * the channel accordingly.
+ * @hw: Pointer to the ieee80211_hw structure.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+static int rsi_channel_change(struct ieee80211_hw *hw)
+{
+ struct rsi_hw *adapter = hw->priv;
+ struct rsi_common *common = adapter->priv;
+ int status = -EOPNOTSUPP;
+ struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+ u16 channel = curchan->hw_value;
+ struct ieee80211_bss_conf *bss = &adapter->vifs[0]->bss_conf;
+
+ rsi_dbg(INFO_ZONE,
+ "%s: Set channel: %d MHz type: %d channel_no %d\n",
+ __func__, curchan->center_freq,
+ curchan->flags, channel);
+
+ if (bss->assoc) {
+ if (!common->hw_data_qs_blocked &&
+ (rsi_get_connected_channel(adapter) != channel)) {
+ rsi_dbg(INFO_ZONE, "blk data q %d\n", channel);
+ if (!rsi_send_block_unblock_frame(common, true))
+ common->hw_data_qs_blocked = true;
+ }
+ }
+
+ status = rsi_band_check(common);
+ if (!status)
+ status = rsi_set_channel(adapter->priv, channel);
+
+ if (bss->assoc) {
+ if (common->hw_data_qs_blocked &&
+ (rsi_get_connected_channel(adapter) == channel)) {
+ rsi_dbg(INFO_ZONE, "unblk data q %d\n", channel);
+ if (!rsi_send_block_unblock_frame(common, false))
+ common->hw_data_qs_blocked = false;
+ }
+ } else {
+ if (common->hw_data_qs_blocked) {
+ rsi_dbg(INFO_ZONE, "unblk data q %d\n", channel);
+ if (!rsi_send_block_unblock_frame(common, false))
+ common->hw_data_qs_blocked = false;
+ }
+ }
+
+ return status;
+}
+
+/**
* rsi_mac80211_config() - This function is a handler for configuration
* requests. The stack calls this function to
* change hardware configuration, e.g., channel.
@@ -357,17 +410,10 @@ static int rsi_mac80211_config(struct ieee80211_hw *hw,
int status = -EOPNOTSUPP;
mutex_lock(&common->mutex);
- if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
- struct ieee80211_channel *curchan = hw->conf.chandef.chan;
- u16 channel = curchan->hw_value;
-
- rsi_dbg(INFO_ZONE,
- "%s: Set channel: %d MHz type: %d channel_no %d\n",
- __func__, curchan->center_freq,
- curchan->flags, channel);
- common->band = curchan->band;
- status = rsi_set_channel(adapter->priv, channel);
- }
+
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
+ status = rsi_channel_change(hw);
+
mutex_unlock(&common->mutex);
return status;
@@ -421,6 +467,15 @@ static void rsi_mac80211_bss_info_changed(struct ieee80211_hw *hw,
bss_conf->qos,
bss_conf->aid);
}
+
+ if (changed & BSS_CHANGED_CQM) {
+ common->cqm_info.last_cqm_event_rssi = 0;
+ common->cqm_info.rssi_thold = bss_conf->cqm_rssi_thold;
+ common->cqm_info.rssi_hyst = bss_conf->cqm_rssi_hyst;
+ rsi_dbg(INFO_ZONE, "RSSI throld & hysteresis are: %d %d\n",
+ common->cqm_info.rssi_thold,
+ common->cqm_info.rssi_hyst);
+ }
mutex_unlock(&common->mutex);
}
@@ -723,17 +778,17 @@ static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw,
{
struct rsi_hw *adapter = hw->priv;
struct rsi_common *common = adapter->priv;
+ enum ieee80211_band band = hw->conf.chandef.chan->band;
mutex_lock(&common->mutex);
+ common->fixedrate_mask[band] = 0;
- common->fixedrate_mask[IEEE80211_BAND_2GHZ] = 0;
-
- if (mask->control[IEEE80211_BAND_2GHZ].legacy == 0xfff) {
- common->fixedrate_mask[IEEE80211_BAND_2GHZ] =
- (mask->control[IEEE80211_BAND_2GHZ].ht_mcs[0] << 12);
+ if (mask->control[band].legacy == 0xfff) {
+ common->fixedrate_mask[band] =
+ (mask->control[band].ht_mcs[0] << 12);
} else {
- common->fixedrate_mask[IEEE80211_BAND_2GHZ] =
- mask->control[IEEE80211_BAND_2GHZ].legacy;
+ common->fixedrate_mask[band] =
+ mask->control[band].legacy;
}
mutex_unlock(&common->mutex);
@@ -741,6 +796,37 @@ static int rsi_mac80211_set_rate_mask(struct ieee80211_hw *hw,
}
/**
+ * rsi_perform_cqm() - This function performs cqm.
+ * @common: Pointer to the driver private structure.
+ * @bssid: pointer to the bssid.
+ * @rssi: RSSI value.
+ */
+static void rsi_perform_cqm(struct rsi_common *common,
+ u8 *bssid,
+ s8 rssi)
+{
+ struct rsi_hw *adapter = common->priv;
+ s8 last_event = common->cqm_info.last_cqm_event_rssi;
+ int thold = common->cqm_info.rssi_thold;
+ u32 hyst = common->cqm_info.rssi_hyst;
+ enum nl80211_cqm_rssi_threshold_event event;
+
+ if (rssi < thold && (last_event == 0 || rssi < (last_event - hyst)))
+ event = NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
+ else if (rssi > thold &&
+ (last_event == 0 || rssi > (last_event + hyst)))
+ event = NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+ else
+ return;
+
+ common->cqm_info.last_cqm_event_rssi = rssi;
+ rsi_dbg(INFO_ZONE, "CQM: Notifying event: %d\n", event);
+ ieee80211_cqm_rssi_notify(adapter->vifs[0], event, GFP_KERNEL);
+
+ return;
+}
+
+/**
* rsi_fill_rx_status() - This function fills rx status in
* ieee80211_rx_status structure.
* @hw: Pointer to the ieee80211_hw structure.
@@ -755,6 +841,7 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
struct rsi_common *common,
struct ieee80211_rx_status *rxs)
{
+ struct ieee80211_bss_conf *bss = &common->priv->vifs[0]->bss_conf;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct skb_info *rx_params = (struct skb_info *)info->driver_data;
struct ieee80211_hdr *hdr;
@@ -770,10 +857,7 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
rxs->signal = -(rssi);
- if (channel <= 14)
- rxs->band = IEEE80211_BAND_2GHZ;
- else
- rxs->band = IEEE80211_BAND_5GHZ;
+ rxs->band = common->band;
freq = ieee80211_channel_to_frequency(channel, rxs->band);
@@ -792,6 +876,14 @@ static void rsi_fill_rx_status(struct ieee80211_hw *hw,
rxs->flag |= RX_FLAG_DECRYPTED;
rxs->flag |= RX_FLAG_IV_STRIPPED;
}
+
+ /* CQM only for connected AP beacons, the RSSI is a weighted avg */
+ if (bss->assoc && !(memcmp(bss->bssid, hdr->addr2, ETH_ALEN))) {
+ if (ieee80211_is_beacon(hdr->frame_control))
+ rsi_perform_cqm(common, hdr->addr2, rxs->signal);
+ }
+
+ return;
}
/**
@@ -983,6 +1075,7 @@ int rsi_mac80211_attach(struct rsi_common *common)
hw->max_tx_aggregation_subframes = 6;
rsi_register_rates_channels(adapter, IEEE80211_BAND_2GHZ);
+ rsi_register_rates_channels(adapter, IEEE80211_BAND_5GHZ);
hw->rate_control_algorithm = "AARF";
SET_IEEE80211_PERM_ADDR(hw, common->mac_addr);
@@ -1000,6 +1093,8 @@ int rsi_mac80211_attach(struct rsi_common *common)
wiphy->available_antennas_tx = 1;
wiphy->bands[IEEE80211_BAND_2GHZ] =
&adapter->sbands[IEEE80211_BAND_2GHZ];
+ wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &adapter->sbands[IEEE80211_BAND_5GHZ];
status = ieee80211_register_hw(hw);
if (status)
diff --git a/drivers/net/wireless/rsi/rsi_91x_mgmt.c b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
index 2eefbf159bc0..8d110fd9eba1 100644
--- a/drivers/net/wireless/rsi/rsi_91x_mgmt.c
+++ b/drivers/net/wireless/rsi/rsi_91x_mgmt.c
@@ -217,6 +217,7 @@ static void rsi_set_default_parameters(struct rsi_common *common)
common->min_rate = 0xffff;
common->fsm_state = FSM_CARD_NOT_READY;
common->iface_down = true;
+ common->endpoint = EP_2GHZ_20MHZ;
}
/**
@@ -276,7 +277,6 @@ static int rsi_load_radio_caps(struct rsi_common *common)
{
struct rsi_radio_caps *radio_caps;
struct rsi_hw *adapter = common->priv;
- struct ieee80211_hw *hw = adapter->hw;
u16 inx = 0;
u8 ii;
u8 radio_id = 0;
@@ -285,7 +285,6 @@ static int rsi_load_radio_caps(struct rsi_common *common)
0xf0, 0xf0, 0xf0, 0xf0,
0xf0, 0xf0, 0xf0, 0xf0,
0xf0, 0xf0, 0xf0, 0xf0};
- struct ieee80211_conf *conf = &hw->conf;
struct sk_buff *skb;
rsi_dbg(INFO_ZONE, "%s: Sending rate symbol req frame\n", __func__);
@@ -307,29 +306,36 @@ static int rsi_load_radio_caps(struct rsi_common *common)
if (common->channel_width == BW_40MHZ) {
radio_caps->desc_word[7] |= cpu_to_le16(RSI_LMAC_CLOCK_80MHZ);
radio_caps->desc_word[7] |= cpu_to_le16(RSI_ENABLE_40MHZ);
- if (common->channel_width) {
- radio_caps->desc_word[5] =
- cpu_to_le16(common->channel_width << 12);
- radio_caps->desc_word[5] |= cpu_to_le16(FULL40M_ENABLE);
- }
- if (conf_is_ht40_minus(conf)) {
- radio_caps->desc_word[5] = 0;
- radio_caps->desc_word[5] |=
- cpu_to_le16(LOWER_20_ENABLE);
- radio_caps->desc_word[5] |=
- cpu_to_le16(LOWER_20_ENABLE >> 12);
- }
-
- if (conf_is_ht40_plus(conf)) {
- radio_caps->desc_word[5] = 0;
- radio_caps->desc_word[5] |=
- cpu_to_le16(UPPER_20_ENABLE);
- radio_caps->desc_word[5] |=
- cpu_to_le16(UPPER_20_ENABLE >> 12);
+ if (common->fsm_state == FSM_MAC_INIT_DONE) {
+ struct ieee80211_hw *hw = adapter->hw;
+ struct ieee80211_conf *conf = &hw->conf;
+ if (conf_is_ht40_plus(conf)) {
+ radio_caps->desc_word[5] =
+ cpu_to_le16(LOWER_20_ENABLE);
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(LOWER_20_ENABLE >> 12);
+ } else if (conf_is_ht40_minus(conf)) {
+ radio_caps->desc_word[5] =
+ cpu_to_le16(UPPER_20_ENABLE);
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(UPPER_20_ENABLE >> 12);
+ } else {
+ radio_caps->desc_word[5] =
+ cpu_to_le16(BW_40MHZ << 12);
+ radio_caps->desc_word[5] |=
+ cpu_to_le16(FULL40M_ENABLE);
+ }
}
}
+ radio_caps->sifs_tx_11n = cpu_to_le16(SIFS_TX_11N_VALUE);
+ radio_caps->sifs_tx_11b = cpu_to_le16(SIFS_TX_11B_VALUE);
+ radio_caps->slot_rx_11n = cpu_to_le16(SHORT_SLOT_VALUE);
+ radio_caps->ofdm_ack_tout = cpu_to_le16(OFDM_ACK_TOUT_VALUE);
+ radio_caps->cck_ack_tout = cpu_to_le16(CCK_ACK_TOUT_VALUE);
+ radio_caps->preamble_type = cpu_to_le16(LONG_PREAMBLE);
+
radio_caps->desc_word[7] |= cpu_to_le16(radio_id << 8);
for (ii = 0; ii < MAX_HW_QUEUES; ii++) {
@@ -588,7 +594,7 @@ static int rsi_program_bb_rf(struct rsi_common *common)
mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
mgmt_frame->desc_word[1] = cpu_to_le16(BBP_PROG_IN_TA);
- mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint << 8);
+ mgmt_frame->desc_word[4] = cpu_to_le16(common->endpoint);
if (common->rf_reset) {
mgmt_frame->desc_word[7] = cpu_to_le16(RF_RESET_ENABLE);
@@ -615,6 +621,9 @@ int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode)
{
struct sk_buff *skb = NULL;
struct rsi_vap_caps *vap_caps;
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_hw *hw = adapter->hw;
+ struct ieee80211_conf *conf = &hw->conf;
u16 vap_id = 0;
rsi_dbg(MGMT_TX_ZONE, "%s: Sending VAP capabilities frame\n", __func__);
@@ -644,13 +653,24 @@ int rsi_set_vap_capabilities(struct rsi_common *common, enum opmode mode)
vap_caps->frag_threshold = cpu_to_le16(IEEE80211_MAX_FRAG_THRESHOLD);
vap_caps->rts_threshold = cpu_to_le16(common->rts_threshold);
- vap_caps->default_mgmt_rate = 0;
- if (conf_is_ht40(&common->priv->hw->conf)) {
- vap_caps->default_ctrl_rate =
- cpu_to_le32(RSI_RATE_6 | FULL40M_ENABLE << 16);
- } else {
+ vap_caps->default_mgmt_rate = cpu_to_le32(RSI_RATE_6);
+
+ if (common->band == IEEE80211_BAND_5GHZ) {
vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_6);
+ if (conf_is_ht40(&common->priv->hw->conf)) {
+ vap_caps->default_ctrl_rate |=
+ cpu_to_le32(FULL40M_ENABLE << 16);
+ }
+ } else {
+ vap_caps->default_ctrl_rate = cpu_to_le32(RSI_RATE_1);
+ if (conf_is_ht40_minus(conf))
+ vap_caps->default_ctrl_rate |=
+ cpu_to_le32(UPPER_20_ENABLE << 16);
+ else if (conf_is_ht40_plus(conf))
+ vap_caps->default_ctrl_rate |=
+ cpu_to_le32(LOWER_20_ENABLE << 16);
}
+
vap_caps->default_data_rate = 0;
vap_caps->beacon_interval = cpu_to_le16(200);
vap_caps->dtim_period = cpu_to_le16(4);
@@ -827,6 +847,63 @@ static int rsi_send_reset_mac(struct rsi_common *common)
}
/**
+ * rsi_band_check() - This function programs the band
+ * @common: Pointer to the driver private structure.
+ *
+ * Return: 0 on success, corresponding error code on failure.
+ */
+int rsi_band_check(struct rsi_common *common)
+{
+ struct rsi_hw *adapter = common->priv;
+ struct ieee80211_hw *hw = adapter->hw;
+ u8 prev_bw = common->channel_width;
+ u8 prev_ep = common->endpoint;
+ struct ieee80211_channel *curchan = hw->conf.chandef.chan;
+ int status = 0;
+
+ if (common->band != curchan->band) {
+ common->rf_reset = 1;
+ common->band = curchan->band;
+ }
+
+ if ((hw->conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) ||
+ (hw->conf.chandef.width == NL80211_CHAN_WIDTH_20))
+ common->channel_width = BW_20MHZ;
+ else
+ common->channel_width = BW_40MHZ;
+
+ if (common->band == IEEE80211_BAND_2GHZ) {
+ if (common->channel_width)
+ common->endpoint = EP_2GHZ_40MHZ;
+ else
+ common->endpoint = EP_2GHZ_20MHZ;
+ } else {
+ if (common->channel_width)
+ common->endpoint = EP_5GHZ_40MHZ;
+ else
+ common->endpoint = EP_5GHZ_20MHZ;
+ }
+
+ if (common->endpoint != prev_ep) {
+ status = rsi_program_bb_rf(common);
+ if (status)
+ return status;
+ }
+
+ if (common->channel_width != prev_bw) {
+ status = rsi_load_bootup_params(common);
+ if (status)
+ return status;
+
+ status = rsi_load_radio_caps(common);
+ if (status)
+ return status;
+ }
+
+ return status;
+}
+
+/**
* rsi_set_channel() - This function programs the channel.
* @common: Pointer to the driver private structure.
* @channel: Channel value to be set.
@@ -841,23 +918,6 @@ int rsi_set_channel(struct rsi_common *common, u16 channel)
rsi_dbg(MGMT_TX_ZONE,
"%s: Sending scan req frame\n", __func__);
- if (common->band == IEEE80211_BAND_5GHZ) {
- if ((channel >= 36) && (channel <= 64))
- channel = ((channel - 32) / 4);
- else if ((channel > 64) && (channel <= 140))
- channel = ((channel - 102) / 4) + 8;
- else if (channel >= 149)
- channel = ((channel - 151) / 4) + 18;
- else
- return -EINVAL;
- } else {
- if (channel > 14) {
- rsi_dbg(ERR_ZONE, "%s: Invalid chno %d, band = %d\n",
- __func__, channel, common->band);
- return -EINVAL;
- }
- }
-
skb = dev_alloc_skb(FRAME_DESC_SZ);
if (!skb) {
rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
@@ -877,6 +937,7 @@ int rsi_set_channel(struct rsi_common *common, u16 channel)
(RSI_RF_TYPE << 4));
mgmt_frame->desc_word[5] = cpu_to_le16(0x01);
+ mgmt_frame->desc_word[6] = cpu_to_le16(0x12);
if (common->channel_width == BW_40MHZ)
mgmt_frame->desc_word[5] |= cpu_to_le16(0x1 << 8);
@@ -950,7 +1011,7 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
struct ieee80211_hw *hw = common->priv->hw;
u8 band = hw->conf.chandef.chan->band;
u8 num_supported_rates = 0;
- u8 rate_offset = 0;
+ u8 rate_table_offset, rate_offset = 0;
u32 rate_bitmap = common->bitrate_mask[band];
u16 *selected_rates, min_rate;
@@ -986,14 +1047,19 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
if (common->channel_width == BW_40MHZ)
auto_rate->desc_word[7] |= cpu_to_le16(1);
- if (band == IEEE80211_BAND_2GHZ)
- min_rate = STD_RATE_01;
- else
- min_rate = STD_RATE_06;
+ if (band == IEEE80211_BAND_2GHZ) {
+ min_rate = RSI_RATE_1;
+ rate_table_offset = 0;
+ } else {
+ min_rate = RSI_RATE_6;
+ rate_table_offset = 4;
+ }
- for (ii = 0, jj = 0; ii < ARRAY_SIZE(rsi_rates); ii++) {
+ for (ii = 0, jj = 0;
+ ii < (ARRAY_SIZE(rsi_rates) - rate_table_offset); ii++) {
if (rate_bitmap & BIT(ii)) {
- selected_rates[jj++] = (rsi_rates[ii].bitrate / 5);
+ selected_rates[jj++] =
+ (rsi_rates[ii + rate_table_offset].bitrate / 5);
rate_offset++;
}
}
@@ -1006,13 +1072,6 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
rate_offset += ARRAY_SIZE(mcs);
}
- if (rate_offset < (RSI_TBL_SZ / 2) - 1) {
- for (ii = jj; ii < (RSI_TBL_SZ / 2); ii++) {
- selected_rates[jj++] = min_rate;
- rate_offset++;
- }
- }
-
sort(selected_rates, jj, sizeof(u16), &rsi_compare, NULL);
/* mapping the rates to RSI rates */
@@ -1028,25 +1087,25 @@ static int rsi_send_auto_rate_request(struct rsi_common *common)
/* loading HT rates in the bottom half of the auto rate table */
if (common->vif_info[0].is_ht) {
- if (common->vif_info[0].sgi)
- auto_rate->supported_rates[rate_offset++] =
- cpu_to_le16(RSI_RATE_MCS7_SG);
-
for (ii = rate_offset, kk = ARRAY_SIZE(rsi_mcsrates) - 1;
ii < rate_offset + 2 * ARRAY_SIZE(rsi_mcsrates); ii++) {
- if (common->vif_info[0].sgi)
+ if (common->vif_info[0].sgi ||
+ conf_is_ht40(&common->priv->hw->conf))
auto_rate->supported_rates[ii++] =
cpu_to_le16(rsi_mcsrates[kk] | BIT(9));
auto_rate->supported_rates[ii] =
cpu_to_le16(rsi_mcsrates[kk--]);
}
- for (; ii < RSI_TBL_SZ; ii++) {
+ for (; ii < (RSI_TBL_SZ - 1); ii++) {
auto_rate->supported_rates[ii] =
cpu_to_le16(rsi_mcsrates[0]);
}
}
+ for (; ii < RSI_TBL_SZ; ii++)
+ auto_rate->supported_rates[ii] = cpu_to_le16(min_rate);
+
auto_rate->num_supported_rates = cpu_to_le16(num_supported_rates * 2);
auto_rate->moderate_rate_inx = cpu_to_le16(num_supported_rates / 2);
auto_rate->desc_word[7] |= cpu_to_le16(0 << 8);
@@ -1141,6 +1200,49 @@ static int rsi_eeprom_read(struct rsi_common *common)
}
/**
+ * This function sends a frame to block/unblock
+ * data queues in the firmware
+ *
+ * @param common Pointer to the driver private structure.
+ * @param block event - block if true, unblock if false
+ * @return 0 on success, -1 on failure.
+ */
+int rsi_send_block_unblock_frame(struct rsi_common *common, bool block_event)
+{
+ struct rsi_mac_frame *mgmt_frame;
+ struct sk_buff *skb;
+
+ rsi_dbg(MGMT_TX_ZONE, "%s: Sending block/unblock frame\n", __func__);
+
+ skb = dev_alloc_skb(FRAME_DESC_SZ);
+ if (!skb) {
+ rsi_dbg(ERR_ZONE, "%s: Failed in allocation of skb\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ memset(skb->data, 0, FRAME_DESC_SZ);
+ mgmt_frame = (struct rsi_mac_frame *)skb->data;
+
+ mgmt_frame->desc_word[0] = cpu_to_le16(RSI_WIFI_MGMT_Q << 12);
+ mgmt_frame->desc_word[1] = cpu_to_le16(BLOCK_HW_QUEUE);
+
+ if (block_event == true) {
+ rsi_dbg(INFO_ZONE, "blocking the data qs\n");
+ mgmt_frame->desc_word[4] = cpu_to_le16(0xf);
+ } else {
+ rsi_dbg(INFO_ZONE, "unblocking the data qs\n");
+ mgmt_frame->desc_word[5] = cpu_to_le16(0xf);
+ }
+
+ skb_put(skb, FRAME_DESC_SZ);
+
+ return rsi_send_internal_mgmt_frame(common, skb);
+
+}
+
+
+/**
* rsi_handle_ta_confirm_type() - This function handles the confirm frames.
* @common: Pointer to the driver private structure.
* @msg: Pointer to received packet.
@@ -1164,7 +1266,7 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
common->fsm_state = FSM_EEPROM_READ_MAC_ADDR;
}
} else {
- rsi_dbg(ERR_ZONE,
+ rsi_dbg(INFO_ZONE,
"%s: Received bootup params cfm in %d state\n",
__func__, common->fsm_state);
return 0;
@@ -1227,7 +1329,7 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
__func__);
}
} else {
- rsi_dbg(ERR_ZONE,
+ rsi_dbg(INFO_ZONE,
"%s: Received radio caps cfm in %d state\n",
__func__, common->fsm_state);
return 0;
@@ -1245,7 +1347,10 @@ static int rsi_handle_ta_confirm_type(struct rsi_common *common,
return rsi_mac80211_attach(common);
}
} else {
- goto out;
+ rsi_dbg(INFO_ZONE,
+ "%s: Received bbb_rf cfm in %d state\n",
+ __func__, common->fsm_state);
+ return 0;
}
break;
diff --git a/drivers/net/wireless/rsi/rsi_91x_pkt.c b/drivers/net/wireless/rsi/rsi_91x_pkt.c
index 8e48e72bae20..702593f19997 100644
--- a/drivers/net/wireless/rsi/rsi_91x_pkt.c
+++ b/drivers/net/wireless/rsi/rsi_91x_pkt.c
@@ -81,6 +81,16 @@ int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb)
/* Send fixed rate */
frame_desc[3] = cpu_to_le16(RATE_INFO_ENABLE);
frame_desc[4] = cpu_to_le16(common->min_rate);
+
+ if (conf_is_ht40(&common->priv->hw->conf))
+ frame_desc[5] = cpu_to_le16(FULL40M_ENABLE);
+
+ if (common->vif_info[0].sgi) {
+ if (common->min_rate & 0x100) /* Only MCS rates */
+ frame_desc[4] |=
+ cpu_to_le16(ENABLE_SHORTGI_RATE);
+ }
+
}
frame_desc[6] |= cpu_to_le16(seq_num & 0xfff);
@@ -116,6 +126,8 @@ int rsi_send_mgmt_pkt(struct rsi_common *common,
struct ieee80211_hdr *wh = NULL;
struct ieee80211_tx_info *info;
struct ieee80211_bss_conf *bss = NULL;
+ struct ieee80211_hw *hw = adapter->hw;
+ struct ieee80211_conf *conf = &hw->conf;
struct skb_info *tx_params;
int status = -E2BIG;
__le16 *msg = NULL;
@@ -175,6 +187,11 @@ int rsi_send_mgmt_pkt(struct rsi_common *common,
else
msg[4] = cpu_to_le16((RSI_RATE_6 & 0x0f) | RSI_11G_MODE);
+ if (conf_is_ht40(conf)) {
+ msg[4] = cpu_to_le16(0xB | RSI_11G_MODE);
+ msg[5] = cpu_to_le16(0x6);
+ }
+
/* Indicate to firmware to give cfm */
if ((skb->data[16] == IEEE80211_STYPE_PROBE_REQ) && (!bss->assoc)) {
msg[1] |= cpu_to_le16(BIT(10));
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio.c b/drivers/net/wireless/rsi/rsi_91x_sdio.c
index 46e7af446f01..8428858204a6 100644
--- a/drivers/net/wireless/rsi/rsi_91x_sdio.c
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio.c
@@ -820,9 +820,11 @@ static struct sdio_driver rsi_driver = {
*/
static int rsi_module_init(void)
{
- sdio_register_driver(&rsi_driver);
+ int ret;
+
+ ret = sdio_register_driver(&rsi_driver);
rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__);
- return 0;
+ return ret;
}
/**
diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
index 20d11ccfffe3..4834a9abc171 100644
--- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
+++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c
@@ -401,14 +401,16 @@ void rsi_interrupt_handler(struct rsi_hw *adapter)
case BUFFER_AVAILABLE:
dev->rx_info.watch_bufferfull_count = 0;
dev->rx_info.buffer_full = false;
+ dev->rx_info.semi_buffer_full = false;
dev->rx_info.mgmt_buffer_full = false;
rsi_sdio_ack_intr(common->priv,
(1 << PKT_BUFF_AVAILABLE));
- rsi_set_event((&common->tx_thread.event));
+ rsi_set_event(&common->tx_thread.event);
+
rsi_dbg(ISR_ZONE,
- "%s: ==> BUFFER_AVILABLE <==\n",
+ "%s: ==> BUFFER_AVAILABLE <==\n",
__func__);
- dev->rx_info.buf_avilable_counter++;
+ dev->rx_info.buf_available_counter++;
break;
case FIRMWARE_ASSERT_IND:
diff --git a/drivers/net/wireless/rsi/rsi_91x_usb.c b/drivers/net/wireless/rsi/rsi_91x_usb.c
index 4c46e5631e2f..ef5d394f185b 100644
--- a/drivers/net/wireless/rsi/rsi_91x_usb.c
+++ b/drivers/net/wireless/rsi/rsi_91x_usb.c
@@ -25,7 +25,7 @@
* @len: Length to be written.
* @endpoint: Type of endpoint.
*
- * Return: status: 0 on success, -1 on failure.
+ * Return: status: 0 on success, a negative error code on failure.
*/
static int rsi_usb_card_write(struct rsi_hw *adapter,
void *buf,
@@ -60,7 +60,7 @@ static int rsi_usb_card_write(struct rsi_hw *adapter,
* @data: Pointer to the data that has to be written.
* @count: Number of multiple bytes to be written.
*
- * Return: 0 on success, -1 on failure.
+ * Return: 0 on success, a negative error code on failure.
*/
static int rsi_write_multiple(struct rsi_hw *adapter,
u8 endpoint,
@@ -147,7 +147,7 @@ static int rsi_find_bulk_in_and_out_endpoints(struct usb_interface *interface,
* @value: Value to be read.
* @len: length of data to be read.
*
- * Return: status: 0 on success, -1 on failure.
+ * Return: status: 0 on success, a negative error code on failure.
*/
static int rsi_usb_reg_read(struct usb_device *usbdev,
u32 reg,
@@ -189,7 +189,7 @@ static int rsi_usb_reg_read(struct usb_device *usbdev,
* @value: Value to write.
* @len: Length of data to be written.
*
- * Return: status: 0 on success, -1 on failure.
+ * Return: status: 0 on success, a negative error code on failure.
*/
static int rsi_usb_reg_write(struct usb_device *usbdev,
u32 reg,
@@ -249,7 +249,7 @@ static void rsi_rx_done_handler(struct urb *urb)
* rsi_rx_urb_submit() - This function submits the given URB to the USB stack.
* @adapter: Pointer to the adapter structure.
*
- * Return: 0 on success, -1 on failure.
+ * Return: 0 on success, a negative error code on failure.
*/
static int rsi_rx_urb_submit(struct rsi_hw *adapter)
{
@@ -281,7 +281,7 @@ static int rsi_rx_urb_submit(struct rsi_hw *adapter)
* @data: Pointer to the data that has to be written.
* @count: Number of multiple bytes to be written on to the registers.
*
- * Return: status: 0 on success, -1 on failure.
+ * Return: status: 0 on success, a negative error code on failure.
*/
int rsi_usb_write_register_multiple(struct rsi_hw *adapter,
u32 addr,
@@ -331,7 +331,7 @@ int rsi_usb_write_register_multiple(struct rsi_hw *adapter,
* @pkt: Pointer to the data to be written on to the card.
* @len: Length of the data to be written on to the card.
*
- * Return: 0 on success, -1 on failure.
+ * Return: 0 on success, a negative error code on failure.
*/
static int rsi_usb_host_intf_write_pkt(struct rsi_hw *adapter,
u8 *pkt,
@@ -359,6 +359,7 @@ static void rsi_deinit_usb_interface(struct rsi_hw *adapter)
struct rsi_91x_usbdev *dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
rsi_kill_thread(&dev->rx_thread);
+ usb_free_urb(dev->rx_usb_urb[0]);
kfree(adapter->priv->rx_data_pkt);
kfree(dev->tx_buffer);
}
@@ -368,7 +369,7 @@ static void rsi_deinit_usb_interface(struct rsi_hw *adapter)
* @adapter: Pointer to the adapter structure.
* @pfunction: Pointer to USB interface structure.
*
- * Return: 0 on success, -1 on failure.
+ * Return: 0 on success, a negative error code on failure.
*/
static int rsi_init_usb_interface(struct rsi_hw *adapter,
struct usb_interface *pfunction)
@@ -397,8 +398,16 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter,
return -ENOMEM;
}
- rsi_dev->tx_buffer = kmalloc(2048, GFP_ATOMIC);
+ rsi_dev->tx_buffer = kmalloc(2048, GFP_KERNEL);
+ if (!rsi_dev->tx_buffer) {
+ status = -ENOMEM;
+ goto fail_tx;
+ }
rsi_dev->rx_usb_urb[0] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rsi_dev->rx_usb_urb[0]) {
+ status = -ENOMEM;
+ goto fail_rx;
+ }
rsi_dev->rx_usb_urb[0]->transfer_buffer = adapter->priv->rx_data_pkt;
rsi_dev->tx_blk_size = 252;
@@ -413,7 +422,7 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter,
rsi_usb_rx_thread, "RX-Thread");
if (status) {
rsi_dbg(ERR_ZONE, "%s: Unable to init rx thrd\n", __func__);
- goto fail;
+ goto fail_thread;
}
#ifdef CONFIG_RSI_DEBUGFS
@@ -424,8 +433,11 @@ static int rsi_init_usb_interface(struct rsi_hw *adapter,
rsi_dbg(INIT_ZONE, "%s: Enabled the interface\n", __func__);
return 0;
-fail:
+fail_thread:
+ usb_free_urb(rsi_dev->rx_usb_urb[0]);
+fail_rx:
kfree(rsi_dev->tx_buffer);
+fail_tx:
kfree(common->rx_data_pkt);
return status;
}
@@ -437,7 +449,7 @@ fail:
* @pfunction: Pointer to the USB interface structure.
* @id: Pointer to the usb_device_id structure.
*
- * Return: 0 on success, -1 on failure.
+ * Return: 0 on success, a negative error code on failure.
*/
static int rsi_probe(struct usb_interface *pfunction,
const struct usb_device_id *id)
@@ -445,6 +457,7 @@ static int rsi_probe(struct usb_interface *pfunction,
struct rsi_hw *adapter;
struct rsi_91x_usbdev *dev;
u16 fw_status;
+ int status;
rsi_dbg(INIT_ZONE, "%s: Init function called\n", __func__);
@@ -452,10 +465,11 @@ static int rsi_probe(struct usb_interface *pfunction,
if (!adapter) {
rsi_dbg(ERR_ZONE, "%s: Failed to init os intf ops\n",
__func__);
- return 1;
+ return -ENOMEM;
}
- if (rsi_init_usb_interface(adapter, pfunction)) {
+ status = rsi_init_usb_interface(adapter, pfunction);
+ if (status) {
rsi_dbg(ERR_ZONE, "%s: Failed to init usb interface\n",
__func__);
goto err;
@@ -465,26 +479,30 @@ static int rsi_probe(struct usb_interface *pfunction,
dev = (struct rsi_91x_usbdev *)adapter->rsi_dev;
- if (rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2) < 0)
+ status = rsi_usb_reg_read(dev->usbdev, FW_STATUS_REG, &fw_status, 2);
+ if (status)
goto err1;
else
fw_status &= 1;
if (!fw_status) {
- if (rsi_usb_device_init(adapter->priv)) {
+ status = rsi_usb_device_init(adapter->priv);
+ if (status) {
rsi_dbg(ERR_ZONE, "%s: Failed in device init\n",
__func__);
goto err1;
}
- if (rsi_usb_reg_write(dev->usbdev,
- USB_INTERNAL_REG_1,
- RSI_USB_READY_MAGIC_NUM, 1) < 0)
+ status = rsi_usb_reg_write(dev->usbdev,
+ USB_INTERNAL_REG_1,
+ RSI_USB_READY_MAGIC_NUM, 1);
+ if (status)
goto err1;
rsi_dbg(INIT_ZONE, "%s: Performed device init\n", __func__);
}
- if (rsi_rx_urb_submit(adapter))
+ status = rsi_rx_urb_submit(adapter);
+ if (status)
goto err1;
return 0;
@@ -493,7 +511,7 @@ err1:
err:
rsi_91x_deinit(adapter);
rsi_dbg(ERR_ZONE, "%s: Failed in probe...Exiting\n", __func__);
- return 1;
+ return status;
}
/**
@@ -550,33 +568,7 @@ static struct usb_driver rsi_driver = {
#endif
};
-/**
- * rsi_module_init() - This function registers the client driver.
- * @void: Void.
- *
- * Return: 0 on success.
- */
-static int rsi_module_init(void)
-{
- usb_register(&rsi_driver);
- rsi_dbg(INIT_ZONE, "%s: Registering driver\n", __func__);
- return 0;
-}
-
-/**
- * rsi_module_exit() - This function unregisters the client driver.
- * @void: Void.
- *
- * Return: None.
- */
-static void rsi_module_exit(void)
-{
- usb_deregister(&rsi_driver);
- rsi_dbg(INFO_ZONE, "%s: Unregistering driver\n", __func__);
-}
-
-module_init(rsi_module_init);
-module_exit(rsi_module_exit);
+module_usb_driver(rsi_driver);
MODULE_AUTHOR("Redpine Signals Inc");
MODULE_DESCRIPTION("Common USB layer for RSI drivers");
diff --git a/drivers/net/wireless/rsi/rsi_main.h b/drivers/net/wireless/rsi/rsi_main.h
index 2cb73e7edb98..5baed945f60e 100644
--- a/drivers/net/wireless/rsi/rsi_main.h
+++ b/drivers/net/wireless/rsi/rsi_main.h
@@ -115,6 +115,7 @@ struct wmm_qinfo {
s32 weight;
s32 wme_params;
s32 pkt_contended;
+ s32 txop;
};
struct transmit_q_stats {
@@ -141,6 +142,12 @@ struct rsi_thread {
atomic_t thread_done;
};
+struct cqm_info {
+ s8 last_cqm_event_rssi;
+ int rssi_thold;
+ u32 rssi_hyst;
+};
+
struct rsi_hw;
struct rsi_common {
@@ -192,6 +199,11 @@ struct rsi_common {
u8 selected_qnum;
u32 pkt_cnt;
u8 min_weight;
+
+ /* bgscan related */
+ struct cqm_info cqm_info;
+
+ bool hw_data_qs_blocked;
};
struct rsi_hw {
diff --git a/drivers/net/wireless/rsi/rsi_mgmt.h b/drivers/net/wireless/rsi/rsi_mgmt.h
index 225215a3b8bb..3741173fd3ac 100644
--- a/drivers/net/wireless/rsi/rsi_mgmt.h
+++ b/drivers/net/wireless/rsi/rsi_mgmt.h
@@ -69,6 +69,7 @@
#define RSI_LMAC_CLOCK_80MHZ 0x1
#define RSI_ENABLE_40MHZ (0x1 << 3)
+#define ENABLE_SHORTGI_RATE BIT(9)
#define RX_BA_INDICATION 1
#define RSI_TBL_SZ 40
@@ -123,6 +124,20 @@
#define BW_20MHZ 0
#define BW_40MHZ 1
+#define EP_2GHZ_20MHZ 0
+#define EP_2GHZ_40MHZ 1
+#define EP_5GHZ_20MHZ 2
+#define EP_5GHZ_40MHZ 3
+
+#define SIFS_TX_11N_VALUE 580
+#define SIFS_TX_11B_VALUE 346
+#define SHORT_SLOT_VALUE 360
+#define LONG_SLOT_VALUE 640
+#define OFDM_ACK_TOUT_VALUE 2720
+#define CCK_ACK_TOUT_VALUE 9440
+#define LONG_PREAMBLE 0x0000
+#define SHORT_PREAMBLE 0x0001
+
#define RSI_SUPP_FILTERS (FIF_ALLMULTI | FIF_PROBE_REQ |\
FIF_BCN_PRBRESP_PROMISC)
enum opmode {
@@ -153,7 +168,7 @@ enum cmd_frame_type {
SCAN_REQUEST,
TSF_UPDATE,
PEER_NOTIFY,
- BLOCK_UNBLOCK,
+ BLOCK_HW_QUEUE,
SET_KEY_REQ,
AUTO_RATE_IND,
BOOTUP_PARAMS_REQUEST,
@@ -238,6 +253,12 @@ struct rsi_radio_caps {
u8 num_11n_rates;
u8 num_11ac_rates;
__le16 gcpd_per_rate[20];
+ __le16 sifs_tx_11n;
+ __le16 sifs_tx_11b;
+ __le16 slot_rx_11n;
+ __le16 ofdm_ack_tout;
+ __le16 cck_ack_tout;
+ __le16 preamble_type;
} __packed;
static inline u32 rsi_get_queueno(u8 *addr, u16 offset)
@@ -272,6 +293,7 @@ int rsi_send_aggregation_params_frame(struct rsi_common *common, u16 tid,
int rsi_hal_load_key(struct rsi_common *common, u8 *data, u16 key_len,
u8 key_type, u8 key_id, u32 cipher);
int rsi_set_channel(struct rsi_common *common, u16 chno);
+int rsi_send_block_unblock_frame(struct rsi_common *common, bool event);
void rsi_inform_bss_status(struct rsi_common *common, u8 status,
const u8 *bssid, u8 qos_enable, u16 aid);
void rsi_indicate_pkt_to_os(struct rsi_common *common, struct sk_buff *skb);
@@ -283,4 +305,5 @@ void rsi_core_qos_processor(struct rsi_common *common);
void rsi_core_xmit(struct rsi_common *common, struct sk_buff *skb);
int rsi_send_mgmt_pkt(struct rsi_common *common, struct sk_buff *skb);
int rsi_send_data_pkt(struct rsi_common *common, struct sk_buff *skb);
+int rsi_band_check(struct rsi_common *common);
#endif
diff --git a/drivers/net/wireless/rsi/rsi_sdio.h b/drivers/net/wireless/rsi/rsi_sdio.h
index df4b5e20e05f..c7e8f2be7901 100644
--- a/drivers/net/wireless/rsi/rsi_sdio.h
+++ b/drivers/net/wireless/rsi/rsi_sdio.h
@@ -30,7 +30,7 @@
enum sdio_interrupt_type {
BUFFER_FULL = 0x0,
- BUFFER_AVAILABLE = 0x1,
+ BUFFER_AVAILABLE = 0x2,
FIRMWARE_ASSERT_IND = 0x3,
MSDU_PACKET_PENDING = 0x4,
UNKNOWN_INT = 0XE
@@ -42,7 +42,7 @@ enum sdio_interrupt_type {
#define PKT_MGMT_BUFF_FULL 2
#define MSDU_PKT_PENDING 3
/* Interrupt Bit Related Macros */
-#define PKT_BUFF_AVAILABLE 0
+#define PKT_BUFF_AVAILABLE 1
#define FW_ASSERT_IND 2
#define RSI_DEVICE_BUFFER_STATUS_REGISTER 0xf3
@@ -84,7 +84,7 @@ enum sdio_interrupt_type {
#define TA_HOLD_THREAD_VALUE cpu_to_le32(0xF)
#define TA_RELEASE_THREAD_VALUE cpu_to_le32(0xF)
#define TA_BASE_ADDR 0x2200
-#define MISC_CFG_BASE_ADDR 0x4150
+#define MISC_CFG_BASE_ADDR 0x4105
struct receive_info {
bool buffer_full;
@@ -98,7 +98,7 @@ struct receive_info {
u32 total_sdio_msdu_pending_intr;
u32 total_sdio_unknown_intr;
u32 buf_full_counter;
- u32 buf_avilable_counter;
+ u32 buf_available_counter;
};
struct rsi_91x_sdiodev {
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index c17fcf272728..893c9d5f3d6f 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -947,6 +947,40 @@ static inline u8 rt2800_get_beacon_offset(struct rt2x00_dev *rt2x00dev,
return BEACON_BASE_TO_OFFSET(rt2800_hw_beacon_base(rt2x00dev, index));
}
+static void rt2800_update_beacons_setup(struct rt2x00_dev *rt2x00dev)
+{
+ struct data_queue *queue = rt2x00dev->bcn;
+ struct queue_entry *entry;
+ int i, bcn_num = 0;
+ u64 off, reg = 0;
+ u32 bssid_dw1;
+
+ /*
+ * Setup offsets of all active beacons in BCN_OFFSET{0,1} registers.
+ */
+ for (i = 0; i < queue->limit; i++) {
+ entry = &queue->entries[i];
+ if (!test_bit(ENTRY_BCN_ENABLED, &entry->flags))
+ continue;
+ off = rt2800_get_beacon_offset(rt2x00dev, entry->entry_idx);
+ reg |= off << (8 * bcn_num);
+ bcn_num++;
+ }
+
+ WARN_ON_ONCE(bcn_num != rt2x00dev->intf_beaconing);
+
+ rt2800_register_write(rt2x00dev, BCN_OFFSET0, (u32) reg);
+ rt2800_register_write(rt2x00dev, BCN_OFFSET1, (u32) (reg >> 32));
+
+ /*
+ * H/W sends up to MAC_BSSID_DW1_BSS_BCN_NUM + 1 consecutive beacons.
+ */
+ rt2800_register_read(rt2x00dev, MAC_BSSID_DW1, &bssid_dw1);
+ rt2x00_set_field32(&bssid_dw1, MAC_BSSID_DW1_BSS_BCN_NUM,
+ bcn_num > 0 ? bcn_num - 1 : 0);
+ rt2800_register_write(rt2x00dev, MAC_BSSID_DW1, bssid_dw1);
+}
+
void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
{
struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
@@ -1003,6 +1037,12 @@ void rt2800_write_beacon(struct queue_entry *entry, struct txentry_desc *txdesc)
rt2800_register_multiwrite(rt2x00dev, beacon_base, entry->skb->data,
entry->skb->len + padding_len);
+ __set_bit(ENTRY_BCN_ENABLED, &entry->flags);
+
+ /*
+ * Change global beacons settings.
+ */
+ rt2800_update_beacons_setup(rt2x00dev);
/*
* Restore beaconing state.
@@ -1053,8 +1093,13 @@ void rt2800_clear_beacon(struct queue_entry *entry)
* Clear beacon.
*/
rt2800_clear_beacon_register(rt2x00dev, entry->entry_idx);
+ __clear_bit(ENTRY_BCN_ENABLED, &entry->flags);
/*
+ * Change global beacons settings.
+ */
+ rt2800_update_beacons_setup(rt2x00dev);
+ /*
* Restore beaconing state.
*/
rt2800_register_write(rt2x00dev, BCN_TIME_CFG, orig_reg);
@@ -1556,7 +1601,7 @@ void rt2800_config_intf(struct rt2x00_dev *rt2x00dev, struct rt2x00_intf *intf,
if (!is_zero_ether_addr((const u8 *)conf->bssid)) {
reg = le32_to_cpu(conf->bssid[1]);
rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_ID_MASK, 3);
- rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 7);
+ rt2x00_set_field32(&reg, MAC_BSSID_DW1_BSS_BCN_NUM, 0);
conf->bssid[1] = cpu_to_le32(reg);
}
@@ -4517,28 +4562,6 @@ static int rt2800_init_registers(struct rt2x00_dev *rt2x00dev)
if (ret)
return ret;
- rt2800_register_read(rt2x00dev, BCN_OFFSET0, &reg);
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN0,
- rt2800_get_beacon_offset(rt2x00dev, 0));
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN1,
- rt2800_get_beacon_offset(rt2x00dev, 1));
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN2,
- rt2800_get_beacon_offset(rt2x00dev, 2));
- rt2x00_set_field32(&reg, BCN_OFFSET0_BCN3,
- rt2800_get_beacon_offset(rt2x00dev, 3));
- rt2800_register_write(rt2x00dev, BCN_OFFSET0, reg);
-
- rt2800_register_read(rt2x00dev, BCN_OFFSET1, &reg);
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN4,
- rt2800_get_beacon_offset(rt2x00dev, 4));
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN5,
- rt2800_get_beacon_offset(rt2x00dev, 5));
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN6,
- rt2800_get_beacon_offset(rt2x00dev, 6));
- rt2x00_set_field32(&reg, BCN_OFFSET1_BCN7,
- rt2800_get_beacon_offset(rt2x00dev, 7));
- rt2800_register_write(rt2x00dev, BCN_OFFSET1, reg);
-
rt2800_register_write(rt2x00dev, LEGACY_BASIC_RATE, 0x0000013f);
rt2800_register_write(rt2x00dev, HT_BASIC_RATE, 0x00008003);
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 4fa43a2eeb73..9967a1d9f0ec 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -141,8 +141,11 @@ static void rt2x00lib_intf_scheduled_iter(void *data, u8 *mac,
if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
return;
- if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags))
+ if (test_and_clear_bit(DELAYED_UPDATE_BEACON, &intf->delayed_flags)) {
+ mutex_lock(&intf->beacon_skb_mutex);
rt2x00queue_update_beacon(rt2x00dev, vif);
+ mutex_unlock(&intf->beacon_skb_mutex);
+ }
}
static void rt2x00lib_intf_scheduled(struct work_struct *work)
@@ -216,7 +219,7 @@ static void rt2x00lib_beaconupdate_iter(void *data, u8 *mac,
* never be called for USB devices.
*/
WARN_ON(rt2x00_is_usb(rt2x00dev));
- rt2x00queue_update_beacon_locked(rt2x00dev, vif);
+ rt2x00queue_update_beacon(rt2x00dev, vif);
}
void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev)
@@ -1470,8 +1473,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev)
/*
* Free the driver data.
*/
- if (rt2x00dev->drv_data)
- kfree(rt2x00dev->drv_data);
+ kfree(rt2x00dev->drv_data);
}
EXPORT_SYMBOL_GPL(rt2x00lib_remove_dev);
diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c
index 004dff9b962d..ad6e5a8d1e10 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mac.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mac.c
@@ -626,25 +626,24 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
* Start/stop beaconing.
*/
if (changes & BSS_CHANGED_BEACON_ENABLED) {
+ mutex_lock(&intf->beacon_skb_mutex);
if (!bss_conf->enable_beacon && intf->enable_beacon) {
rt2x00dev->intf_beaconing--;
intf->enable_beacon = false;
- /*
- * Clear beacon in the H/W for this vif. This is needed
- * to disable beaconing on this particular interface
- * and keep it running on other interfaces.
- */
- rt2x00queue_clear_beacon(rt2x00dev, vif);
if (rt2x00dev->intf_beaconing == 0) {
/*
* Last beaconing interface disabled
* -> stop beacon queue.
*/
- mutex_lock(&intf->beacon_skb_mutex);
rt2x00queue_stop_queue(rt2x00dev->bcn);
- mutex_unlock(&intf->beacon_skb_mutex);
}
+ /*
+ * Clear beacon in the H/W for this vif. This is needed
+ * to disable beaconing on this particular interface
+ * and keep it running on other interfaces.
+ */
+ rt2x00queue_clear_beacon(rt2x00dev, vif);
} else if (bss_conf->enable_beacon && !intf->enable_beacon) {
rt2x00dev->intf_beaconing++;
intf->enable_beacon = true;
@@ -660,11 +659,10 @@ void rt2x00mac_bss_info_changed(struct ieee80211_hw *hw,
* First beaconing interface enabled
* -> start beacon queue.
*/
- mutex_lock(&intf->beacon_skb_mutex);
rt2x00queue_start_queue(rt2x00dev->bcn);
- mutex_unlock(&intf->beacon_skb_mutex);
}
}
+ mutex_unlock(&intf->beacon_skb_mutex);
}
/*
@@ -801,6 +799,8 @@ int rt2x00mac_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant)
setup.tx = tx_ant;
setup.rx = rx_ant;
+ setup.rx_chain_num = 0;
+ setup.tx_chain_num = 0;
rt2x00lib_config_antenna(rt2x00dev, setup);
diff --git a/drivers/net/wireless/rt2x00/rt2x00mmio.c b/drivers/net/wireless/rt2x00/rt2x00mmio.c
index 6f236ea180aa..f0178fd4fe5f 100644
--- a/drivers/net/wireless/rt2x00/rt2x00mmio.c
+++ b/drivers/net/wireless/rt2x00/rt2x00mmio.c
@@ -119,14 +119,12 @@ static int rt2x00mmio_alloc_queue_dma(struct rt2x00_dev *rt2x00dev,
/*
* Allocate DMA memory for descriptor and buffer.
*/
- addr = dma_alloc_coherent(rt2x00dev->dev,
- queue->limit * queue->desc_size,
- &dma, GFP_KERNEL);
+ addr = dma_zalloc_coherent(rt2x00dev->dev,
+ queue->limit * queue->desc_size, &dma,
+ GFP_KERNEL);
if (!addr)
return -ENOMEM;
- memset(addr, 0, queue->limit * queue->desc_size);
-
/*
* Initialize all queue entries to contain valid addresses.
*/
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index 5642ccceca7c..8e68f87ab13c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -754,8 +754,6 @@ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
if (unlikely(!intf->beacon))
return -ENOBUFS;
- mutex_lock(&intf->beacon_skb_mutex);
-
/*
* Clean up the beacon skb.
*/
@@ -768,13 +766,11 @@ int rt2x00queue_clear_beacon(struct rt2x00_dev *rt2x00dev,
if (rt2x00dev->ops->lib->clear_beacon)
rt2x00dev->ops->lib->clear_beacon(intf->beacon);
- mutex_unlock(&intf->beacon_skb_mutex);
-
return 0;
}
-int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev,
- struct ieee80211_vif *vif)
+int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
+ struct ieee80211_vif *vif)
{
struct rt2x00_intf *intf = vif_to_intf(vif);
struct skb_frame_desc *skbdesc;
@@ -815,19 +811,6 @@ int rt2x00queue_update_beacon_locked(struct rt2x00_dev *rt2x00dev,
}
-int rt2x00queue_update_beacon(struct rt2x00_dev *rt2x00dev,
- struct ieee80211_vif *vif)
-{
- struct rt2x00_intf *intf = vif_to_intf(vif);
- int ret;
-
- mutex_lock(&intf->beacon_skb_mutex);
- ret = rt2x00queue_update_beacon_locked(rt2x00dev, vif);
- mutex_unlock(&intf->beacon_skb_mutex);
-
- return ret;
-}
-
bool rt2x00queue_for_each_entry(struct data_queue *queue,
enum queue_index start,
enum queue_index end,
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index c48125be0e34..2233b911a1d7 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -353,6 +353,7 @@ struct txentry_desc {
*/
enum queue_entry_flags {
ENTRY_BCN_ASSIGNED,
+ ENTRY_BCN_ENABLED,
ENTRY_OWNER_DEVICE_DATA,
ENTRY_DATA_PENDING,
ENTRY_DATA_IO_FAILED,
diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c
index 2c1c02bafa10..4b904f708184 100644
--- a/drivers/net/wireless/rtl818x/rtl8180/dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c
@@ -16,6 +16,7 @@
*
* based also on:
* - portions of rtl8187se Linux staging driver, Copyright Realtek corp.
+ * (available in drivers/staging/rtl8187se directory of Linux 3.14)
* - other GPL, unpublished (until now), Linux driver code,
* Copyright Larry Finger <Larry.Finger@lwfinger.net>
*
@@ -209,7 +210,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
struct rtl8180_priv *priv = dev->priv;
struct rtl818x_rx_cmd_desc *cmd_desc;
unsigned int count = 32;
- u8 signal, agc, sq;
+ u8 agc, sq, signal = 1;
dma_addr_t mapping;
while (count--) {
@@ -222,12 +223,20 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
struct rtl8187se_rx_desc *desc = entry;
flags = le32_to_cpu(desc->flags);
+ /* if ownership flag is set, then we can trust the
+ * HW has written other fields. We must not trust
+ * other descriptor data read before we checked (read)
+ * the ownership flag
+ */
+ rmb();
flags2 = le32_to_cpu(desc->flags2);
tsft = le64_to_cpu(desc->tsft);
} else {
struct rtl8180_rx_desc *desc = entry;
flags = le32_to_cpu(desc->flags);
+ /* same as above */
+ rmb();
flags2 = le32_to_cpu(desc->flags2);
tsft = le64_to_cpu(desc->tsft);
}
@@ -266,18 +275,21 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev)
rx_status.rate_idx = (flags >> 20) & 0xF;
agc = (flags2 >> 17) & 0x7F;
- if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185) {
+ switch (priv->chip_family) {
+ case RTL818X_CHIP_FAMILY_RTL8185:
if (rx_status.rate_idx > 3)
- signal = 90 - clamp_t(u8, agc, 25, 90);
+ signal = -clamp_t(u8, agc, 25, 90) - 9;
else
- signal = 95 - clamp_t(u8, agc, 30, 95);
- } else if (priv->chip_family ==
- RTL818X_CHIP_FAMILY_RTL8180) {
+ signal = -clamp_t(u8, agc, 30, 95);
+ break;
+ case RTL818X_CHIP_FAMILY_RTL8180:
sq = flags2 & 0xff;
signal = priv->rf->calc_rssi(agc, sq);
- } else {
+ break;
+ case RTL818X_CHIP_FAMILY_RTL8187SE:
/* TODO: rtl8187se rssi */
signal = 10;
+ break;
}
rx_status.signal = signal;
rx_status.freq = dev->conf.chandef.chan->center_freq;
@@ -336,7 +348,6 @@ static void rtl8180_handle_tx(struct ieee80211_hw *dev, unsigned int prio)
info->flags |= IEEE80211_TX_STAT_ACK;
info->status.rates[0].count = (flags & 0xFF) + 1;
- info->status.rates[1].idx = -1;
ieee80211_tx_status_irqsafe(dev, skb);
if (ring->entries - skb_queue_len(&ring->queue) == 2)
@@ -528,9 +539,7 @@ static void rtl8180_tx(struct ieee80211_hw *dev,
entry->plcp_len = cpu_to_le16(plcp_len);
entry->tx_buf = cpu_to_le32(mapping);
- entry->flags2 = info->control.rates[1].idx >= 0 ?
- ieee80211_get_alt_retry_rate(dev, info, 0)->bitrate << 4 : 0;
- entry->retry_limit = info->control.rates[0].count;
+ entry->retry_limit = info->control.rates[0].count - 1;
/* We must be sure that tx_flags is written last because the HW
* looks at it to check if the rest of data is valid or not
@@ -852,7 +861,7 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev)
if (priv->chip_family != RTL818X_CHIP_FAMILY_RTL8180) {
rtl818x_iowrite8(priv, &priv->map->WPA_CONF, 0);
- rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0x81);
+ rtl818x_iowrite8(priv, &priv->map->RATE_FALLBACK, 0);
} else {
rtl818x_iowrite8(priv, &priv->map->SECURITY, 0);
@@ -868,6 +877,16 @@ static int rtl8180_init_hw(struct ieee80211_hw *dev)
reg = rtl818x_ioread8(priv, &priv->map->CONFIG3);
rtl818x_iowrite8(priv, &priv->map->CONFIG3, reg | (1 << 2));
rtl818x_iowrite8(priv, &priv->map->EEPROM_CMD, RTL818X_EEPROM_CMD_NORMAL);
+ /* fix eccessive IFS after CTS-to-self */
+ if (priv->map_pio) {
+ u8 reg;
+
+ reg = rtl818x_ioread8(priv, &priv->map->PGSELECT);
+ rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg | 1);
+ rtl818x_iowrite8(priv, REG_ADDR1(0xff), 0x35);
+ rtl818x_iowrite8(priv, &priv->map->PGSELECT, reg);
+ } else
+ rtl818x_iowrite8(priv, REG_ADDR1(0x1ff), 0x35);
}
if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8187SE) {
@@ -1450,9 +1469,10 @@ static void rtl8180_bss_info_changed(struct ieee80211_hw *dev,
vif_priv = (struct rtl8180_vif *)&vif->drv_priv;
if (changed & BSS_CHANGED_BSSID) {
- for (i = 0; i < ETH_ALEN; i++)
- rtl818x_iowrite8(priv, &priv->map->BSSID[i],
- info->bssid[i]);
+ rtl818x_iowrite16(priv, (__le16 __iomem *)&priv->map->BSSID[0],
+ le16_to_cpu(*(__le16 *)info->bssid));
+ rtl818x_iowrite32(priv, (__le32 __iomem *)&priv->map->BSSID[2],
+ le32_to_cpu(*(__le32 *)(info->bssid + 2)));
if (is_valid_ether_addr(info->bssid)) {
if (vif->type == NL80211_IFTYPE_ADHOC)
@@ -1723,17 +1743,20 @@ static int rtl8180_probe(struct pci_dev *pdev,
priv = dev->priv;
priv->pdev = pdev;
- dev->max_rates = 2;
+ dev->max_rates = 1;
SET_IEEE80211_DEV(dev, &pdev->dev);
pci_set_drvdata(pdev, dev);
+ priv->map_pio = false;
priv->map = pci_iomap(pdev, 1, mem_len);
- if (!priv->map)
+ if (!priv->map) {
priv->map = pci_iomap(pdev, 0, io_len);
+ priv->map_pio = true;
+ }
if (!priv->map) {
- printk(KERN_ERR "%s (rtl8180): Cannot map device memory\n",
- pci_name(pdev));
+ dev_err(&pdev->dev, "Cannot map device memory/PIO\n");
+ err = -ENOMEM;
goto err_free_dev;
}
@@ -1751,8 +1774,7 @@ static int rtl8180_probe(struct pci_dev *pdev,
dev->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
dev->flags = IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING |
- IEEE80211_HW_RX_INCLUDES_FCS |
- IEEE80211_HW_SIGNAL_UNSPEC;
+ IEEE80211_HW_RX_INCLUDES_FCS;
dev->vif_data_size = sizeof(struct rtl8180_vif);
dev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
BIT(NL80211_IFTYPE_ADHOC);
@@ -1783,12 +1805,19 @@ static int rtl8180_probe(struct pci_dev *pdev,
case RTL818X_TX_CONF_RTL8187SE:
chip_name = "RTL8187SE";
+ if (priv->map_pio) {
+ dev_err(&pdev->dev,
+ "MMIO failed. PIO not supported on RTL8187SE\n");
+ err = -ENOMEM;
+ goto err_iounmap;
+ }
priv->chip_family = RTL818X_CHIP_FAMILY_RTL8187SE;
break;
default:
printk(KERN_ERR "%s (rtl8180): Unknown chip! (0x%x)\n",
pci_name(pdev), reg >> 25);
+ err = -ENODEV;
goto err_iounmap;
}
@@ -1809,6 +1838,11 @@ static int rtl8180_probe(struct pci_dev *pdev,
pci_try_set_mwi(pdev);
}
+ if (priv->chip_family == RTL818X_CHIP_FAMILY_RTL8185)
+ dev->flags |= IEEE80211_HW_SIGNAL_DBM;
+ else
+ dev->flags |= IEEE80211_HW_SIGNAL_UNSPEC;
+
rtl8180_eeprom_read(priv);
switch (priv->rf_type) {
@@ -1834,12 +1868,14 @@ static int rtl8180_probe(struct pci_dev *pdev,
default:
printk(KERN_ERR "%s (rtl8180): Unknown RF! (0x%x)\n",
pci_name(pdev), priv->rf_type);
+ err = -ENODEV;
goto err_iounmap;
}
if (!priv->rf) {
printk(KERN_ERR "%s (rtl8180): %s RF frontend not supported!\n",
pci_name(pdev), rf_name);
+ err = -ENODEV;
goto err_iounmap;
}
diff --git a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h
index 291a55970d1a..e8243a44d6b6 100644
--- a/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h
+++ b/drivers/net/wireless/rtl818x/rtl8180/rtl8180.h
@@ -107,6 +107,7 @@ struct rtl8180_priv {
struct ieee80211_vif *vif;
/* rtl8180 driver specific */
+ bool map_pio;
spinlock_t lock;
void *rx_ring;
u8 rx_ring_sz;
diff --git a/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h
index 871fc3c6d559..049f4c8d98a8 100644
--- a/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h
+++ b/drivers/net/wireless/rtlwifi/btcoexist/halbtcoutsrc.h
@@ -114,7 +114,7 @@ extern u32 btc_dbg_type[];
#define CL_SPRINTF snprintf
-#define CL_PRINTF printk
+#define CL_PRINTF(buf) printk("%s", buf)
#define BTC_PRINT(dbgtype, dbgflag, printstr, ...) \
do { \
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
index b14cf5a10f44..d840ad7bdf65 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/hw.c
@@ -1231,7 +1231,7 @@ static int _rtl88ee_set_media_status(struct ieee80211_hw *hw,
rtl_write_byte(rtlpriv, (MSR), bt_msr);
rtlpriv->cfg->ops->led_control(hw, ledaction);
- if ((bt_msr & 0xfc) == MSR_AP)
+ if ((bt_msr & MSR_MASK) == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
else
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h b/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h
index 7af85cfa8f87..cd7e7a527133 100644
--- a/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8188ee/reg.h
@@ -411,6 +411,7 @@
#define MSR_ADHOC 0x01
#define MSR_INFRA 0x02
#define MSR_AP 0x03
+#define MSR_MASK 0x03
#define RRSR_RSC_OFFSET 21
#define RRSR_SHORT_OFFSET 23
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
index cdecb0fd4d8e..e2736929b5d0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
@@ -1206,7 +1206,7 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw,
rtl_write_byte(rtlpriv, (MSR), bt_msr);
rtlpriv->cfg->ops->led_control(hw, ledaction);
- if ((bt_msr & 0xfc) == MSR_AP)
+ if ((bt_msr & MSR_MASK) == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
else
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
index ed703a1b3b7c..dc8460c0b32f 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/reg.h
@@ -375,6 +375,7 @@
#define MSR_ADHOC 0x01
#define MSR_INFRA 0x02
#define MSR_AP 0x03
+#define MSR_MASK 0x03
#define RRSR_RSC_OFFSET 21
#define RRSR_SHORT_OFFSET 23
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
index a903c2671b4d..270cbffcac70 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
@@ -1360,7 +1360,7 @@ static int _rtl92cu_set_media_status(struct ieee80211_hw *hw,
}
rtl_write_byte(rtlpriv, (MSR), bt_msr);
rtlpriv->cfg->ops->led_control(hw, ledaction);
- if ((bt_msr & 0xfc) == MSR_AP)
+ if ((bt_msr & MSR_MASK) == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
else
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
index 2b08671004a0..280c3da42993 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c
@@ -1128,7 +1128,7 @@ static int _rtl92de_set_media_status(struct ieee80211_hw *hw,
}
rtl_write_byte(rtlpriv, REG_CR + 2, bt_msr);
rtlpriv->cfg->ops->led_control(hw, ledaction);
- if ((bt_msr & 0xfc) == MSR_AP)
+ if ((bt_msr & MSR_MASK) == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
else
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
index 3d1f0dd4e52d..592125a5f19c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/phy.c
@@ -203,11 +203,12 @@ u32 rtl92d_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask)
struct rtl_priv *rtlpriv = rtl_priv(hw);
struct rtl_hal *rtlhal = rtl_hal(rtlpriv);
u32 returnvalue, originalvalue, bitshift;
- u8 dbi_direct;
RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "regaddr(%#x), bitmask(%#x)\n",
regaddr, bitmask);
if (rtlhal->during_mac1init_radioa || rtlhal->during_mac0init_radiob) {
+ u8 dbi_direct = 0;
+
/* mac1 use phy0 read radio_b. */
/* mac0 use phy1 read radio_b. */
if (rtlhal->during_mac1init_radioa)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
index 7f29b8d765b3..315a298bab06 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/reg.h
@@ -369,6 +369,7 @@
#define MSR_ADHOC 0x01
#define MSR_INFRA 0x02
#define MSR_AP 0x03
+#define MSR_MASK 0x03
/* 6. Adaptive Control Registers (Offset: 0x0160 - 0x01CF) */
/* ----------------------------------------------------- */
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
index 87f69166a7ed..539e53987372 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c
@@ -1109,7 +1109,7 @@ static int _rtl8723ae_set_media_status(struct ieee80211_hw *hw,
rtl_write_byte(rtlpriv, (MSR), bt_msr);
rtlpriv->cfg->ops->led_control(hw, ledaction);
- if ((bt_msr & 0x03) == MSR_AP)
+ if ((bt_msr & MSR_MASK) == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
else
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h b/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h
index 64376b38708b..ce2c66fd9eee 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h
@@ -361,6 +361,7 @@
#define MSR_ADHOC 0x01
#define MSR_INFRA 0x02
#define MSR_AP 0x03
+#define MSR_MASK 0x03
#define RRSR_RSC_OFFSET 21
#define RRSR_SHORT_OFFSET 23
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c
index 3d555495b453..3cd286930fe0 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/hw.c
@@ -1197,7 +1197,7 @@ static int _rtl8723be_set_media_status(struct ieee80211_hw *hw,
}
rtl_write_byte(rtlpriv, (MSR), bt_msr);
rtlpriv->cfg->ops->led_control(hw, ledaction);
- if ((bt_msr & 0x03) == MSR_AP)
+ if ((bt_msr & MSR_MASK) == MSR_AP)
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00);
else
rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66);
diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/reg.h b/drivers/net/wireless/rtlwifi/rtl8723be/reg.h
index 4c653fab8795..3006849ed439 100644
--- a/drivers/net/wireless/rtlwifi/rtl8723be/reg.h
+++ b/drivers/net/wireless/rtlwifi/rtl8723be/reg.h
@@ -412,6 +412,7 @@
#define MSR_ADHOC 0x01
#define MSR_INFRA 0x02
#define MSR_AP 0x03
+#define MSR_MASK 0x03
#define RRSR_RSC_OFFSET 21
#define RRSR_SHORT_OFFSET 23
diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c
index 4e782f18ae34..38234851457e 100644
--- a/drivers/net/wireless/ti/wl1251/main.c
+++ b/drivers/net/wireless/ti/wl1251/main.c
@@ -991,8 +991,9 @@ out:
static int wl1251_op_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req)
+ struct ieee80211_scan_request *hw_req)
{
+ struct cfg80211_scan_request *req = &hw_req->req;
struct wl1251 *wl = hw->priv;
struct sk_buff *skb;
size_t ssid_len = 0;
diff --git a/drivers/net/wireless/ti/wl12xx/scan.c b/drivers/net/wireless/ti/wl12xx/scan.c
index 7541bd1a4a4b..0c0d5cd98514 100644
--- a/drivers/net/wireless/ti/wl12xx/scan.c
+++ b/drivers/net/wireless/ti/wl12xx/scan.c
@@ -156,7 +156,7 @@ static int wl1271_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
cmd->params.role_id, band,
wl->scan.ssid, wl->scan.ssid_len,
wl->scan.req->ie,
- wl->scan.req->ie_len, false);
+ wl->scan.req->ie_len, NULL, 0, false);
if (ret < 0) {
wl1271_error("PROBE request template failed");
goto out;
@@ -317,7 +317,7 @@ static void wl12xx_adjust_channels(struct wl1271_cmd_sched_scan_config *cmd,
int wl1271_scan_sched_scan_config(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies)
+ struct ieee80211_scan_ies *ies)
{
struct wl1271_cmd_sched_scan_config *cfg = NULL;
struct wlcore_scan_channels *cfg_channels = NULL;
@@ -378,8 +378,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
wlvif->role_id, band,
req->ssids[0].ssid,
req->ssids[0].ssid_len,
- ies->ie[band],
- ies->len[band], true);
+ ies->ies[band],
+ ies->len[band],
+ ies->common_ies,
+ ies->common_ie_len,
+ true);
if (ret < 0) {
wl1271_error("2.4GHz PROBE request template failed");
goto out;
@@ -392,8 +395,11 @@ int wl1271_scan_sched_scan_config(struct wl1271 *wl,
wlvif->role_id, band,
req->ssids[0].ssid,
req->ssids[0].ssid_len,
- ies->ie[band],
- ies->len[band], true);
+ ies->ies[band],
+ ies->len[band],
+ ies->common_ies,
+ ies->common_ie_len,
+ true);
if (ret < 0) {
wl1271_error("5GHz PROBE request template failed");
goto out;
@@ -449,7 +455,7 @@ out_free:
int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies)
+ struct ieee80211_scan_ies *ies)
{
int ret;
diff --git a/drivers/net/wireless/ti/wl12xx/scan.h b/drivers/net/wireless/ti/wl12xx/scan.h
index 264af7ac2785..427f9af85a00 100644
--- a/drivers/net/wireless/ti/wl12xx/scan.h
+++ b/drivers/net/wireless/ti/wl12xx/scan.h
@@ -135,6 +135,6 @@ int wl12xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl12xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl12xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies);
+ struct ieee80211_scan_ies *ies);
void wl12xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
#endif
diff --git a/drivers/net/wireless/ti/wl18xx/scan.c b/drivers/net/wireless/ti/wl18xx/scan.c
index 2b642f8c9266..98666f235a12 100644
--- a/drivers/net/wireless/ti/wl18xx/scan.c
+++ b/drivers/net/wireless/ti/wl18xx/scan.c
@@ -113,6 +113,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
req->ssids ? req->ssids[0].ssid_len : 0,
req->ie,
req->ie_len,
+ NULL,
+ 0,
false);
if (ret < 0) {
wl1271_error("2.4GHz PROBE request template failed");
@@ -128,6 +130,8 @@ static int wl18xx_scan_send(struct wl1271 *wl, struct wl12xx_vif *wlvif,
req->ssids ? req->ssids[0].ssid_len : 0,
req->ie,
req->ie_len,
+ NULL,
+ 0,
false);
if (ret < 0) {
wl1271_error("5GHz PROBE request template failed");
@@ -161,7 +165,7 @@ static
int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies)
+ struct ieee80211_scan_ies *ies)
{
struct wl18xx_cmd_scan_params *cmd;
struct wlcore_scan_channels *cmd_channels = NULL;
@@ -237,8 +241,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
cmd->role_id, band,
req->ssids ? req->ssids[0].ssid : NULL,
req->ssids ? req->ssids[0].ssid_len : 0,
- ies->ie[band],
+ ies->ies[band],
ies->len[band],
+ ies->common_ies,
+ ies->common_ie_len,
true);
if (ret < 0) {
wl1271_error("2.4GHz PROBE request template failed");
@@ -252,8 +258,10 @@ int wl18xx_scan_sched_scan_config(struct wl1271 *wl,
cmd->role_id, band,
req->ssids ? req->ssids[0].ssid : NULL,
req->ssids ? req->ssids[0].ssid_len : 0,
- ies->ie[band],
+ ies->ies[band],
ies->len[band],
+ ies->common_ies,
+ ies->common_ie_len,
true);
if (ret < 0) {
wl1271_error("5GHz PROBE request template failed");
@@ -277,7 +285,7 @@ out:
int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies)
+ struct ieee80211_scan_ies *ies)
{
return wl18xx_scan_sched_scan_config(wl, wlvif, req, ies);
}
diff --git a/drivers/net/wireless/ti/wl18xx/scan.h b/drivers/net/wireless/ti/wl18xx/scan.h
index eadee42689d1..2e636aa5dba9 100644
--- a/drivers/net/wireless/ti/wl18xx/scan.h
+++ b/drivers/net/wireless/ti/wl18xx/scan.h
@@ -122,6 +122,6 @@ int wl18xx_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wl18xx_scan_completed(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int wl18xx_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies);
+ struct ieee80211_scan_ies *ies);
void wl18xx_scan_sched_scan_stop(struct wl1271 *wl, struct wl12xx_vif *wlvif);
#endif
diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c
index 40dc30f4faaa..e269c0a57017 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.c
+++ b/drivers/net/wireless/ti/wlcore/cmd.c
@@ -1124,7 +1124,8 @@ out:
int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 role_id, u8 band,
const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len, bool sched_scan)
+ const u8 *ie0, size_t ie0_len, const u8 *ie1,
+ size_t ie1_len, bool sched_scan)
{
struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif);
struct sk_buff *skb;
@@ -1136,13 +1137,15 @@ int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
wl1271_debug(DEBUG_SCAN, "build probe request band %d", band);
skb = ieee80211_probereq_get(wl->hw, vif, ssid, ssid_len,
- ie_len);
+ ie0_len + ie1_len);
if (!skb) {
ret = -ENOMEM;
goto out;
}
- if (ie_len)
- memcpy(skb_put(skb, ie_len), ie, ie_len);
+ if (ie0_len)
+ memcpy(skb_put(skb, ie0_len), ie0, ie0_len);
+ if (ie1_len)
+ memcpy(skb_put(skb, ie1_len), ie1, ie1_len);
if (sched_scan &&
(wl->quirks & WLCORE_QUIRK_DUAL_PROBE_TMPL)) {
diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h
index b084830a61cf..6788d7356ca5 100644
--- a/drivers/net/wireless/ti/wlcore/cmd.h
+++ b/drivers/net/wireless/ti/wlcore/cmd.h
@@ -64,7 +64,8 @@ int wl1271_cmd_build_ps_poll(struct wl1271 *wl, struct wl12xx_vif *wlvif,
int wl12xx_cmd_build_probe_req(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 role_id, u8 band,
const u8 *ssid, size_t ssid_len,
- const u8 *ie, size_t ie_len, bool sched_scan);
+ const u8 *ie, size_t ie_len, const u8 *common_ie,
+ size_t common_ie_len, bool sched_scan);
struct sk_buff *wl1271_cmd_build_ap_probe_req(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct sk_buff *skb);
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 3d6028e62750..48f83868f9cb 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -3540,8 +3540,9 @@ out:
static int wl1271_op_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
- struct cfg80211_scan_request *req)
+ struct ieee80211_scan_request *hw_req)
{
+ struct cfg80211_scan_request *req = &hw_req->req;
struct wl1271 *wl = hw->priv;
int ret;
u8 *ssid = NULL;
@@ -3636,7 +3637,7 @@ out:
static int wl1271_op_sched_scan_start(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies)
+ struct ieee80211_scan_ies *ies)
{
struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
diff --git a/drivers/net/wireless/ti/wlcore/scan.h b/drivers/net/wireless/ti/wlcore/scan.h
index a6ab24b5c0f9..4dadd0c62cde 100644
--- a/drivers/net/wireless/ti/wlcore/scan.h
+++ b/drivers/net/wireless/ti/wlcore/scan.h
@@ -37,7 +37,7 @@ void wl1271_scan_complete_work(struct work_struct *work);
int wl1271_scan_sched_scan_config(struct wl1271 *wl,
struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies);
+ struct ieee80211_scan_ies *ies);
int wl1271_scan_sched_scan_start(struct wl1271 *wl, struct wl12xx_vif *wlvif);
void wlcore_scan_sched_scan_results(struct wl1271 *wl);
diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h
index 95a54504f0cc..71320509b56d 100644
--- a/drivers/net/wireless/ti/wlcore/wlcore.h
+++ b/drivers/net/wireless/ti/wlcore/wlcore.h
@@ -95,7 +95,7 @@ struct wlcore_ops {
int (*scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int (*sched_scan_start)(struct wl1271 *wl, struct wl12xx_vif *wlvif,
struct cfg80211_sched_scan_request *req,
- struct ieee80211_sched_scan_ies *ies);
+ struct ieee80211_scan_ies *ies);
void (*sched_scan_stop)(struct wl1271 *wl, struct wl12xx_vif *wlvif);
int (*get_spare_blocks)(struct wl1271 *wl, bool is_gem);
int (*set_key)(struct wl1271 *wl, enum set_key_cmd cmd,